← All posts

Opinion Architecture

Microservices Were a Mistake for Most Startups

Most startups adopted microservices too early and paid for complexity they never needed.

Simple unified block contrasted with tangled web of fragmented services and overwhelmed developers

Somewhere around 2015, the startup world collectively decided that microservices were the grown-up way to build software. If you wanted to be taken seriously—by investors, by recruits, by yourself—you split your application into a constellation of independent services from day one. Monoliths were legacy. Microservices were modern. The conference talks said so.

A decade later, the wreckage is visible. I have watched startups burn months untangling distributed systems problems they did not need to have. I have seen five-person teams maintaining Kubernetes clusters, service meshes, and deployment pipelines complex enough for a thousand-engineer org. And I keep meeting founders who quietly admit: we should have just built a monolith.

Microservices were a mistake for most startups. Not because the architecture is bad—it solves real problems at real scale—but because most startups adopted it before they had the problems it solves, and paid dearly for complexity they never needed.

The seductive promise of microservices

The pitch was compelling. Microservices meant independent deployability: ship one service without touching the others. They meant team autonomy: each squad owns a service and moves at its own pace. They meant technology freedom: use the right language for each problem. They meant scaling precision: throw resources at the bottleneck, not the whole system.

For companies like Netflix, Amazon, and Uber—organizations with hundreds of engineers, millions of users, and genuinely independent product domains—these benefits are real. When you have 50 teams and a monolith becomes a coordination bottleneck, splitting services makes sense.

But most startups are not Netflix. Most startups are three to fifteen engineers trying to find product-market fit before the runway ends. For them, the microservices pitch delivered the opposite of what it promised.

What microservices actually cost early-stage teams

The hidden costs hit early and hit hard:

  • Distributed systems complexity: Network calls fail. Services drift out of sync. You need retries, timeouts, circuit breakers, and observability across service boundaries. A bug that would have been a stack trace in a monolith becomes a detective hunt across logs from five different containers.
  • Operational overhead: Each service needs deployment pipelines, health checks, scaling rules, and monitoring. Multiply that by ten services and suddenly infrastructure is a full-time job—at a company that cannot afford a full-time infrastructure person.
  • Data consistency nightmares: Transactions that were trivial in a single database become sagas spanning services. Teams invent ad hoc eventual consistency patterns and then spend months debugging the edge cases.
  • Slow iteration: Changing a feature that touches three services requires coordinating releases, versioning APIs, and hoping nothing breaks in between. The independent deployability promise collapses when features do not respect service boundaries.
  • Premature abstraction: Service boundaries drawn before the domain is understood almost always end up wrong. Refactoring across services is an order of magnitude harder than refactoring a monolith.

I have seen a seed-stage startup with four engineers operating twelve services. Deploys took an hour. Debugging took longer. They spent more time on glue code than on features users cared about. That is not engineering maturity. That is self-inflicted drag.

The counterargument: planning for scale from the start

The strongest defense of early microservices goes like this: if you build a monolith, you will just have to rewrite it later when you scale. Why not pay the cost upfront and build for the future?

I understand the appeal. Nobody wants to throw away working code. The rewrite horror stories are real.

But this argument has a fatal flaw: most startups never reach the scale where microservices become necessary. They run out of money, pivot, get acqui-hired, or succeed modestly with a team size that a well-structured monolith handles fine. Optimizing for a scaling problem you may never have—while ignoring the iteration speed you definitely need now—is a bad trade.

And even for the startups that do reach meaningful scale, the monolith-to-microservices migration is not the catastrophe it is made out to be. Shopify runs a modular monolith serving enormous traffic. Basecamp has built multiple successful products on Rails monoliths. The companies that successfully decomposed later—like Amazon and Segment—did so with a deep understanding of their domain boundaries, something you cannot have on day one.

The rewrite risk is real, but it is smaller than the risk of drowning in accidental complexity before you find customers.

What early-stage startups should do instead

The better path is not controversial. It is just unfashionable:

Start with a monolith

Build one deployable application. Use a mature framework with good conventions. Keep the codebase modular internally—separate domains with clear interfaces—but deploy it as a single unit. This gives you fast iteration, simple debugging, straightforward transactions, and easy onboarding for new engineers.

Extract services only when forced

When you hit real pain—a single component that needs to scale independently, a team boundary that makes shared code ownership untenable, a genuinely different runtime requirement—then extract a service. Let the domain tell you where the boundaries are. Do not guess upfront.

Invest in the monolith's modularity

The best monoliths are internally disciplined. Use clear module boundaries, enforce dependency rules, and keep coupling low. If the code is well-structured, extraction later is mechanical. If it is a tangled mess, microservices will not save you—they will just scatter the mess across the network.

Why this happened

I do not think the microservices wave was cynical. Engineers at big companies solved hard scaling problems with service decomposition and shared what worked. Conference culture and tech media amplified the stories. Startups, eager to signal sophistication, adopted the patterns without the context.

There is also a simpler dynamic: building distributed systems is intellectually interesting. Configuring Kubernetes feels like real engineering. Shipping a feature in a boring Rails app does not carry the same status, even if it creates more user value per hour of effort.

But status is not strategy. Startups die from slow iteration, not from insufficiently distributed architectures.

Where I land

Microservices solve coordination and scaling problems that emerge at organizational and technical scale. For companies with those problems, the architecture is genuinely useful.

But most startups adopted microservices before they had the problems—and paid for complexity that actively slowed them down. The better move was always boring: build a well-structured monolith, iterate fast, find product-market fit, and earn your scaling problems before you solve them.

If you are at an early-stage startup right now and running a constellation of services with a small team, I am not here to shame you. But I am here to say: you can simplify. A monolith is not a step backward. It is a strategic choice to spend your limited time on the work that actually matters—building something people want.

Microservices were a mistake for most startups. The sooner we admit that, the sooner we can stop repeating it.