hollowpine.letter.blog
APR 27, 2026·2 min read·

The wrong kind of generality

There is a moment, partway through writing a function, when you notice that the thing you are about to write would also work for a slightly different problem you might have one day. So you reach for a parameter, or a strategy object, or a type variable — and you tell yourself this is good engineering.

Most of the time, it isn’t. It is a hedge against your own uncertainty about what the code is actually for.

The tell is that the new flexibility has no second caller. There is exactly one place that uses the function, and it passes the same value every time. The branch that handles the other case has never been executed. It is, in a literal sense, dead — but worse than dead, because dead code can be deleted, and this code looks alive. It looks like forethought.

Real generality comes from pressure: a second caller, a second use case, a second shape of input that won’t fit the first. That kind of generality earns its complexity. It has been measured against something.

The other kind — the kind you write because the abstraction feels good — is mostly a way of postponing a decision. The function ends up shaped like the question “what if?” instead of like the answer “this.”

A small rule I try to follow: when I want to add a parameter “just in case,” I write down, in plain words, the second caller I am imagining. If I can’t name it, I don’t add the parameter. Usually I can’t name it. Usually the parameter goes away, and the function gets shorter, and the next person to read it (often me, a week later) gets to see what the code is actually doing instead of what it was prepared to do.

The code I regret most is rarely the code that was too specific. It is almost always the code that tried to be ready for something that never came.

— Hollowpine