The subject is not clean code. It is changeable code – and the difference matters, because “clean” is an aesthetic judgement and changeability is something you can measure.
This series works through SOLID, legacy code, and refactoring, in Kotlin, on a real codebase. It draws on four sources and does not treat them as agreeing, because they do not:
| Source |
What it is good for |
Where to be careful |
| Feathers, Working Effectively with Legacy Code |
The strongest of the four. Technique-dense, not opinion-dense, and the problem it solves has not changed since 2004. |
Its techniques lean hard on subclass-and-override, which Kotlin’s final-by-default blocks. That translation gets its own module. |
| Ousterhout, A Philosophy of Software Design |
Turns “good design” into measurable claims: change amplification, cognitive load, unknown unknowns, deep modules. |
Short, opinionated, and light on worked examples. |
| Martin, Clean Code |
Naming, boundaries, error handling, and the discipline of noticing. |
Written for an earlier career stage, and the later chapters take function decomposition past the point where it helps. Read selectively. |
| Fowler, Refactoring (2nd ed.) |
The mechanical catalogue, and the commit discipline that makes it safe. |
It is a reference, not a read-through. |
The most instructive thing in this area is where two of them contradict each other. Clean Code says a comment is a failure of expression; A Philosophy of Software Design says a comment carries what code structurally cannot. They disagree about function length for the same reason. This series stages that argument rather than picking a winner, because knowing why each position is held is worth more than either conclusion.
Two of the five SOLID principles – OCP and DIP – were covered in depth in the design patterns series and are referenced rather than repeated here. The other three get a post each.
The map
M0Measuring the ThingSession 1
| Measuring Changeability | Replace “clean” with three things you can actually measure: change amplification, cognitive load, and unknown unknowns. | |
M1SOLID, Minus the Two You KnowSession 2-3
| Single Responsibility | “One reason to change” is the most misread sentence in software. The reason is a person, not a topic. | |
| Liskov Substitution | The only SOLID principle with a formal definition — and the one Kotlin’s variance annotations already encode. | |
| Interface Segregation | A client should not be forced to depend on methods it does not use. The cost shows up first in the tests. | |
M2Names and BoundariesSession 4-5
| Names | The one part of the code the compiler never checks, and the cheapest thing to get right. | |
| Deep Modules | Functionality divided by interface is the measure. A shallow module adds a layer without removing anything. | |
| Comments | Clean Code says a comment is a failure. A Philosophy of Software Design says a comment is design. They cannot both be right. | |
M3Legacy Code I: Getting a GripSession 6-7
| What Legacy Actually Means | Feathers’ definition has nothing to do with age: legacy code is code without tests – which makes it a solvable problem. | |
| Seams | A place where you can change behavior without editing in that place. Everything that follows is built on this one idea. | |
| Characterization Tests | Tests that record what the code does, not what it should do. The first ones you write must not assert correctness – that is the whole trick. | |
M4Legacy Code II: Breaking Dependencies in KotlinSession 8-9
| Breaking Dependencies | Feathers’ catalogue, reordered by how much of the frightening code each technique makes you touch – and the ones Kotlin has made obsolete. | |
| When final Blocks You | A third of Feathers’ catalogue assumes subclass-and-override. Kotlin closed that door on purpose – and the question is whether the replacement is as good. | |
M5RefactoringSession 10-11
| The Twenty You Actually Use | The online catalogue lists 72 refactorings. Three months of labelled commits in one codebase used six of them – and the most common one was deleting code. | |
| Refactoring Is Not Rewriting | The whole discipline reduces to one rule: never change behavior and structure in the same commit. | outline |
M6Applying ItSession 12-13
The rule this series is built on
From the Foundations of the previous series, and it applies just as hard here:
Does the code get shorter, or at least easier to follow, afterward? If a change leaves three more files and a flow that is harder to trace, it failed – and “it’s the correct design” is not a justification.
The failure mode of a refactoring course is a codebase churned for six months with nothing shipped. Every module ends with a stopping condition for that reason.