The Cost of Surprise
Reading code is an act of prediction. You see a function named getUser and you assume it fetches a user and returns it — you don’t read its body, you predict its behavior from its name and the conventions around it, and you move on. This is the only way anyone reads a codebase of any size; nobody executes every line in their head. So the quiet cost of code that surprises isn’t that it’s wrong. It’s that it breaks the prediction, and once a reader learns that predictions here can’t be trusted, they have to stop predicting and start verifying — opening every function, checking every assumption, reading what they used to be able to skim.
A surprise is any place where the code does something a reasonable reader wouldn’t expect from the outside. getUser that also writes to the database. A function that usually returns a list but sometimes returns null instead of an empty one. A parameter named timeout measured in seconds everywhere except this one call site where it’s milliseconds. A module that follows the codebase’s pattern in nine places and quietly does its own thing in the tenth. Each is individually survivable, and each teaches the reader a small lesson: you can’t trust the surface here, you have to check. Enough of those lessons and the whole codebase becomes something you have to read slowly and suspiciously, which is the most expensive way for code to be.
This is why consistency is worth more than it looks, and why it sometimes beats local cleverness. If there are five ways to do a thing and the codebase already uses one of them, using that one — even if it’s not your favorite — is usually the right call, because a reader who’s seen it four times already predicts the fifth correctly and moves on. A better-but-different approach in one spot makes that spot a surprise: now the reader has to notice it’s different, work out why, and wonder whether the difference is meaningful or accidental. The consistency is doing work that the marginal improvement doesn’t pay for. A codebase where everything is done the same way is one where knowing one part transfers to all the others.
The connective tissue with names and comments is that all three serve the same master: letting a reader understand code without running it. A good name lets you predict a function’s behavior; a why-comment explains the one place the behavior has to be surprising; consistency means the predictions hold across the whole codebase so the reader can rely on them. They’re three faces of a single property — that the code communicates its behavior through its surface, so people can trust the surface. Every surprise, every lie in a name, every stale comment chips at that trust, and trust is the thing that lets a large codebase be worked on at all.
Which gives a concrete test for a lot of decisions that otherwise feel like taste. Before doing something the unusual way, ask what a reader who knows this codebase would expect here — and if you’re about to violate that expectation, you’d better be getting something worth the tax, and you’d better leave a why-comment saying what. Sometimes the surprise is justified: a real constraint forces it, and the note explains it. But most surprises aren’t justified; they’re just someone doing it their own way in a place where the established way would have read as obvious. The goal running through all of this is code that a competent reader can trust at a glance — and the discipline is spending your surprises deliberately, only where they buy something, because every one you spend, someone else pays for every time they read past it.