Perfection is not over-engineering
"We don't want to do perfect." "We don't want to build the perfect solution." I've heard versions of that line more times than I can count, delivered as if "perfect" were a dirty word. And I understand the caution — over-engineering burns teams, and people have learned to treat anything that smells like perfection as the same risk.
It isn't. The industry has quietly conflated the two.
Over-engineering is solving the wrong problem. That's the whole definition. Not "caring too much." Not "making it too good." Solving the wrong problem. Often with good intentions, and almost always with a growing pile of incidental complexity.
There is a perfect solution
I believe a perfect solution exists. With one big caveat: you need a very clear set of requirements. Every constraint on the table. Tighten those enough and something interesting happens, you end up with only one possible solution. And that solution is, somewhat ironically, the perfect one. It's perfect because it's the only one that fits.
Start a new project. Every language, every tool, every hosting model available. You pick serverless. Python is a strong choice: no compilation step, upload your files to Lambda, ship. For someone else it's the wrong choice, they don't know Python, or they need to optimize for a different set of requirements, such as performance. Different constraints, different answer. Same problem space, different "perfect."
Or you chose Python and you're building a web app. Django or Flask? You can reach similar results with both. They're still different tools with completely different philosophies. Which one wins? It depends. Set clearer requirements, set stricter constraints, and the solution follows. That solution is the perfect one for you, for that case.
Systems are products
When a system is over-engineered, the cause is almost always requirements. And I mean requirements in the product sense, not just the technical one.
A library, an API, an internal tool... we like to pretend these are "purely technical," that they somehow sit outside the idea of a product. They don't. You have users. Those users have needs. You need to understand those needs well enough to address them properly.
Maybe what they need is a service. Or maybe it's better served by a library than by an HTTP call. Instead of handing them an API, maybe you hand them a package. The shape of the solution only becomes obvious once you treat the system as a product and define the requirements honestly. Then the solution follows.
How you can tell
The clearest tell that something is over-engineered: you start asking why are things built the way they are? and the answers don't hold.
Classic example. A team of three people maintains five microservices. The services share data among each other. Is it over-engineered? Figure out which problem they were trying to solve. Most likely you'll conclude they were solving the wrong ones (or several of them at once).
Look at what the split actually costs. What used to be a hard reference in a database, a foreign key the engine enforces for you, is now a loose string id sitting in a field. Data integrity is gone. One service can delete a record and the other has no idea; it just keeps a dangling reference and finds out later, the hard way. Why all this ceremony between services when it's all part of the same domain? Why give up those integrity checks?
What did you gain in exchange? Usually: not as much as you lost. Independent deploys, sure, but was that a problem you actually had? Three people, one domain. You solved for a scaling and ownership problem that wasn't on the table, and paid for it with distributed inconsistency, operational overhead, and a system that solves multiple problems partially, none of them completely, while introducing a bunch of problems you wouldn't have had otherwise.
That's the signature. Not elegance. Not thoroughness. And it's not that these solutions are bad, usually they're the correct answer to the problems that were proposed. The problem is that those were problems you never had.
Gather the right requirements
So the diagnosis is simple, even if the work isn't. Over-engineering is a failure of requirements gathering. Call it product engineering if you want. It's the consequence of collecting the wrong requirements, and then engineering diligently against them.
Perfection was never the enemy. Ambiguous requirements were. Get those right, get every constraint on the table, and the perfect solution stops being a fantasy. It becomes the only thing left standing.
I made a video version of this argument, if you'd rather watch it: Perfection is not over-engineering.
Thanks for reading.