Skip to content
back to the episode
Episode summary2023Fellow

A Philosophy of Software Design — Episode 3

The club covers five chapters of the software design book, discussing comments, documentation, names and code changes. Ilya and Alexander work through them with guest Anton, who works in management, analysis and architecture and often encounters other people's code without comments or meaningful names.

Code of Architecture · Book Club9 min read

This summary is shorter than the full episode; examples and assessments reflect the participants’ experience and views.

The main thread of the material
01

Four excuses for skipping comments

Anton explains why the book's thesis speaks to him: he mostly does management, analysis and architecture, yet he regularly reads other people's code, and not always in its original form. He enjoys taking apart the popular applications everyone uses daily, and there are no comments there, no sensibly named methods, variables or classes, so the whole context has to be reconstructed by hand. Reading code costs time and effort, sometimes a compile, when you only wanted to call a method.

The first excuse — good code documents itself — the participants trace to Robert Martin's books. Alexander recalls Steve McConnell's Code Complete, one of the first big books that shaped him, and a month-old interview with McConnell in another book club, conducted by an engineering director from Google: thirty years after the first edition the interviewer quotes that line back at him, and McConnell can only answer "oh my god".

What he meant was different: there is usually no documentation and no comments, you end up reading the code anyway, so let it at least be readable. Alexander notes how a line torn out of context turns into a meme that people then use to fend off comments. The author's own answer is blunt: if you have to read the code to understand the behaviour, the abstraction has already leaked.

Ilya adds that signatures and types cover only part of it and informal agreements cannot be expressed in code; Anton offers a method that proxies errors from calls one level down — to compile the full list you must read a lot of other code, whereas in Java or Go the generated library documentation is enough.

The no-time excuse the author measures out: typing code is about ten per cent of the work, and comments are an investment in a considered design rather than a debt.

The excuse about comments going stale is answered by the level of abstraction — high-level comments change rarely; and good documentation, the participants note, cures precisely the symptoms of complexity that code generation cannot touch: cognitive load and unknown unknowns.

The excuse that every comment one has seen is useless is cured by writing useful ones yourself, after which they appear in neighbouring systems across the company.

02

Four levels and the comment test

A throwaway script in tactical-tornado style need not be commented, but the participants recall code built on the write-it-and-forget-it principle: two or three years later the trade-offs and constraints baked into it are almost impossible to recover.

Chapter thirteen demands that a comment add value: repeating what is obvious from the code is pointless. One of the hosts confesses he sinned early in his career, writing comments formally because it was expected and treating their emptiness of meaning as a second-order question. The participants recall the sandwich principle and arrive at a layered cake: a lid on top explaining why any of this was written, the code below it, and low-level notes inside on how it works.

Anton runs through the four levels of comments: interface comments about behaviour and structure rather than a list of methods; comments on struct fields explaining why the field exists and what values it takes; implementation comments that add context, such as the requirements of a standard, instead of retelling the code; and the mysterious unicorn, cross-module comments, which the author loves but has rarely met.

The author suggests keeping cross-module comments in a separate design-notes file: it sits further from the code, but gives one source of truth with references instead of the same edit in a dozen places.

Then comes the algorithm: adopt conventions — languages have Javadoc, godoc and Doxygen, with documentation generated from them; do not repeat the code; and give the code to a new reader, asking whether they can reconstruct the comment from it. If they can, the comment probably only repeats the code.

A separate slide lists the plan for documenting an interface: the behaviour of the method, its parameters and return value, the exceptions invisible in the signature, and the preconditions for calling it.

03

Language models, names, a six-month bug

Could an outsider reconstruct the same comment from the code? Anton finds the test ageing: in his account, a language model can supplement the context using similar fragments from other projects. One of the hosts counters that a human would have to hold the entire codebase in their head to do the same, and the participants joke that good comments are worth writing if only to make generation easier for the models.

The chapter on naming opens with a bug the author hunted for six months. He was building a distributed file system with his students — the speaker hedges that this is how he remembers it — a rare bug drifted through it, the students gave up, and John found the cause not by reading code but by narrowing the search zone step by step: a variable called block sometimes meant a logical and sometimes a physical block, and in one place rubbish was written to disk. Nobody saw it while reading, because the context always makes clear which block is expected.

In the author's view, different names for different entities would have prevented this mistake. Hence the recommendations: a name creates an image in the reader's mind, not the writer's, it has to be precise and consistent — source and destination drawn from one vocabulary — and the further a declaration sits from its use, the longer the name should be. What stayed with the participants from the book are the nested structures socket and sock: which of them does what, the speaker no longer remembers.

The write-the-comments-first chapter wins them over because the interesting conceptual part of the work goes into describing abstractions rather than into routine at the end; it also validates the design: if you cannot write the comment, you do not understand it. Alexander recalls the advice to write pseudocode first and ties the chapter to strategic programming, adding that he and Anton now write production code close to never.

The last chapter covers changing code: do not duplicate, do not push into commit messages what belongs next to the code, and re-read your diff before review to check whether the documentation needs updating.

The tally: five chapters out of twenty-two, almost a quarter of the book; ahead, by the host's reckoning, lie six more chapters and a closing episode with green and red flags, and comments are likened to breadcrumbs leading back to the meaning of the code.

Takeaways

What to take away

  1. 01The good-code-documents-itself excuse fails a simple test: if you have to read the code to understand the behaviour, the abstraction has leaked; McConnell, by Alexander's account, meant nothing of the sort.
  2. 02Comments fall into four levels — interface, struct fields, implementation and the rare cross-module notes; the author suggests keeping the last of these in a separate design-notes file as one source of truth.
  3. 03The test that a comment reconstructible from the code is unnecessary is, in Anton's view, ageing: a model rebuilds context from similar code elsewhere, where a human would need the whole codebase in mind.
  4. 04Six months hunting a bug caused by a variable called block that meant sometimes a logical, sometimes a physical block is the argument for precise names: different entities deserve different names.

Sources