Why simple architecture always wins
After a decade of building, rescuing, and replacing software systems, I've learned one truth that contradicts most of what the industry preaches: simple architecture always wins.
The fourteen-service Rails app
A few years ago I was brought in to look at a product that wasn't shipping. The team had taken a working monolith — a Rails app with a Postgres database, doing a few thousand requests per minute — and spent eighteen months breaking it into fourteen services. They had Kafka. They had Kubernetes. They had a service mesh. What they did not have was a working checkout flow.
Their architecture diagram was beautiful. Their on-call rotation was a horror story. The original product owner, who had specced the rebuild in the first place, had quit six months in. The agency that sold them the rebuild had moved on to the next client.
We deleted nine services in the first month. Revenue went up. Pages loaded faster. The team started shipping again. None of that was clever. It was just less.
The complexity tax
Complexity is not free, and it is not paid once. It is paid every day, by every person who has to reason about the system. It compounds.
Every additional service is another deploy pipeline, another runbook, another set of dashboards, another place a 2 a.m. page can come from. Every distributed transaction is another opportunity for a half-written state. Every network hop is latency you cannot optimize away because the network is not your code.
The cost is rarely visible on the architecture diagram. It shows up in the velocity chart six months later, and in the engineer who quietly updates their LinkedIn at month nine.
Complexity is profitable for the agency. It is expensive for you, forever.
What "simple" actually means
Simple does not mean naive, and it does not mean unsophisticated. It means choosing the least complex solution that meets the requirement you actually have today, with a clear path to the requirement you might have tomorrow.
In practice that usually looks like:
- One deploy unit until proven otherwise. A modular monolith with clear internal boundaries beats a distributed system you don't have the org to operate.
- Boring infrastructure. Postgres. A queue table before a queue service. A cron before a workflow engine.
- Code a competent mid-level can read on their first day, without a tour.
- Defaults over configuration. Every config knob is a future bug report.
Where simple breaks
Simple is not always the right answer. There are real moments where the cost of staying simple is higher than the cost of splitting up. The honest signals are:
- Independent scaling profiles that genuinely diverge — for example, a tiny synchronous API in front of a heavy async ML pipeline.
- Independent failure domains where one part of the system has to keep serving when another is down (payments vs. recommendations).
- Independent teams large enough to own and operate a service end-to-end, including its on-call rotation.
If none of those are true today, you don't have a microservices problem. You have an I-want-microservices-on-my-CV problem, and it is very expensive to solve.
How to choose, in practice
Before adding any new piece of infrastructure — a queue, a cache, another service, an event bus — answer four questions out loud, in writing, in front of the people who will operate it:
- What concrete problem, observed in production, does this solve?
- What is the simplest thing that could solve it inside the existing system?
- Who is on-call for this new thing at 3 a.m., and have they agreed?
- How do we delete it if we're wrong?
If you cannot answer those, you are not making an architectural decision. You are decorating.
Want this kind of judgment on your project?
I read every email within one working day. Bring a project, a quote, or a system you're stuck on.
The 5 unbilled months hidden in every Contentful enterprise build
Every Contentful enterprise build I have audited carries the same five hidden months: link references, redirects, i18n, schema-driven forms, and type safety. None of them are on the SOW. All of them are on the timeline.
The microservices trap
How the industry convinced everyone they needed distributed systems, and why most companies would be better off with a modular monolith.
The real cost of custom development
Custom software isn't just the initial price. It's maintenance, updates, and the opportunity cost of not using existing solutions.