CP//DEV

6/15/2026 · 4 min read

Ockham's razor and code: against premature abstraction

Don't multiply entities without need. Ockham, YAGNI, and why the abstraction you don't need yet is debt dressed as elegance.

In the 14th century, William of Ockham stated a principle that outlived seven centuries: do not multiply entities without need. It was a rule of metaphysics, not of software. But it describes, with uncomfortable precision, the most expensive mistake developers make: abstracting before it’s needed.

The principle, as it stands

Ockham’s razor doesn’t say “simple is pretty”. It says something stronger: between two explanations that account for the same facts, the one introducing fewer entities is preferable. Not for aesthetic taste, but because each extra entity is one more claim that can be wrong.

In code, each layer of abstraction is an entity. Each interface, each factory, each layer of indirection is a claim about the future: “this will vary in these ways”. And like every claim about the future, it’s almost always false.

Abstraction is a bet, not a gift

There’s an idea that abstracting is always virtuous —that code with many layers is “more professional”. It’s the other way around. Abstracting is betting.

When you create a PaymentProvider interface with a single implementation, you’re betting there will someday be several. If you’re right, you save a refactor. If you’re wrong —and you’re wrong most of the time— you’ve paid a permanent tax: every developer reading that flow has to jump through an indirection that protects nothing.

The right abstraction pays for itself. The premature one is paid by whoever maintains it, every day, for a future that never came.

YAGNI is Ockham applied

The acronym YAGNI —“You Aren’t Gonna Need It”— is Ockham’s razor translated into the trade. Don’t build for the requirement you imagine; build for the one in front of you.

The typical objection: “but if I leave it ready, it’s easier later”. Almost never. The cost of adding an abstraction when it’s genuinely needed —when you already know the two or three real ways it varies— is lower than the cost of maintaining a speculative one that guessed the variation wrong. The known future is cheap to abstract. The imagined future is expensive to hold up.

How I tell a needed layer from a premature one

  • How many real implementations does it have today? An interface with a single implementation isn’t an abstraction: it’s an ornament with a reading cost.
  • Did the variation already happen, or am I imagining it? Abstracting over observed variation is design. Abstracting over imagined variation is guessing.
  • Can I introduce it later without pain? If extracting the abstraction the day it’s needed costs half an hour, don’t build it today. Wait for the day.
  • Does this layer protect from something, or just move the problem one house over? Indirection that isolates nothing only adds one more hop to the reading.

The rule of three

There’s an old heuristic that still pays: don’t abstract until the third repetition.

The first time, you write the code. The second time something similar shows up, you duplicate it —yes, on purpose— and tolerate the discomfort. Only on the third, when you can see the real shape of what repeats, do you extract the abstraction. By then you aren’t guessing the pattern: you’re reading it off three concrete cases.

Duplicating twice is cheaper than maintaining, for two years, an abstraction that guessed wrong. Premature duplication is deleted in five minutes; premature abstraction grows roots.

The cost you don’t see in the diff

What makes premature abstraction dangerous is that it doesn’t show up as a problem. A diff with more interfaces, more layers and more files looks like serious work. Nobody rejects your PR for being “too well structured”.

But the cost arrives later, scattered, impossible to attribute: the person who takes twice as long to follow a simple flow, the bug hiding three layers down, the trivial change that must be touched in five places because the abstraction split up a decision that should have lived together.

Why it matters to me

Philosophy taught me to distrust the entities I introduce to explain something. Each new concept has to earn its place: if I can account for the same facts without it, it’s surplus.

Code is the same. Elegance isn’t in the number of layers you hold up, but in the number you managed not to need. A system is mature not when there’s nothing left to add, but when there’s nothing left to remove without breaking it.

Ockham’s razor, seven centuries later, is still the best architecture advice I know: when you hesitate between two designs, keep the one that introduces fewer things that can be wrong.