Bitsmi Blog
14.- Successive refinement
Chapter 14 is a detailed case study in successive refinement. It traces the full lifecycle of an Args command-line argument parser: a clean initial version, the messy intermediate state that resulted from adding features without refactoring, and the disciplined step-by-step restoration of cleanliness through TDD-guided incremental changes.
13.- Concurrency
Writing clean concurrent programs is hard. Code that looks correct on the surface is often broken at a deeper level and only fails when the system is under stress. Chapter 13 explains why concurrency is difficult, presents defence principles, catalogues the classic execution models, and gives practical guidance for testing threaded code.
12.- Emergence
Getting Clean via Emergent Design
What if by following these rules you gained insights into the structure and design of your code, making it easier to apply principles such as SRP and DIP?
According to Kent Beck, a design is “simple” if it follows these four rules:
- Runs all the tests
- Contains no duplication
- Expresses the intent of the programmer
- Minimises the number of classes and methods
The rules are given in order of importance.
Simple Design Rule 1: Runs All the Tests
A system that is comprehensively tested and passes all of its tests all of the time is a testable system. Tight coupling makes it difficult to write tests.
Simple Design Rules 2–4: Refactoring
Having a comprehensive test suite eliminates the fear that cleaning up the code will break it. The final three rules of simple design are: eliminate duplication, ensure expressiveness, and minimise the number of classes and methods.
No Duplication
Duplication is the primary enemy of a well-designed system.
The TEMPLATE METHOD pattern is a common technique for removing higher-level duplication.
Consider a system with two classes — VacationPolicy for US employees and VacationPolicy for EU employees — both implementing an accrueVacation() method that shares identical steps but differs in one detail: legal minimums vary by jurisdiction. The duplicated structure can be eliminated by defining the common algorithm in a base class and delegating only the variable step to subclasses:
abstract public class VacationPolicy {
public void accrueVacation() {
calculateBaseVacationHours();
alterForLegalMinimums(); // subclass provides jurisdiction-specific logic
applyToPayroll();
}
private void calculateBaseVacationHours() { ... }
abstract protected void alterForLegalMinimums();
private void applyToPayroll() { ... }
}
public class USVacationPolicy extends VacationPolicy {
@Override protected void alterForLegalMinimums() { /* US rules */ }
}
public class EUVacationPolicy extends VacationPolicy {
@Override protected void alterForLegalMinimums() { /* EU rules */ }
}
The skeleton of the algorithm lives in VacationPolicy; the subclasses fill in only the parts that differ. No duplication of the common steps remains.
Expressive
You can express yourself by choosing good names, by keeping your functions and classes small, and by using standard nomenclature. By using standard pattern names — such as COMMAND or VISITOR — in the names of the classes that implement those patterns, you can succinctly describe your design to other developers. Well-written unit tests are also expressive.
Minimal Classes and Methods
The goal is to keep the overall system small while also keeping individual functions and classes small. Remember, however, that this rule is the lowest priority of the four rules of Simple Design.
Conclusion
Following the practice of simple design encourages and enables developers to adhere to good principles and patterns that might otherwise take years to learn.
11.- Systems
Chapter 11 applies the lessons of clean code to the system level. The same separation of concerns, single responsibility, and avoidance of tight coupling that guide functions and classes are equally important when designing the architecture of an entire system.
10.- Classes
Chapter 10 moves the focus from functions to classes. Clean classes are small, cohesive, and embody a single responsibility. The same discipline applied to naming and sizing functions applies here at a higher level of abstraction.
9.- Unit Testing
Overview
Chapter 9 argues that unit tests are not a second-class citizen of the codebase. Tests must be written with the same care, design, and discipline as production code. Dirty tests are as damaging as no tests: they accumulate technical debt, resist change, and ultimately get discarded – taking with them all the safety they once provided.
8.- Boundaries
Overview
Chapter 8, written by James Grenning, addresses the challenge of integrating third-party or externally-controlled code into your system. The boundary between your code and foreign code requires special care: too much direct coupling to a third-party API spreads fragility throughout the codebase. The chapter presents techniques for keeping those boundaries clean and well-contained.
7.- Handling errors
Overview
Chapter 7, written by Michael Feathers, addresses error handling as a first-class concern of clean code. Many codebases are dominated by error-handling logic scattered throughout the business code, making the real algorithm nearly invisible. The goal is to separate error handling from the main logic so each can be understood independently.
3.- Functions
Overview
Functions are the first line of organisation in any program. This chapter describes what makes a function clean: how small it should be, what it should do, how many arguments it should take, and how it should handle errors.
4.- Comments
Overview
Comments are not inherently good. At best they are a necessary evil — a compensation for our failure to express ourselves clearly in code. The proper response to bad code is to clean it, not to comment it.
“Don’t comment bad code — rewrite it.” — Kernighan and Plaugher