Should you read it too?
┌─────────────────┐ yes ┌───────────────────────────────────┐
│Have you read it?│────────────►│Did you read Clean-Code afterwards?│
└─────┬───────────┘ └──┬──────────────────┬─────────────┘
│ │ │
│ │ │
│ │ │
│no │yes │ no
│ │ │
│ │ │
│ │ │
▼ │ ▼
┌───────┐ │ ┌──────────────────┐
│Read it│◄───────────────────────┘ │Just read it again│
└───────┘ └──────────────────┘
Chapter 1: Introduction
“Writing computer software is one of the purest creative activities in the history of the human race.”
The only limitation of writing software is our understanding of the system we want to create. Therefore we should make systems as simple as possible, or at least just simple enough to understand. Increasing simplicity means decreasing complexity. The two ways of dealing with complexitiy is to eliminate it in the first place or to encapsulate it - modular design. By composing a system of multiple smaller and independent modules one never has to gasp the complexity of the whole system.
Chaper 2: The Nature of Complexity
Anything that makes a system structurally harder to understand or modify can be considered as complexity. A complex system is not inheritly a big system and vice versa. A small system can be complex and a big system can be simple.
Complexity is caused by dependencies and obscurity.
A dependency exists everytime when the reader cant understand Code-A without knowing Code-B.
Dependencies are not always explicit (like a foreign function call) but can also be implicit (like a communication protocoll that was agreed on).
Obscurity occurs when information is not obvious.
This can be caused by bad documentation but more prominant by bad design.
Having just a temperature
variable forces the reader to search the codebase for usage examples to determine a unit.
Common reasons
In “Tidy First?” Kent Beck writes about coupling and cohesion. Both can serve us as measurement for complexity. Coupling measures how strong and how many relationships exist in a codebase. Cohesion measures if code that works together is also written together, also called Locality of Behavior (LoB).
“The primary feature for easy maintenance is locality: Locality is that characteristic of source code that enables a programmer to understand that source by looking at only a small portion of it.” - Richard Gabriel
Coupling is the equivalent to dependencies and obscurity to cohesion and locality.
Resources
- Tidy First? by Kent Beck https://www.oreilly.com/library/view/tidy-first/9781098151232/
- Patterns of Software by Richard Gabriel https://www.dreamsongs.com/Files/PatternsOfSoftware.pdf
- HTMX LoB https://htmx.org/essays/locality-of-behaviour/
Chapter 3: Working Code isn’t enough
Most programming is approched with a tactical mindset. That means getting a certain feature or fix done as quickly as possible without reviewing the design. Every tactically added feature may add a kludge. Over time these will add up and create complexity called technical debt.
The opposite is programming with a strategic mindset. That means implementing a feature while thinking about the design. Most code written is an extension of some existing code, thus the best design facilitates those future extensions. “Your primary goal must be to produce a great design, which also happens to work”.
Of course implementing a feature and thinking about the design takes more time then only doing the first - that is until you hit a certain unknown threshold. Increasing technical debt also means increasing working times for adding new features. Tactical programming is the equivalent of borrowing money. Strategic programming is like investing money.
Good design is not a free lunch.
Chapter 4: Modules should be deep
TODO: rewrite
- we must reduce the amount of things a developer needs to know at one time
- deep vs shallow modules
- Classitis
- “interfaces should be designed to make the common case as simple as possible”
Chapter 5: Information Hiding (and leakage)
TODO: rewrite
- “information hiding”
- “information leakage”
- rahter one function that performs 3 math functions then 3 functions that each perform 1 math function
- “temporal decomposition”
- “[modules] should do the right thing”
- “The best features are the ones you get without even knowing they exist”
- “overexposure”
- “try not to be influnced by the order in which operations will occur at runtime…”
Chapter 6: General-Purpose Modules are deeper
TODO: rewrite
void Backspace(Cursor cursor) void Delete(Cursor cursor)
vs
void Insert(Position pos, String text) void Delete(Position start, end)
Generality lead to better information hiding: Better seperation of front and backend - backend doesnt need to know how keys are handled Frontend Develop only has to learn some general purpose functions
6.5 Questions for yourself What is the simplest interface that will cover all my needs? In how many situations will this method be used? Is this API easy to use for my current needs?
6.6 Push specializations upwards or/and downwards example udwards: apps example downwards: device driver
6.8 Eliminate Special cases in code “is_selection” instead of an empty selection
Chapter 7: Different Layer, different Abstraction
software is composed of layers where each layer provides a different abstraction for the layer above
TODO: rewrite
7.1 Passthrough methods 7.2 when is interface duplication ok? With interfaces 7.3 decoraters and wrapper classes 7.4 interface vs implementation the interface of a class should normally be different from its implementation 7.5 Passthrough variables Proposed solution: global state or global context object My proposal: passthrough context because global state sucks 7.6 conclusion cite first two sentences
Chapter 8
TODO
Chapter 9: Better together or better apart?
- Bring together if information is shared
- Bring togehter if it will simplify the interface
- Bring together to eliminate duplication
- A method should do one thing completely - length is not a good indicator for breaking up functions
Warning: Conjoined methods. It should be possible to understand each method independently.
A different opinion: Clean Code
TODO: rewrite
“Depth is more important then length”
Conclusion
The decision to split or join modules should be based on complexity. Pick …best information hiding, the fewest dependencies, and the deepes interfaces.
Chapter 10: Define Errors Out Of Existence
TODO
Chapter 11: Design It Twice
TODO
Chapter 12: Why Write Comments? The Four Excuses
TODO
Chapter 13: Comments Should Describe Things That Aren’t Obvious from the Code
Your programming language has a standard way of documenting things? Use it. Godoc for Go, javadoc for Java and Doxygen for C++.
Don’t repeat the code with comments.
func increment(x int) int {...} // increments x by one
…you don’t say?
Lower-level comments add precision.
Unit of the variable? Are boundaries inclusive or exclusive? Is the caller or callee responsible for freeing resources?
Higher-level comments enhance intuition.
What is this code trying to do? What is most important about the code? Think Black-Box.
Interfaces/abstractions need to be descibed with comments. “If interface comments must also describe the implementation, then the class or method is shallow” Interface and implementation comments should be sepperated.
Implementation comments appear inside the code and describe what the code is doing. Short functions generally just need a interface documentation. Longer functions in contrast often consist of multiple blocks that all work towards the overall task. Add a comment before each block. This helps readers find the code that matters to them. If the code is “tricky” implementation comment also should describe “why” the code is doing something.
Variables with a short scope can often be understood by their name. Variables with a longer lifetime may need a coment on what they represent.
Cross-module design decisions (for example network protocols) need to be documented because they are not obvious. The difficulty is where to store them so that developers naturally discover them? Storing them in one of the modules means its missing in the other. Duplicating the doc in both makes maintanence troublesome and error prone. The best solution is a central/global directory in the project containing such docs. For example a “doc” directory. The modules then can link or redirect to the central documentation like “See XYZ in doc”.
Comments are there to make “non-obvious” code easier to understand. When writing comments always imagine not knowing the code yourself - like most readers.