Taking notes while learning a new codebase

By Lior Rabanian · · 6 min read
  • Code
  • Notes
  • Workflow

You are four levels into a call stack trying to work out where the value comes from. You find it, you fix the thing, you move on.

Three weeks later you are in the same place, tracing the same path, having the same small realisation. You have done this before. You remember doing it. You do not remember the answer.

This is the defining experience of learning a large codebase, and it is not a memory problem. It is that the understanding you built was never written down, and understanding that is not written down decays at roughly the rate of a phone number.

What is worth writing, and what is not

The instinct is to document the code. Resist it — the code documents itself better than you can, it changes underneath your notes, and a note describing what a function does is stale within a month and misleading thereafter.

What decays badly and is not in the repository is different.

Call paths. "A request to /export goes through the controller, the serialiser, and the job queue, and the timeout is set in the middleware, not the job." Four lines, and it saves you the twenty minutes it took to establish. This is the single highest-value kind of note there is.

Why, when why is not obvious. Every codebase has a section that looks wrong and is not. Somebody explained it to you, or you worked it out from the history. That explanation exists nowhere in the code, and it is what will stop you or someone else from "fixing" it later.

Where things live. Not the directory structure — the answers to "where does X happen". Where is auth actually enforced. Where do the emails come from. Where is the thing that runs at midnight. A dozen of these is an enormous head start.

The gap between how it looks and how it behaves. The naming that lies, the config that is overridden somewhere else, the abstraction that has one real implementation. Every codebase has a handful and they are the source of most wasted afternoons.

The environment incantations. The commands with the flags. How to run one test. How to reset the local database when it wedges. These are trivial, they are looked up constantly, and they belong somewhere you can find them in two seconds.

A code note with the command, the call path and the explanation in the same document as the prose
Not documentation. A record of things that took twenty minutes to establish and will take twenty minutes again.

The rule that makes it work

Write it down the moment you have understood it, before you fix the thing.

The window is narrow. While you are tracing, you are holding the whole path in your head and writing it costs almost nothing. Once you have made the fix, the understanding has done its job and starts unloading immediately — and half an hour later, writing the same note means reconstructing it.

The second half of the rule: stop before you write documentation. Four lines about a call path is a note. Two pages about the architecture is a document, and a document is a commitment to maintain something that will drift. The value is in the small, specific, expensive-to-rederive facts.

Where these notes should live

Split by lifespan, and this is the practical decision.

In the repo: anything true about the code that the team should share and that should change when the code changes. Setup instructions, architecture decision records, the reasons behind a design. If it lives next to the code, it moves with it and gets reviewed with it.

In your own notes: your working understanding. Half-formed, occasionally wrong, containing "I think this is because" and "ask someone about this". That is exactly the material that should not go into shared documentation — nobody wants a wiki full of somebody's provisional guesses — and it is also the material that makes you effective in month two.

The mistake is trying to put the second category in the first. It makes you slow, because you edit yourself, and it degrades the shared docs with uncertainty.

The one-page map

After a fortnight, write one page and keep it updated.

Not the architecture — the territory. Six or eight lines: what the main pieces are called, roughly what each does, and where the seams are. Where does a request enter. Where does data get written. What is the one part everybody is afraid of.

Every codebase has an implicit version of this that the long-serving people hold in their heads, and none of them have ever written it down because it is too obvious to them to be worth stating. It is the single most useful document for the next person, and you can only write it during the window where you have just learned it and have not yet forgotten what it was like not to know.

Code, prose and the same window

A practical note, because this is the friction that stops people.

These notes are inherently mixed: a paragraph of explanation, a command with flags, a stack trace, a path, a snippet. If your notes app mangles code — smart quotes, autocorrect, no monospace, no syntax highlighting — you will not paste the command, and the command was the useful part. If your code lives in a scratch file and the explanation lives in a notes app, you will keep one and lose the other.

That is the whole argument for keeping notes and code in one app: not elegance, but that a mixed note is the natural shape of this material and splitting it in half loses whichever half is more inconvenient.

The other mechanic that matters is search. You will not remember which note has the thing; you will remember a word from it — a function name, an error string, a flag. That means search has to reach inside code blocks, which is not universal and is worth checking before you rely on it.

A month in

Reread what you wrote in week one. Two things happen.

Some of it is wrong. Fix it — the wrongness is informative, because it shows you what the codebase misleads people about, which is exactly what the one-page map should warn about.

Some of it is now obvious and you cannot remember needing it. Leave it alone. It is not for you any more; it is for the next person, and the fact that it looks trivial to you is precisely why nobody else was ever going to write it.

The honest version

Nobody is going to keep this up for years, and they should not. The intensity of note-taking should fall off as the codebase becomes familiar, and by month six you will be writing almost nothing, which is correct.

The window that matters is the first six to eight weeks — the period where everything is expensive to establish and cheap to record. After that the value drops sharply, and continuing to document out of discipline is just a slower way to work.

Cyanote handles the mixed shape: prose notes with syntax-highlighted code blocks inline, and separate code notes with language detection that can be saved straight back to a file on disk. ⇧⌘F searches the body of everything including code blocks, [[ links a note to the one it relates to, and notes nest into sub-pages so a codebase can have a page with children rather than fourteen loose notes. One local database, so the map you built of a system you were paid to understand stays yours.