I want to treat this knowledge base as a [[Home#About this Website|Digital Garden]]. Therefore I decided to discuss concepts that I find interesting and that I want to discuss more deeply in individual notes. Use this document or the sidebar to find those # Upfront Notes This part of the garden is inspired by Steven’s reading journal of the some book. It can be found here → https://stevenpauls.de/articles/cleancode I discuss this specific journal as well → [[A Discussion about Steven's Discussion about Uncle Bob's Clean Code]] → [[On Measuring the Quality of Code]] → [[My Alignment towards the Heuristics of Clean Code Explained]] # Chapter 1 - Clean Code I like the introductory example of how bad code can destroy entire products and companies. Unfortunately, all of those examples are based on personal experience and not properly backed by sources. I think it would have helped the argument to cite actual, real world examples of companies going down or overspending millions because of bad code. ## Clean Code and Martial Arts Uncle Bob goes on and drops a great statement. First, he takes his and his peers’ opinions, puts them under the umbrella term *Clean Code* and describes this construct of opinions as “school of thought”. Now we know that *Clean Code* is not only made from Uncle Bobs’ opinions, but also those of (at least) Bjarne Stroustrup, Grady Booch, Dave Thomas, Michael Feathers, Ron Jeffries and Ward Cunningham. But the bomb I am referring to is Bob’s comparison that *Clean Code* is to programming what a specific marital arts style is to the world of martial arts. As a practitioner of Kyokushin Karate and a former practitioner of Shotokan Karate and Jiu Jitsu myself I find this comparison fascinating, because it sets a very clear perspective onto the term of “Clean Code”, what it claims to be, and what it does not. Unfortunately, I can’t really communicate this intuition to someone who hasn’t practiced at least 2 fundamentally different styles of martial arts. ## The Boy Scout Rule The first chapter finishes off with the first *Clean Code* heuristic you can use in your everyday work with code. I really like this rule, but at the same time, I think there are very few programmers who are disciplined enough to follow this rule at least most of the time. ## Alignment | Heuristic | Rating | References | | ----------------------------------------------------------------------------------- | ------ | ---------------------------------------------------- | | The Boy Scout Rule - Check in your code cleaner than it was when you checked it out | 5 | [[Clean Code - Robert C. Martin#The Boy Scout Rule]] | # Chapter 2 - Meaningful Names ## Abbreviations I still find myself cringing when I see abbreviated names that distract you from the flow of reading just for the sake of saving 2 or 3 characters. That’s why I completely agree with the *Clean Code* notion of not using them. ## I-Interfaces and Implementation-Impls Uncle Bob prefers leaving any pre- or postfix out of the interfaces name, and changing the class name accordingly, if necessary. I want to ask the question which one of the following is better: `class Cat : ICat` or `class CatImpl : Cat`? I have had this discussion with another Senior at Optimate. It turned out that he had a clear opinion: neither. During this discussion, I too came to the same conclusion. An interface describes a *thing*, while the class contains a concrete implementation of that *thing*. Therefore, the class name should reference the way the interface was implemented in its name, and the interface should simply be the name of the thing. This also improves the understanding why this abstraction was necessary in the first place. A very generic example that might just exists like that in a very generic web application: ```csharp interface UserRepository { ...} class InMemoryUserRepository : UserRepository { ... } class SQLiteUserRepository : UserRepository { ... } ``` I think if we do it this way, we get the most information out of the classes' and interfaces’ names. ## Class Names Uncle Bob recommends avoiding names like `Manager`, `Processor`, `Data` or `Info`. I would have liked a slight elaboration on that, and how to determine when a word is “like” one of those 4 words. Maybe it is intuitive for native English speakers, but I can’t really figure out the “set” that those word belongs to. ## Alignment | Heuristic | Rating | References | | ----------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Use Intention-Revealing Names | 5 | | | Avoid Disinformation | 5 | | | Make Meaningful Distinctions | 4 | | | Use Pronounceable Names | 4 | | | Avoid Encodings | 5 | | | Avoid Mental Mappings | 5 | | | Class Names | 3 | [[Clean Code - Robert C. Martin#I-Interfaces and Implementation-Impls]] | | Method Names | 4 | | | Don’t be Cute | 5 | | | Pick one word per Concept | 4 | | | Don’t Pun | 5 | | | Use Solution Domain Names | 2 | I think this heuristic ignores modern software development methodologies, such as DDD, which strongly suggests to build the solution domain using problem domain terms ([[Ubiquitous Language]]). You need to understand the problem to be able to come up with a solution anyway. | | Add Meaningful Context | 3 | I agree with the heuristic itself, but not with the shown implementation ([[A Discussion about Steven's Discussion about Uncle Bob's Clean Code#Chapter 2]]) | | Don’t add Gratuitous Context | 5 | | # Chapter 3 - Functions ## Small! This paragraph is probably one of the most controversially discussed. In a nutshell, Uncle Bob recommends to write functions that are 2-4 (yes, two to four) lines long. Yes, I disagree with the 2-4 lines of code. However, I do agree with keeping functions small. My current heuristic is not bound to a certain amount of lines, but I consider every function that I have to scroll through because it doesn’t fit on my screen a definite code smell. I prioritize staying at the same level of abstraction (according to my definition) per function and minimizing nesting. This usually results in a reasonable function size. ## Levels of Abstraction Uncle Bob eventually refactors the large `renderPageWithSetupsAndTeardown` function into a class with 16 methods (excluding one override and the constructor). The main issue here is that those abstractions are not helpful. If I only call the method, I only care about the most-outer abstraction (which might as well be the atrocious `render`-method from the original version of the class). But if we have to change or extend this class, we have to read and understand it. Having to keep 16 levels of abstractions in your head is not natural, and it doesn’t make sense. Every new function signature introduced this way also adds to the complexity. My goal in situations like these is to find the equilibrium between function length and function count. The sweet spot, where there are no too many functions but the functions are also not too long. I personally find this to happen with functions that are between 5 and 10, sometimes 20 lines of code. *Which is still “small”, right?*. But as I mentioned before, I can live with longer functions. But once they are longer than that, I consider them a smell. That way I can minimize what needs to be kept inside my head when working with the code. ## Notes → [[Refactoring the SetupTeardownIncluder]] ## Alignment | Heuristic | Rating | Explanation | | ------------------------------------------ | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Small! | 4 | I agree with keeping functions small. Just not as small as Uncle Bob suggests. See [[Clean Code - Robert C. Martin#Small!]] | | Do One Thing | 3 | A function should only do one thing, but sometimes doing two or three things avoids the complexity that would be introduced by introducing 2-3 sub-functions, given that the function is small and simple to begin with. | | One Level of Abstraction per Function | 4 | I generally agree that mixing levels of abstraction is generally not good, and I try to avoid it myself. But just not as extreme as Uncle Bob does it. See [[Levels of Abstraction]] | | Switch Statements | 3 | To move the switch statement to a factory doesn’t remove the switch statement from the code. But I have definitely witnessed code where a replacement of switch with polymorphism would have made the code at that place much simpler and more easy to comprehend. On the other hand, game developers don’t like this refactoring because it makes their code slow. | | Use Descriptive Names | 5 | | | Function Arguments | 3 | While I agree that long parameter lists can make the code much more complex than it has to be, it sometimes can also make an interface more simple to use. E.g the `read_csv` function of the Python pandas library is a really powerful function with over 40 parameters, which is really pleasant to use due to keyword arguments and sensible defaults. On the other hands, business logic is best written using functions with as few parameters as possible. So it depends on the context. | | Have no Side Effects & Output Arguments | 5 | I cringe every time when people and even official libraries use `out var` in C#. It is a function. A function has a return value. Use it! | | Command Query Separation | 5 | | | Prefer Exceptions to Returning Error Codes | 3 | While I agree that exceptions are better than error codes, a Result Object is often better than an exception | | Don’t Repeat Yourself | 5 | Code duplication is bad, and I don’t understand why this heuristic is also discussed that controversially in certain circles | | Structured Programming | 2 | using returns or breaks can make the code much simpler (less nested, simpler loop condition) and therefore be worth it | # Chapter 4 - Comments This chapter is a bit different than the previous chapters. Instead of presenting a handfull of heuristics, you get a list of situations where comments are “bad” and one where they are “good”. Critics of *Clean Code* often say that comment are discouraged entirely, even though there are 8 types of comments which are classified as “good” comments. Even TODOs! ## Notes → [[The Clean Code Prime Generator]] ## Alignment ### Good Comments | Heuristic | Rating | Explanation | | ----------------------- | ------ | ----------- | | Legal Comments | 4 | | | Informative Comments | 4 | | | Explanation of Intent | 5 | | | Clarification | 5 | | | Warning of Consequences | 5 | | | TODO Comments | 4 | | | Amplification | 4 | | ### Bad Comments | Heuristic | Rating | Explanation | | ------------------------------------------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Mumbling | 5 | | | Redundant Comments | 5 | | | Misleading Comments | 5 | | | Mandated Comments | 5 | | | Journal Comments | 5 | | | Noise Comments | 5 | | | Scary Noise | 5 | | | Don't Use a Comment When You Can Use a Function or a Variable | 2 | The only reason why I disagree with this heuristic are those rare edge cases, where it is *somehow* possible to use variables and functions, but it just looks really forced and makes readability worse | | Position Markers | 5 | | | Closing Brace Comments | 5 | | | Attributions and Bylines | 5 | | | Commented out Code | 5 | | | HTML Comments | 5 | | | Nonlocal Information | 5 | | | Too Much Information | 5 | | | Inobvious Connection | 5 | | | Function Headers | 5 | | # Chapter 5 - Formatting This article is not very exciting for an experienced developer and doesn’t include any new or interesting takes. When you work alone, format in a way you are able to work with it. When you work in a team, use the team’s formatting rules. If there aren’t any, define them, make them part of your deployment pipeline and never think about formatting again. ## Alignment | Heuristic | Rating | Explanation | | ----------------------------------------------------------------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Files should be 200 lines long on average, 500 lines at most | 4 | No hard opinion about the average Size, but files *always* become difficult to read at some point. Defining 500 as the soft upper limit is a fair choice | | Different Concepts should be vertically separated | 5 | | | Related Concepts should be vertically together | 5 | | | Variables should be declared as close to their usage as possible | 5 | | | Instance Variable should be declared at the top of the class | 5 | | | Dependent functions should be vertically close | 5 | | | Conceptually affine (close to each other) functions should have little vertical distance between them | 5 | | | Function calls should go from top to bottom | 5 | | | Line width should be as small as possible, but never more than 120 characters | 5 | I would give this a 6 if I could. I work in a split editor more than 90% of the time, and it is incredibly annoying to have to scroll both vertically and horizontally | | Variable should be horizontally aligned, not vertically | 5 | | | Avoid breaking indentation, even for short statements | 5 | | | Team Rules > Personal Rules | 5 | | # Chapter 6 - Objects and Data Structures ## The Law of Demeter and Train Wrecks I usually don’t spend a lot of time actively thinking about this heuristic while programming (the truth is: much less than “not a lot”). If you model your code correctly, you probably won’t end in situation where you have to pull out a deeply nested method anyway. The train wreck on the other hand immediately reminded me of the syntax of a programming context of C#: *LINQ methods*. They also exist in Java, where they are called *Streams*. Of course, concatenating functions which take and output the same type of data structure don’t violate the Law of Demeter, because everything stays at the same level of abstraction. So, don’t confuse them with train wrecks. ## Conclusion What I like about this chapter is that Uncle Bob puts object oriented and procedural code on the same level and makes it the responsibility of the programmer to chose the right paradigm for their problem. There are many problems where polymorphism is not applicable. No need to artificially impose random OOP-principles. I also like that he introduces two important types of objects that often appear in software development: DTOs and Active Records. ## Notes → [[The Importance of Polymorphism in Object Oriented Modeling]] ## Alignment | Heuristic | Rating | Explanation | | -------------------- | ------ | ------------------------------------------------------------------------- | | Avoid “Train Wrecks” | 3 | See [[Clean Code - Robert C. Martin#The Law of Demeter and Train Wrecks]] | | Avoid “Hybrids” | 4 | | # Chapter 7 - Error Handling ## Alignment | Heuristic | Rating | Explanation | | ------------------------------------------------------ | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Write Your `try-catch-finally` statement first | 2 | I generally don’t like and also don’t apply heuristics that tell me in which order I should write code to achieve the final result, unless of course I agree with said order | | Use Unchecked Exceptions | 2 | I think it is always better if a function communicates explicitly that it raises an exception. Even if I have to add an `throws Exception` to 3 methods, I at least don’t accidently miss one | | Provide Context with Exceptions | 4 | I agree to add enough context to an exception as necessary. I disagree with the statement that a stack trace can’t tell you the intent of the operation that failed, at least for internal code. Because from the stack trace, you can go to the line where the exception was raised and figure out the reason from there. Giving even more information is nice-to-have, but no necessity. When writing a library, I agree with this heuristic. | | Define Exceptions Classes in Terms of a Caller’s Needs | 3 | I’m not sure whether we should always wrap APIs and their exception and translate them to our own exception classes. But I definitely understand the author’s points and advantages of wrapping such APIs. I guess it depends on the complexity of the project. | | Define the Normal Flow | 4 | Basically “prefer a sensible default return value over raising an exception and let the client deal with it” | | Don’t return null | 4 | I generally agree that using null is a bad idea. But sometimes it makes sense to return “nothing” from an operation that can semantically return nothing. Null, in many languages, is the easiest way to do that. An option-type is better though | | Don’t pass null | 5 | | # Chapter 8 - Boundaries | Heuristic | Rating | Explanation | | ------------------------------------------------------------------ | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Wrap boundary interfaces | 3 | I don’t know if the `Map` example is the best one, because it’s one of those fundamental data structures that I expect to be able to use in my code like `int` or `string`. This heuristic might make more sense for more complex types or functions | | Use learning tests to understand 3rd party APIs | 5 | | | Use an adapter if the interface you want to use is not defined yet | 5 | | # Chapter 9 - Unit Tests ## TDD Test-Driven-Development. This is where it gets interesting for me. This is one of those heuristics that don’t tell me how the code should be, but rather tell me the exact order of steps that I need to take to achieve this, basically micro-managing me away from the way my brain is thinking about building the code. I have tried it several times. I value unit tests. But I can’t get TDD to work. It is so fundamentally different from the way I produce code, my mind makes a complete halt and I am not able to write anything. I like to write some actual code first, experiment a bit with the API if it is not clear. I don’t have a problem with starting to write test early. Maybe after 10 lines of production code have been written, maybe after 50 lines. But never before any production code has been written. It does not make sense to me. The laws of TDD are ridiculously strict and severely inhibit my creativity and chain of thought when writing code. Therefore I have decided to not use TDD when doing any kind of software development. ## Alignment | Heuristic | Rating | Explanation | | ------------------------------------------------------------------------------ | ------ | ------------------------------------- | | Use TDD | 1 | [[Clean Code - Robert C. Martin#TDD]] | | Keep Tests Clean | 5 | | | Use Testing DSLs | 4 | | | Not using the “One Assert per Test” rule | 4 | | | Single Concept per Test | 5 | | | Tests should be Fast, Independent, Repeatable, Self-Validating, Timely (FIRST) | 4 | |