Web & App Development

Monolith vs. Microservices: Which Should You Start With?

Shubham Parmar8 min read
Monolith vs. Microservices — a decision framework for choosing the right architecture

In short: start with a monolith unless you already have a specific, proven reason not to. A well-organized "modular monolith" — one deployable application with clean internal boundaries between modules — gets most teams the maintainability benefits people reach for microservices to get, without the network calls, service discovery, and deployment overhead. Move to microservices when the pain is concrete: a specific service needs to scale independently, or your team has grown past the point where one codebase can ship without people blocking each other.

What this means for you

  • Default to a monolith at MVP and early-growth stage — it is faster to build, faster to change, and has one thing to deploy and debug.
  • Structure it as a modular monolith from day one (clear module boundaries, no cross-module database queries) so extracting a service later is a refactor, not a rewrite.
  • Split a piece out only when you can name the specific bottleneck it fixes — "it might scale better" is not a reason, "this service needs to scale independently of the rest of the app" is.

"Monolith vs. microservices" gets framed as a permanent architectural identity, as if choosing one locks you in forever. It doesn't. The more useful question is sequencing: which one do you need on day one, and what has to be true before the other one earns its cost?

What a monolith gets you

A monolith is one codebase, one deployable, one database (usually). That sounds unglamorous, but it's the source of almost everything that makes early-stage development fast:

  • One thing to run and debug. A stack trace stays in one process. There's no distributed tracing to configure before you can answer "why did this request fail."
  • Refactoring is cheap. Moving logic between two modules in the same codebase is a code change. Moving it between two services is an API contract change, a deployment, and a migration plan.
  • No network calls where a function call would do. Every service boundary you add is a new place a request can time out, retry, or fail partially — failure modes a monolith doesn't have internally.
  • One deploy pipeline. Shipping a change touches one build, one test suite, one release — not a coordinated rollout across N services with compatibility constraints between versions.

None of this means monoliths don't have real weaknesses — a large, badly organized one becomes a place where nobody can change one thing without breaking three others. But that's a modularity problem, not an argument for microservices. A monolith with clean internal boundaries doesn't have that problem; a monolith without them will have the same problem after being split into services, just distributed across a network instead of contained in one process.

What microservices actually solve — and what they don't

Microservices exist to solve two specific problems: independent scaling and independent deployment.

Independent scaling matters when one part of your system has radically different resource needs than the rest — a video-transcoding job that needs GPU instances shouldn't force your marketing pages onto the same fleet. Independent deployment matters when enough engineers are working in the same codebase that they're routinely blocked on each other's changes, code review queues, or a shared release train — usually a problem that shows up around 15-20+ engineers, not at 3-8.

What microservices don't automatically solve is code quality, team communication, or architectural clarity. A poorly bounded microservices split produces what's often called a "distributed monolith": services that are technically separate deployables but so tightly coupled through synchronous calls and shared data that you still have to change and deploy several of them together to ship anything — except now every one of those changes also has to cross a network boundary, with all the latency, partial-failure, and versioning problems that come with it. You've paid the full operational cost of microservices and kept the coordination cost of a monolith.

The middle ground most teams skip: the modular monolith

A modular monolith is a single deployable application organized into modules with enforced boundaries — each module owns its own data access and exposes a defined interface to the others, the same discipline you'd want between microservices, just without the network hop. Teams get most of the maintainability win (no tangled cross-module dependencies, each module ownable by a sub-team) while keeping one deploy, one process, and in-process function calls instead of network calls.

The practical payoff shows up later: if a module in a well-structured modular monolith really does need to become its own service — because it has a genuinely different scaling profile, or a specific team needs to own its release cycle independently — extracting it is a bounded, mechanical piece of work. Extracting a module from a monolith that was never organized this way means untangling database queries, shared state, and implicit dependencies first, which is most of the actual difficulty in a monolith-to-microservices migration.

A framework for deciding

Two questions do most of the work:

Domain boundaries are still shiftingDomain boundaries are stable and well understood
Pain is organizational (team growth, coordination overhead)Modular monolith — you don't yet know where the real seams are, so a hard service split now will guess wrong and need redoing.Consider extracting the specific module causing coordination pain, once its interface is stable.
Pain is technical (one component has a measured, different scaling need)Extract just that one component as a service; leave the rest as a monolith.Microservices are a reasonable fit if the org is also large enough to own them operationally.

