Code is read far more often than it is written, so every decision — a variable name, a function’s length, whether to comment at all — is really a decision about how much friction the next reader (often future-you) will face.
Meaningful Names
Names are the cheapest form of documentation available, and Martin treats them as a discipline in their own right:
- Spell things out in full — no abbreviations that save the writer time at the reader’s expense.
- Use verbs for functions, nouns for variables and classes.
- Names should be searchable; single-letter or generic names fail the moment you need to grep for them.
- Be consistent for similar concepts — pick one word per concept and stick with it across the codebase.
- Don’t use encodings (Hungarian notation, type prefixes) — the type system and IDE already carry that information.
Functions
This is where the book gets most prescriptive, and most useful in practice:
- Keep functions small — smaller than feels natural at first.
- Each function should do one thing, at one level of abstraction.
- Keep arguments to zero where possible, sometimes one or two, rarely three, never more.
- Don’t pass flags into functions — a boolean argument usually means the function is doing two things; split it into two functions instead.
- Don’t accept output arguments — a function should return its result, not mutate something passed in.
- Be predictable: avoid side effects that aren’t obvious from the function’s name.
- Dependencies and timing must be explicit — hidden temporal coupling is a common source of bugs.
- Prefer exceptions to returning error codes, since error codes force the caller to remember to check them.
Comments
Martin’s stance on comments is the most controversial part of the book, and worth stating precisely rather than as a blanket “no comments” rule:
- Comments are a last resort, not a first instinct — always try to explain yourself in code first.
- If a comment exists only because a variable or function name isn’t clear, fix the name instead. Prefer explaining variables and multiple well-named lines over one dense, clever one.
- Comments don’t run and aren’t tested, so they rot — the code changes and the comment doesn’t.
- Legitimate uses: explaining an architectural decision, justifying an algorithm choice, stating intent, clarifying an otherwise-confusing piece of code, or warning of consequences.
- Never comment out code — delete it. Version control remembers; a dead comment block is just noise.
- The rule of thumb: code explains what, comments explain why.
Structure
Martin borrows a newspaper metaphor for file layout — high-level concepts at the top, detail as you scroll down — and a set of vertical/horizontal ordering rules:
- Separate concepts vertically.
- Related code should read vertically dense — no unnecessary blank lines between tightly coupled statements.
- Declare variables close to where they’re used.
- Dependent functions should sit close together — ideally the called function directly beneath the caller.
- Similar functions should be grouped near each other.
- Order functions in the downward direction, from high-level to low-level detail.
- Keep lines short enough that horizontal scrolling is never necessary.
- Use whitespace deliberately — to associate related statements and separate weakly related ones.
Objects and Data Structures
Objects hide data and expose behavior; data structures expose data and carry no behavior. This makes them near-opposites in how they respond to change:
- Objects (e.g.
Square,Circleeach witharea()) — adding a new shape is one new class, nothing else changes. Adding a new operation (perimeter()) means touching every class. - Data structures (e.g. a
Shapestruct + free functionarea(shape)) — adding a new operation is one new function. Adding a new shape means touching every function.
Rule of thumb: classes make new types cheap to add; data structures make new operations cheap to add. Pick based on which one you expect to grow — plus:
- Reach for a class when there are invariants to protect (e.g. a balance that can’t go negative) — encapsulation as safety, not just organization.
- Reach for a data structure when you’re just moving data across a boundary (parsing, payloads, pipelines) — a class there adds ceremony without adding safety.
- Avoid the hybrid (public fields and behavior) — see point 3 below — it gets the costs of both with the benefits of neither.
The Rest of the Chapter’s Guidance
- Classes should be small.
- Each class should do one thing.
- Keep the number of instance variables small.
- A base class should know nothing about its derivatives — dependency flows one way.
- Prefer non-static methods to static ones — static methods can’t be polymorphic and are harder to test in isolation.
Unit Tests
Martin folds TDD directly into what “clean” means, via the Three Laws of TDD:
- Start with tests — test-first isn’t optional, it’s the workflow.
- One assert per test — keeps each test’s failure message meaningful.
- Law 1: don’t write production code until you have a failing test.
- Law 2: don’t write more of a unit test than is sufficient to fail.
- Law 3: don’t write more production code than is sufficient to pass the currently failing test.
The cumulative effect of following all three laws is a tight, seconds-long red-green-refactor loop — tests and production code evolve together in lockstep rather than tests being bolted on afterward.
See also: SOLID Principles