The microservices pitch is everywhere. Split your app into tiny, independent services. Deploy them separately. Scale them individually. It sounds like engineering maturity. It often isn't.
I've watched teams of eight developers run Kubernetes clusters managing fourteen services for an app with three hundred daily active users. The ops overhead was staggering. Two engineers spent most of their week just keeping the infrastructure coherent. Meanwhile, a competitor with a boring Rails monolith shipped features twice as fast.
This post is about why monolithic architecture still wins — not always, not forever, but far more often than the conference-talk consensus would have you believe.
The Microservices Hype Cycle Left a Lot of Wreckage
Around 2015–2018, microservices became the default answer to every architecture question. Netflix was doing it. Amazon was doing it. Surely you should too. What got lost in translation: Netflix and Amazon had thousands of engineers and genuinely needed that complexity to scale their organizations, not just their software.
The pattern that emerged at smaller shops was cargo-culting. Teams adopted the operational complexity without the organizational scale that makes it worthwhile. They got distributed system failure modes — network partitions, partial outages, cascading timeouts — without the engineering headcount to handle them.
Sam Newman, who literally wrote the book on microservices, has said repeatedly that a monolith is often the right starting point. That nuance rarely makes it into the blog posts.
What a Monolith Actually Gives You
Let's be concrete. A monolith is a single deployable unit. One process, one codebase, one database (usually). That sounds limiting. In practice it means:
Refactoring is cheap. You rename a function in your IDE and every caller updates instantly. In a microservices setup, that same change might require versioning an API contract, coordinating deployments across three teams, and running two versions in parallel for a deprecation window.
Local development is simple. git clone, bundle install, rails s. Done. With microservices, local dev often means Docker Compose files with eight containers, service stubs, and a shared secrets manager you have to fake. Onboarding a new engineer takes a day instead of a week.
Transactions are free. Database transactions spanning multiple operations are trivial in a monolith. In a distributed system, you're looking at sagas, two-phase commits, or eventual consistency — all of which add code, bugs, and mental overhead.
Observability is straightforward. One log stream. One trace. One place to look when something breaks at 2 a.m.
None of this is glamorous. It doesn't make for a good conference talk. It just works.
The "But It Won't Scale" Objection
This is the first thing someone says when you propose a monolith. It's mostly a phantom concern.
You can scale a monolith vertically — bigger machines are cheap now. A $400/month dedicated server on Hetzner or a c6i.4xlarge on AWS handles serious traffic. You can also scale a monolith horizontally: run multiple instances behind a load balancer. Stateless monoliths do this fine. Shopify ran a monolith at enormous scale for years. Stack Overflow still does. Their engineering blog posts about it are worth reading.
The real scaling constraint is usually the database, not the application tier. And that's a problem microservices don't magically solve — they just move it around and add network hops.
If you genuinely hit a bottleneck that a monolith can't handle, you'll know. You'll have data. You'll have a specific service with a specific load profile that justifies extraction. That's the right time to split — not at the architecture whiteboard before you've written a line of code.
When Microservices Actually Make Sense
I'm not saying microservices are always wrong. They make sense when:
- Teams are large and independent. Conway's Law is real. If you have four teams that need to deploy without coordinating, service boundaries can mirror team boundaries. This works at maybe 50+ engineers.
- You have wildly different scaling requirements. If your image processing pipeline needs GPU instances and your API tier needs memory-optimized boxes, splitting them makes operational sense.
- Compliance requires isolation. Sometimes a payment service genuinely needs to live in a separate environment with different access controls.
Note what's not on that list: "we might need to scale someday," "it feels more modern," or "the CTO read a blog post."
The Modular Monolith: A Practical Middle Ground
If the word "monolith" triggers your colleagues, call it a modular monolith. Same deployment unit, but with disciplined internal boundaries.
The idea: structure your codebase so that modules communicate through well-defined interfaces, even though they're in the same process. In Rails, this looks like engines or the packwerk tool Shopify open-sourced. In Java, it looks like modules with explicit exports. In Python, it's enforced package boundaries.
Here's a rough directory sketch:
app/
modules/
billing/
api.rb # public interface only
internals/ # private — other modules can't touch this
inventory/
api.rb
internals/
notifications/
api.rb
internals/
If you ever need to extract billing into its own service, the interface is already defined. The extraction is surgical rather than a rewrite. You get the organizational benefits of thinking in services without paying the operational tax upfront.
This is what I'd recommend to most teams under 30 engineers building a product that isn't yet at meaningful scale.
The Hidden Cost Nobody Talks About
Distributed systems are hard in ways that don't show up in architecture diagrams. They show up at 11 p.m. on a Friday.
Partial failures are the nasty ones. In a monolith, a bug usually crashes the whole process — loud, obvious, pagerworthy. In a microservices mesh, Service A calls Service B which calls Service C, and C starts returning 500s intermittently. A is still up. B is still up. Users see weird partial failures. Your tracing tool shows the problem only if you've instrumented everything correctly, which you probably haven't.
There's also the coordination overhead. Who owns the contract between Service A and Service B? What happens when Team A wants to change a field? You need API versioning, consumer-driven contract tests (Pact is the usual tool), and a deprecation process. For a team that was previously just merging PRs, this is a significant bureaucratic addition.
None of this is insurmountable. It's just cost. And most teams underestimate it by a factor of three.
Why Monolithic Architecture Still Wins for Most Shops
Let me put a stake in the ground. If your team is under 25 engineers, your product is under five years old, and you're not operating in a regulated industry with hard isolation requirements — a monolith is almost certainly the right call. Not because you lack ambition, but because you're optimizing for the right thing: shipping software that works, fast, without burning your engineers on infrastructure.
The teams I've seen succeed long-term are the ones that resisted the urge to over-architect early. They built something that worked, learned what actually needed to scale, and made targeted decisions based on evidence. The teams that adopted microservices from day one mostly spent their first two years fighting their own infrastructure.
Architecture should follow organizational and scaling reality, not precede it.
What to Do Tomorrow
If you're starting a new project: start with a monolith. Structure it modularly from the beginning so extraction is possible later if you need it. Use packwerk, Java modules, or just disciplined package boundaries.
If you're already in a microservices setup that feels painful: audit the actual cost. Count the hours per week your team spends on infrastructure, deployment coordination, and debugging distributed failures. Put a number on it. Then ask whether that cost is justified by what you're getting. Often it isn't, and merging services back together is a legitimate option — sometimes called a "monorepo migration" to make it sound less embarrassing.
The architecture that lets your team ship is the right architecture. For most teams, most of the time, that's still a monolith.