The Code Outlives the Reason
Every line of code is the residue of a decision, and the two have wildly different lifespans. The line persists exactly, byte for byte, indefinitely. The reasoning that produced it lives in someone’s head for a few months, gets fuzzy in a year, and is gone entirely once that person moves teams or simply forgets. So a codebase steadily accumulates a gap: a growing body of instructions whose what is perfectly preserved and whose why has quietly evaporated. Everything still runs. Nobody knows why it’s shaped like that.
The symptom is the line nobody will touch. There’s an odd retry with a strange delay, a condition that special-cases one customer, a sort applied twice. It looks wrong or at least pointless, and the honest reaction is to clean it up — except there’s a nagging sense that it might be load-bearing, that someone put it there for a reason, and that the reason might be a production incident you’ve never heard of. So it stays, and it collects a small tax on everyone who reads that function forever after. The code isn’t complicated because the problem is complicated; it’s complicated because nobody can safely simplify it.
This is the strongest argument for writing down the why rather than the what, and it’s a distinction people habitually get backwards. A comment explaining what the code does is redundant — the code already says that, more precisely and without going stale. A comment explaining why is irreplaceable, because that information exists nowhere else in the system and is the first thing to disappear. “Retry three times with backoff” is noise. “The upstream service returns 500s for a few seconds after a deploy; three retries covers it” is the difference between a future engineer confidently deleting a workaround and one leaving it alone forever out of superstition.
Commit messages and design docs matter for the same reason, and they decay in the same way if they only describe the change. A commit that says “add null check” tells you nothing you couldn’t see in the diff. A commit that says “add null check — the import path can produce records without an owner, see the incident on the 4th” is a message from the past to whoever eventually asks whether that check is still needed. The value isn’t in the record of what happened; it’s in preserving the constraint that made it necessary, because constraints are what you need to know before you’re allowed to remove something.
There’s a real economy to this — writing down every reason for everything is its own kind of waste, and most decisions genuinely are obvious enough to re-derive. The ones worth capturing are the surprising ones: where the code does something a reasonable person would find odd, where you chose the non-obvious option, where you worked around someone else’s behavior. Those are exactly the places where future-you will otherwise stare, guess, and leave it alone. A codebase where the surprises carry their reasons stays malleable, because people can tell the difference between essential complexity and vestigial complexity. One where they don’t slowly fossilizes — every strange line becoming permanent, not because it’s right, but because nobody can prove it’s safe to remove.