# 👉 https://stevenpauls.de/articles/clean-code ## My Bias > Last Edit: 26/07/2024 Like Steven, and anyone taking programming seriously, I have formed a bias and built opinions on how good code should be written. My history with Clean Code is mostly from being mentored by a senior in my first proper development job. The area was scoped to to frontend development of a large TypeScript/Vue.js codebase where he had the tech lead and built the major part. Said senior insisted on keeping the code clean. The result of it was an actually very maintainable piece of software. There were a few minor issues, but nothing that was truly problematic to deal with in practice. But to maintain fairness, I will be mentioning them, as soon as the appropriate section comes up. The company that I worked for after that didn’t use any Clean Code principles. And the codebase was absolutely horrible. There were massive amounts of code duplication, hardwired dependencies all over the place, classes that were thousands of lines in size and were used as god-objects. The only patterns that were consistently used were anti-patterns. I could write a book about the terrible sights I saw as I spent a year at that job. Today, I am more convinced than ever, that Clean Code is *generally* a good idea. There are many great principles, that make a lot of sense. There are also some heuristics that I disagree with. But disagreeing with 10% of the book doesn’t make the other 90% less true and useful. ## Chapter 1 > Last Edit: 26/07/2024 I don’t think that there is a lot to discuss here. I would, however, like to point out a very important rule of Clean Code mentioned in this chapter: the Boy Scout Rule, one of the most fundamental rules of Clean Code. I believe that adhering to this rule will greatly and positively impact any codebase when respected by all developers working on it. > “Leave the campground cleaner than you found it” > \[p. 14] This is also the only way to sustainably improve a codebase over time. ## Chapter 2 > Last Edit: 26/07/2024 Let’s discuss the refactoring of the `printGuessStatistics` method. I too have a lot to criticize, but my issues are *somewhat* different in nature. ### The Disagreements I think it is okay to refactor this part of logic into an own object. It separates the concerns of creating a message from printing it and exposes the logic in a way that makes it unit-testable. It also opens the options to treat the message-creation-logic as a [strategy](https://refactoring.guru/design-patterns/strategy). I also want to heavily criticize the `thereAre` / `thereIs` methods. They don’t tell the reader what they actually *do*, and leave the false impression that they contain logical expressions and return a `boolean`, directly violating *Use Intention-Revealing Names* (p. 18). I would provide documentation about the usage using unit tests instead of a comment block. ### The Agreements I agree with Steven that turning the method into a pure function would be even better, but since Java doesn’t allow for writing free-standing functions (and writing static methods instead is bad idea for different reasons) the only way we are left to solve this problem with is wrapping the function in a class. ### Minor Details The last thing about Stevens refactoring is that it changes the method signature. The difference is minor, but the price for it is that the caller has to now cast it’s `char candidate` to a `String` whenever calling the method. I don’t have a strong opinion about this change, the additional boilerplate might or might not justify the more generic interface of the method – no way to tell without more context. I also want to keep the names related to the original use case as long as the method is tightly coupled to it. With all points out of the way, let me propose the following solution with respect to the caveats of the Java programming language and respecting the original Signature. ### In the end: mostly the same ```java class GuessStatisticMessageFormatter { public String format(char candidate, int count) { String verb, number, pluralSuffix; if (count == 0) { verb = "are"; number = "no"; pluralSuffix = "s"; } else if (count <= 1) { verb = "is"; number = "one"; pluralSuffix = ""; } else { verb = "are"; number = Integer.toString(count); pluralSuffix = "s"; } return String.format("There %s %s %s%s", verb, number, candidate, pluralSuffix); } } ``` Usage / Unit Test: ```java class GuessStatisticMessageFormatterTest { private final GuessStatisticMessageFormatter formatter = new GuessStatisticMessageFormatter(); @Test void formattingWithZeroCandidates() { assertEquals("There are no As", formatter.format('A', 0)); } @Test void testFormattingWithOneCandidate() { assertEquals("There is one B", formatter.format('B', 1)); } @Test void testFormattingWithManyCandidates() { assertEquals("There are 5 Cs", formatter.format('C', 5)); } } ``` This solution mainly overlaps with Steven’s, except for the following adjustments: - class around the method (as there are no free-standing methods in Java) - unit tests provide usage example instead of a code comment block - keeps the same signature - keeps the name related to the “statistic-guessing” use case # Chapter 3 ## Programming Language or Natural Language? We start off almost philosophical. The first antithesis Steven discusses is the generally good principle that code should read like prose, while Uncle Bob implements this principle in an unfavorable manner. > “I don’t think (anymore) that programs should read like text. English is a way better tool to communicate to other _humans_ than Java” I have to admit: I don’t think I fully understand the meaning behind these two sentences. If English is the best way to communicate to other humans, why shouldn’t we strive to bring our code as close to it as possible? > Text is linear but programs are really not. Call trees will always be opaque. Your one sentence function hides ten of your other nicely named functions, which might hide even more code. Yes. That is the unfortunate reality we, as programmers, have to deal with. Code is complex by nature if the problem domain is complex. And you can’t understand everything. To follow the path of the program, starting from the main-function through every possible path of the call stack can quickly become impossible. This is why we separate our code into manageable pieces. Those units are functions or classes. And this is the exact reason why they shouldn’t be too long. From my perspective, there will always be a gap between the programming language (code) and natural language (prose) that you can’t get rid of, no matter how hard you try. But we should also use the language features to make the code as human-readable as possible. ## Short-Term Memory > ![[ape_memory_test.gif]] > I’m not that ape. Being able to use the very limited short-term memory (which – according to popular science at least – can hold around 7 pieces of information at the same time) requires to structure your code in a way that doesn’t require it to keep track of more things at the same time. There is an entire Book from the Robert C. Martin series dedicated to that exact premise: Code That Fits in Your Head - Heuristics for Software Engineering by Mark Seeman (978-0137464401). This is the exact reason why Clean Code is about keeping functions small. If the function doesn’t even fit into the short-term memory of your brain, you will have trouble reading through the implementation. > All of these sub-sections in this chapter pretty much only serve to indicate when to split code off into a separate function. None of these concepts are bad places to split up code, the opposite in fact, but they are not _reasons_ to split up code. While I also don’t agree with every single argument Uncle Bob makes, I do agree that some of them are good reasons to split up code. But not all of them. The book also doesn’t demand method extraction for all the mentioned heuristics. Switch Statements, Function Arguments (except Flags), CQRS and Exceptions over Error Codes are all about good function design and don’t serve to indicate that a function needs to be split into multiple functions. There are also a few heuristics We already agreed that we are not that ape. A 50-lince function with try-catch, if-else branches and loops that does a couple of things is quickly out of short-term memory bounds. I would argue that this is the only reason you need to split up the function, and Clean Code merely tells you all the places where you *can* split up the function. If the function is already simple to begin with, then you don’t need to split it up, even if it is 20 or 50 lines of code. > Now. The boring answer to functions lengths is actually: > *A function should be as long as it needs to be and this will depend on many factors.* > – guy on the internet Come on man, we both know that this answer is unsatisfactory :D would you recommend a beginner or your colleagues the book if this was the only thing Uncle Bob had to say about function length? To close this section, I remain at the standpoint that a function should be small because a large function is complex and too much for the human brain to grasp at once. ## ORDER BY priority DESC I personally don’t see that this is the ordering of the books chapters. The book is structured in a way that starts with the most inner concepts of a program (variables) and moves outwards from there (functions, classes, systems). ## The final bits > I don’t think we need to discuss “Prefer Exceptions to Returning Error Codes.” Oh, I think this would be an interesting discussion. Especially the one about determining when to use a `Result`-monad and when to use an exception. But no matter which path you choose, I think we can agree an that we should not use error codes anymore. The hidden control flow of exceptions is a major issue of many programming languages. Uncle Bob should have added the constraint to this heuristic to only use it in programming language that enforce explicit exception control flow (such as Java with the `throws` keyword). I that case I don’t even object to always use exceptions. The issue with side-effects is tricky and I agree that you should get rid of as much of it as possible. An important addition that I want to add is that this is not an OO-issue by nature. If I declare a function and initialize a few variables at the top, that will get read from and written to during the execution of the function, the situation is the same as if I were using a class and declaring those variables as instance variables. > Lastly, I don’t want to be disrespectful but the `SetupTeardownIncluder` refactoring is atrocious No, go ahead. Be disrespectful. The `SetupTeardownIncluder` refactoring was a mistake. If you can’t show me a good example that supports the claim you make, you might as well not make the claim. The reader gets left with an unnecessarily bitter aftertaste after this chapter.