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 ChangeabilityReplace “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 SubstitutionThe only SOLID principle with a formal definition — and the one Kotlin’s variance annotations already encode.
Interface SegregationA 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
NamesThe one part of the code the compiler never checks, and the cheapest thing to get right.
Deep ModulesFunctionality divided by interface is the measure. A shallow module adds a layer without removing anything.
CommentsClean 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 MeansFeathers’ definition has nothing to do with age: legacy code is code without tests – which makes it a solvable problem.
SeamsA place where you can change behavior without editing in that place. Everything that follows is built on this one idea.
Characterization TestsTests 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 DependenciesFeathers’ catalogue, reordered by how much of the frightening code each technique makes you touch – and the ones Kotlin has made obsolete.
When final Blocks YouA 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 UseThe 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 RewritingThe whole discipline reduces to one rule: never change behavior and structure in the same commit.outline
M6Applying ItSession 12-13
From Audit to a Shipped ChangeRunning the method end to end on one real class — and stopping at exactly one change.outline

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.