A monolith is the simplest possible architecture for a software application: all your code — API endpoints, business logic, data access, background jobs — lives in one codebase, gets built into one artifact, and runs as one process. When someone hits your API, the request flows through your routing layer, into your business logic, touches the database, and returns a response — all within a single application.
That’s it. There’s no magic. And despite what the conference talks and blog posts might have you believe, there’s nothing wrong with it.
Why Monoliths Get a Bad Reputation
Somewhere around 2014, the software industry decided that monoliths were the enemy. Netflix published papers about their microservices architecture. Spotify talked about squads and tribes and autonomous services. Suddenly every architecture diagram that showed a single box was “legacy.”
The problem with this narrative: Netflix has 2,000+ engineers. Spotify has over 1,000. They adopted microservices because they had organizational problems that only manifest at massive scale — hundreds of engineers stepping on each other in the same codebase, deployment bottlenecks where 50 teams share a release train, and scaling requirements where one component needs 100x the resources of another.
Your 8-person startup does not have these problems. And adopting the architecture designed to solve them creates a whole set of problems you definitely don’t need.
What a Monolith Gives You
Speed of development. In a monolith, adding a feature means writing code in one place, running one test suite, and deploying one thing. No service-to-service contracts. No API versioning between your own components. No distributed transaction patterns. You just write the code and ship it.
Simple debugging. When something breaks, you get a stack trace that tells you exactly what happened. You don’t need distributed tracing to follow a request across 12 services. You open the logs, read the error, fix the bug. This simplicity is worth more than most teams realize until they’ve lost it.
Easy testing. Your test suite runs against one application. Integration tests hit a real database and test real workflows. You don’t need to mock 8 services to test a checkout flow. You don’t need contract testing between teams. You run the tests, they pass or fail, and you know whether your code works.
Straightforward deployment. One build, one deploy, one thing to monitor. Your CI/CD pipeline is simple. Your rollback is simple. Your on-call runbook is simple. Every additional service you add multiplies operational complexity, and a monolith has exactly one.
Lower infrastructure cost. One application server (or a few for redundancy) is cheaper than running 15 services, each with their own compute, load balancer, and monitoring. For early-stage companies watching burn rate, this matters.
The Modular Monolith: Having It Both Ways
The fear behind the anti-monolith argument is usually about code quality: “if everything’s in one codebase, it’ll become a tangled mess.” That’s a valid concern, but the solution isn’t microservices — it’s structure.
A modular monolith organizes code into distinct modules with clear boundaries. Your payments module, your user management module, your notification module — each has its own directory structure, its own internal API, and its own data access patterns. They live in the same codebase and deploy as one application, but they’re designed with clean interfaces between them.
The key discipline: modules communicate through defined interfaces, not by reaching into each other’s internals. If your payments module needs user data, it calls the user module’s API, not its database tables directly. This boundary enforcement — through code review, linting rules, or architecture tests — gives you the organizational clarity of microservices without the operational overhead.
Rails, Django, Spring Boot, Laravel, and even Next.js all support this pattern well. You don’t need special tooling. You need discipline and code review.
When You’ve Outgrown the Monolith
A monolith isn’t forever. There are genuine signals that it’s time to start extracting services. But these are signals you experience, not risks you imagine.
Deployment contention. When multiple teams are regularly blocked waiting for each other’s code to merge and deploy, and this is measurably slowing feature delivery, you have a real problem.
Scaling mismatch. When one component genuinely needs to scale independently — your image processing pipeline needs 50x the compute of your API layer, for example — extraction makes economic sense.
Team size beyond 25-30 engineers. At this scale, the coordination overhead of a single codebase starts to outweigh its simplicity benefits. But most companies reach this size only after years of successful product development — on a monolith.
The companies that get this right treat extraction as surgery: precise, planned, and done only when the diagnosis is clear. They don’t extract services because they might need to someday. They extract because they’ve hit a specific, measurable constraint that a monolith can’t solve.
Start Simple, Stay Simple as Long as You Can
The most successful technical decisions I’ve seen across 20+ client engagements share a common trait: they chose the simplest architecture that could support the business, and they resisted complexity until complexity was unavoidable. A monolith is that simple starting point. Build your product, find your market, grow your team — and let the architecture evolve when the business demands it, not when a conference talk suggests it.
Related: Microservices vs. Monolith: The Pragmatic Decision Framework | The Prototype-to-Production Gap | When to Replatform or Rewrite
