“We’ll clean it up later” is the most expensive sentence in software, and it’s expensive in a way that never shows up on the ticket that caused it.
I’ve stopped arguing for clean code on aesthetic grounds. Nobody wins that argument, and honestly nobody should: a beautiful codebase that ships nothing is worth less than an ugly one that pays salaries. The argument that works is narrower and harder to dismiss: readable code is faster to repair under pressure, and every system eventually needs repairing under pressure.
The only benchmark that matters
Here’s the test I apply now. Imagine it’s 2 a.m., the service is misbehaving, and the person looking at this function is not you. It’s whoever is on call, who has never seen this file, and who has about four minutes of clear thinking before the adrenaline makes things worse.
Can they tell what this code does? Can they tell what it’s supposed to do? Can they tell whether their fix will break something else?
That’s the whole standard. Not line count, not cyclomatic complexity, not whether you used the fashionable pattern. Just: can a tired stranger repair this safely.
Code is read under stress far more often than it’s written under stress. Optimise for the reading.
Small habits, disproportionate payoff
Name things after what they mean, not what they are. retryBudget tells the
next reader something count2 never will. Renaming is the cheapest refactor in
existence and the one most consistently skipped.
Make the shape of the data obvious. A function taking five positional booleans is a puzzle at the call site. A small struct with named fields is the same code with the answer written down.
// Before: what does `true, false, true` mean here?
Sync(ctx, src, dst, true, false, true)
// After: the call site explains itself.
Sync(ctx, src, dst, SyncOpts{
DeleteExtras: true,
DryRun: false,
Verbose: true,
})
Handle the error where you understand it. An error passed up six frames arrives somewhere with no idea what it meant. Either add context at the boundary where you still have it, or handle it there.
Write the comment that explains why. Comments describing what the code does go stale and lie. Comments describing why a decision was made (why this timeout is 3 seconds, why this branch exists, which incident it came from) are the only documentation that reliably survives.
Delete dead code immediately. It’s in version control. Leaving it commented out means every future reader has to work out whether it matters. It doesn’t, and now they’ve spent ten minutes proving it.
The part people get wrong
Clean code is not the same as clever abstraction. A great deal of unreadable code was written by people trying very hard to be clean: five layers of indirection to avoid one duplicated function, an interface with a single implementation, a factory producing exactly one thing.
Duplication is cheaper than the wrong abstraction. If two pieces of code look similar but change for different reasons, leave them alone. You can always merge them later; unpicking a bad abstraction after four features have grown around it is a genuine project.
Why it’s a feature
Every hour spent understanding code before changing it is an hour not spent delivering. On a codebase people can read, a bug fix is an afternoon. On one they can’t, the same fix is two days of archaeology plus a coin-flip on whether it broke something downstream. And that coin gets flipped during an incident.
That’s not craftsmanship. That’s throughput, incident duration, and how long your best engineers stay before the friction wears them out.
Which makes it a feature. It just happens to be one that only shows up in the bugs you never had to write a post-mortem for.