Notice what's absent from this table: team size or company stage on their own, and "microservices are the modern way to build software." Neither is a real reason. The reasons that hold up are always about a specific, current bottleneck — a component that needs to scale differently, or a team boundary that's actively causing coordination pain today, not one you're anticipating for a future you haven't reached yet.

A case study in the other direction

In 2023, Amazon's own Prime Video engineering team published a widely discussed writeup of moving one of their services — an audio/video quality-monitoring system — from a distributed, serverless microservices architecture back to a monolith. The original design used AWS Step Functions to orchestrate multiple separate components per video stream, with S3 as intermediate storage between steps. At high volume, the orchestration layer itself became the bottleneck — hitting service quota limits from the sheer number of state transitions — and the constant handoffs between components (including passing video frames through S3) added cost and latency that had nothing to do with the actual work being done. Consolidating the components into a single process on EC2/ECS removed the orchestration overhead and the intermediate storage step entirely, and the team reported a roughly 90% reduction in infrastructure cost for that service, along with more scaling headroom, not less.

The lesson isn't "microservices are bad" — Amazon runs plenty of microservices successfully, including elsewhere in Prime Video. It's that a distributed architecture has to earn its overhead with a real workload characteristic that needs it (here, they didn't have one), and that "microservices by default" can cost you exactly the scalability you adopted them for.

Where this leaves a startup or growing product

If you're building an MVP or an early-stage product, a monolith — organized as a modular monolith from the start — is very rarely the wrong call. It lets a small team move fast, keeps your infrastructure bill low, and doesn't foreclose splitting things out later, provided you kept the internal boundaries clean. The moment to revisit the decision is when you can point at a specific component with a proven, different scaling need, or a team boundary that's genuinely blocking shipping — not when an architecture diagram with a dozen boxes feels more "serious" than a well-organized single codebase.

This is the same judgment call we bring to web and app development engagements: architecture decisions sized to where the product actually is, not to where it might be someday. If you're scoping a new build, our MVP development work starts from exactly this question — what's the leanest architecture that won't need a rewrite when the product actually grows.

How much any of this costs or how long it takes depends entirely on your specific system and team — there's no honest single number for "what does re-architecting cost." That's a scoping conversation, not a rate card.

Frequently asked questions

Should a startup start with microservices or a monolith?

Almost always a monolith, structured as a modular monolith with clean internal boundaries between components. Microservices add network calls, service discovery, and deployment coordination that most early-stage teams don't have the workload or headcount to justify yet. Start monolithic and extract a service later, once you can name the specific bottleneck that requires it.

What is a modular monolith?

A single deployable application organized into modules with enforced boundaries — each module owns its own data access and exposes a defined interface to the others, similar to the discipline you'd want between microservices, but without the network hop. It gets most of the maintainability benefit of service separation while keeping the operational simplicity of one deployable.

When should you actually move from a monolith to microservices?

When the pain is specific and current, not anticipated: a particular component has a measured scaling or resource profile different from the rest of the system, or your engineering team has grown large enough (commonly cited around 15-20+ engineers) that coordinating deploys and code review in one codebase is actively slowing shipping. Splitting services out before either of those is true usually adds operational cost without solving a real problem.

What is a "distributed monolith" and why is it worse than either option?

It's what you get when a system is split into separate services without genuinely independent boundaries — the services still have to be changed and deployed together because they're tightly coupled through synchronous calls or shared data. You end up with the coordination overhead of a monolith and the network, latency, and operational overhead of microservices, with the benefits of neither.

Do successful companies ever move from microservices back to a monolith?

Yes — Amazon's own Prime Video team published a widely cited 2023 writeup of consolidating a distributed, serverless microservices architecture for one of their services back into a monolith, reporting about a 90% infrastructure cost reduction and more scaling headroom, not less. It's a useful reminder that a distributed architecture has to earn its overhead with a real workload need, not adopt it by default.

Sources

Need help putting this into practice?

Tech Programmer builds and ships this work for startups and enterprises. Tell us what you are trying to do and we will tell you what it takes.

Related reading