Clean Code is the last one that I finished reading of Uncle Bob’s clean series. Previously I wrote reading notes for Clean Coder, and I finished reading first pass but have not written reading notes for Clean Architecture yet. This time, instead of blending in some of my own comments or understandings of the book, I simply did highlights—the points in Clean Code are all cleanly made, and I don’t think I am able to summarize them better.
Two things to note before listing my excerpts. First, there are a lot more interesting points made in the book; I have only listed the top one or a few for the chapter. Those who are interested should definitely obtain a copy of the book and read cover to cover, including the code (before and after they are cleaned up). Second, Chapters 14, 15, 16 are very good examples of successively refining code to make it cleaner; if I later get a chance, I’d think of grabbing the code and setting them up a small projects in the IDE and trying out the refactoring myself.
Now here are my excerpts by chapters.
Chapter 1: Clean Code
“Most managers want good code, even when they are obsessing about the schedule. They may defend the schedule and requirements with passion; but that’s their job. It’s your job to defend the code with equal passion.”
Chapter 2: Meaningful Names
“The length of a name should correspond to the size of its scope.”
Chapter 3: Functions
“Flag arguments are ugly. Passing a boolean into a function is a truly terrible practice. It immediately complicates the signature of the method, loudly proclaiming that this function does more than one thing. It does one thing if the flag is true and another if the flag is false!”
“Functions should do one thing. Error handing is one thing. Thus, a function that handles errors should do nothing else. This implies that if the keyword try exists in a function, it should be the very first word in the function and that there should be nothing after the catch/finally blocks.”
Chapter 4: Comments
“One of the more common motivations for writing comments is bad code. We write a module and we know it is confusing and disorganized. We know it’s a mess. So we say to ourselves, ‘Ooh, I’d better comment that!’ No! You’d better clean it!”
Chapter 5: Formatting
“A team of developers should agree upon a single formatting style, and then every member of that team should use that style. We want the software to have a consistent style. We don’t want it to appear to have been written by a bunch of disagreeing individuals.”
Chapter 6: Objects and Data Structures
“Hiding implementation is not just a matter of putting a layer of functions between the variables. Hiding implementation is about abstractions! A class does not simply push its variables out through getters and setters. Rather it exposes abstract interfaces that allow its users to manipulate the essence of the data, without having to know its implementation.”
“Objects hide their data behind abstractions and expose functions that operate on that data. Data structures expose their data and have no meaningful functions. Go back and read that again.”
Chapter 7: Error Handling
“In fact, wrapping third-party APIs is a best practice. When you wrap a third-party API, you minimize your dependencies upon it: You can choose to move to a different library in the future without much penalty. Wrapping also makes it easier to mock out third-party calls when you are testing your own code.”
null from methods is bad, but passing
null into methods
is worse. Unless you are working with an API which expects you to pass
null, you should avoid passing
null in your code whenever
Chapter 8: Boundaries
“Interesting things happen at boundaries. Change is one of those things. Good software designs accommodate change without huge investments and rework. When we use code that is out of our control, special care must be taken to protect our investment and make sure future change is not too costly.”
Chapter 9: Unit Tests
“What makes a clean test? Three things. Readability, readability, and readability. Readability is perhaps even more important in unit tests than it is in production code. What makes tests readable? The same thing that makes all code readable: clarity, simplicity, and density of expression. In a test you want to say a lot with as few expressions as possible.”
- Fast: Tests should be fast.
- Independent: Tests should not depend on each other.
- Repeatable: Tests should be repeatable in any environment.
- Self-Validating: The tests should have a boolean output.
- Timely: The tests need to be written in a timely fashion.
Chapter 10: Classes
“We like to keep our variables and utility functions private, but we’re not fanatic about it. Sometimes we need to make a variable or utility function protected so that it can be accessed by a test. For us, tests rule. If a test in the same package needs to call a function or access a variable, we’ll make it protected or package scope.”
Chapter 11: Systems
“Inversion of Control moves secondary responsibilities from an object to other objects that are dedicated to the purpose, thereby supporting the Single Responsibility Principle. In the context of dependency management, an object should not take responsibility for instantiating dependencies itself. Instead, it should pass this responsibility to another ‘authoritative’ mechanism, thereby inverting the control. Because setup is a global concern, this authoritative mechanism will usually be either the ‘main’ routine or a special-purpose container.”
Chapter 12: Emergence
“High class and method counts are sometimes the result of pointless dogmatism. Consider, for example, a coding standard that insists on creating an interface for each and every class. Or consider developers who insist that fields and behavior must always be separated into data classes and behavior classes. Such dogma should be resisted and a more pragmatic approach adopted.”
Chapter 13: Concurrency
“Concurrency is a decoupling strategy. It helps us decouple what gets done from when it gets done.”
“ConcurrentHashMap implementation performs better than HashMap in nearly all situations. It also allows for simultaneous concurrent reads and writes, and it has methods supporting common composite operations that are otherwise not thread safe. If Java 5 is the deployment environment, start with ConcurrentHashMap.”
Chapter 14: Successive Refinement
“To write clean code, you must first write dirty code and then clean it. This should not be a surprise to you. We learned this truth in grade school when our teachers tried (usually in vain) to get us to write rough drafts of our compositions. The process, they told us, was that we should write a rough draft, then a second draft, then several subsequent drafts until we had our final version. Writing clean compositions, they tried to tell us, is a matter of successive refinement.”
Chapter 15: JUnit Internals
“JUnit has had many authors, but it began with Kent Beck and Eric Gamma together on a plane to Atlanta. Kent wanted to learn Java, and Eric wanted to learn about Kent’s Small-talk testing framework. ‘What could be more natural to a couple of geeks in cramped quarters than to pull out our laptops and start coding?’ After three hours of high-altitude work, they had written the basics of JUnit.”
Chapter 16: Refactoring SerialDate
INCLUDE_BOTH constants are used to describe whether the defining
end-point dates of a range should be included in that range.
Mathematically, this is described using the terms ‘open interval,’
‘half-open interval,’ and ‘closed interval.’ I think it is clearer
using the mathematical nomenclature, so I changed it to an enum named
Chapter 17: Smells and Heuristics
“In general an artificial coupling is a coupling between two modules that serves no direct purpose. It is a result of putting a variable, constant, or function in a temporarily convenient, though inappropriate, location. This is lazy and careless. Take the time to figure out where functions, constants, and variables ought to be declared. Don’t just toss them in the most convenient place at hand and then leave them there.”
“[On ‘Feature Envy’] The methods of a class should be interested in the variables and functions of the class they belong to, and not the variables and functions of other classes. When a method uses accessors and mutators of some other object to manipulate the data within that object, then it envies the scope of the class of that other object. It wishes that it were inside that other class so that it could have direct access to the variables it is manipulating.”
Appendix I: Concurrency II
“What happens if we change type of
lastId from int to long? Is line 5
lastId = 0;) still atomic? Not according to the JVM specification.
It could be atomic on a particular processor, but according to the JVM
specification, assignment to any 64-bit value requires two 32-bit
assignments. This means that between the first 32-bit assignment and
the second 32-bit assignment, some other thread could sneak in and
change one of the values.”