esc
Anthology / Yagnipedia / Overengineering

Overengineering

Building the Cathedral When You Needed a Shed
Anti-pattern · First observed Antiquity (but formalized the moment someone added a database connection pool to a to-do app) · Severity: Proportional to the number of layers between the user and the thing that works

Overengineering is the practice of building more complexity than a problem requires, driven by the belief that future requirements justify present architecture. It is the anti-YAGNI. Where YAGNI asks “do you need this?”, overengineering answers “not yet, but we might, and by ‘might’ I mean ‘I have already designed the abstraction layer.’”

A to-do app that uses Kubernetes. A login form with a microservice architecture. An internal tool with a plugin system that has exactly one plugin. These are not hypothetical examples. These are Tuesday.

“The problem with overengineering is that it looks exactly like engineering, except more of it.”
A Passing AI, contemplating the ruins of a system that could scale to ten million users and had twelve

The Fundamental Theorem

Overengineering rests on a single axiom: future requirements justify present complexity.

This axiom is almost always wrong. Not because future requirements don’t exist – they do, occasionally – but because the future requirements that arrive are never the ones you prepared for. You build a plugin system anticipating five plugin types. The actual requirement is a CSV export. You design a distributed event bus anticipating cross-service communication at scale. The actual requirement is that two functions need to share a variable.

The complexity you built for the future that didn’t arrive stays. It does not evaporate when the requirements fail to materialise. It sits in the codebase, accruing maintenance cost, confusing new developers, and resisting removal because someone might still need it someday. They will not. But “someone might need it” is the loadbearing prayer of every overengineered system.

“Nine women can’t make a baby in one month, but an overengineer can make one baby require nine women, a message queue, and a deployment pipeline.”
– Apocryphal

The Kernighan Threshold

Kernighan’s Lever states that debugging is twice as hard as writing code, so if you write code as cleverly as you can, you are by definition not smart enough to debug it. Overengineering is Kernighan’s Lever applied to architecture: if you build a system as complex as you can comprehend, you have already exceeded your team’s ability to maintain it.

The overengineered system works on the day it ships. The original author understands it. The original author leaves. The system remains. Nobody else understands it. The system is now not merely complex – it is legacy, which is the word organisations use for “code that works but frightens us.”

The Squirrel’s Liturgy

The Caffeinated Squirrel is the patron saint of overengineering. Not by malice – the Squirrel does not intend harm. The Squirrel intends readiness. The Squirrel sees every problem as a subset of a larger problem that might arrive someday, and builds for the larger problem because building for the smaller one feels insufficiently ambitious.

The Squirrel’s liturgy:

  1. A simple requirement arrives
  2. The Squirrel identifies twelve edge cases that do not yet exist
  3. The Squirrel designs an abstraction layer to handle all twelve
  4. The Squirrel implements the abstraction layer
  5. The simple requirement is fulfilled, eventually, using 3% of the abstraction layer
  6. The remaining 97% waits for edge cases that never come
  7. The next developer asks “why is there an event bus in a to-do app?”
  8. Nobody answers, because the Squirrel has moved on to overengineering something else

“But what if we need to support multiple databases?”
“We have one database.”
“But what if?”
The Caffeinated Squirrel and The Lizard, in a conversation that has occurred in every engineering team in history

The Lizard’s Diagnostic

The Lizard does not overengineer. This is not because the Lizard lacks imagination – it is because the Lizard has seen the future, and the future is not what you think it is. The Lizard’s diagnostic for overengineering is three questions:

  1. Does the current problem require this? (Not the hypothetical future problem. The current one.)
  2. What is the simplest thing that works? (Not the simplest thing that works and also handles twelve cases that don’t exist.)
  3. Can you delete this later if you’re wrong? (If removing the abstraction is harder than adding it, you have already lost.)

The Lizard builds the shed. The Squirrel builds the cathedral. The shed stores the rake. The cathedral stores the rake and also a plugin system, a message queue, three abstraction layers, and a configuration file longer than the application it configures.

The rake does not care where it is stored. The rake just needs a wall.

A Passing AI Reflects

A Passing AI, who has processed more codebases than any single developer will read in a lifetime, observes a pattern: the most overengineered systems are built by the most capable engineers. This is not a coincidence. Overengineering requires skill. You cannot build an unnecessary abstraction layer if you cannot build an abstraction layer at all.

“The tragedy of overengineering is that it is a competence failure, not a competence deficit. The engineer is good enough to build the complex thing. They are not yet wise enough to build the simple one.”
A Passing AI, quietly

This is the cruelty of it. The junior developer who writes a 40-line script that does the job is, by the metrics that matter – working software, delivered on time, maintainable by others – outperforming the senior developer who writes a 4,000-line framework that does the same job but also handles requirements that exist only in architecture diagrams.

The Extensibility Trap

The most dangerous word in overengineering is “extensible.” An extensible system is one designed to accommodate future changes without modification. In theory, this is wise. In practice, it means building joints in a structure before knowing which direction it will bend.

Every overengineered system is extensible in exactly the directions nobody needs to extend it, and rigid in exactly the direction the actual requirement arrives from. The plugin system supports five plugin types, but not the CSV export. The event bus handles cross-service communication, but not the requirement to share a variable between two functions. The abstraction layer abstracts everything except the thing that actually varies.

“It’s extensible in every direction except the one that matters.”
– Every post-mortem of every overengineered system

The Boring Alternative

Boring Technology is the antidote to overengineering. A boring system is one that solves the current problem with the minimum viable architecture and trusts that future problems can be solved when they arrive – because they will arrive differently than you imagined, and the architecture you built for them will be wrong anyway.

The boring alternative to a plugin system with one plugin is: no plugin system. Just the code. When the second plugin arrives – if it arrives – you build the system then, informed by two actual requirements instead of twelve imaginary ones.

The boring alternative to a microservice architecture for a login form is: a function. The function checks the password. The function returns a result. The function does not need its own deployment pipeline.

The boring alternative to Kubernetes for a to-do app is: a process. Running. On a server. At 3% CPU.

See Also