September 07, 2026

Monolith vs Microservices architecture: split or not to split?

Viktor Hulyi

Senior Full-stack Developer

11 min

"As you think about legacy code, it's helpful to remember two things. First, it's been running your business this long and got you to where you are today. That is something to celebrate. Second, today's modern technology is just tomorrow's legacy."

Kristen Womack

Somewhere along the way, "monolith" became a dirty word. Engineers use it the way they might say "technical debt" or "legacy codebase": a polite word for something embarrassing, something you tolerate until you can afford to fix it.

And "microservices" became the opposite. Modern, serious, what real companies do.

So companies migrated. Some spent years on it. Some spent millions.

Then in 2023 Amazon's Prime Video team published something awkward. Their video quality analysis service had been built as distributed microservices. They moved it back into a single process and cut infrastructure costs by 90%. Not a failure to recover from. A rational engineering decision, published by the company that popularised the pattern.

Worth being precise about what that does and doesn't prove. When Gartner's Peer Community surveyed 300 IT and software engineering leaders in 2023, 88% of those using microservices said the integration with their existing stack had been at least moderately successful, and only 15% rated their microservices management as not very or not at all successful.

So microservices work, for the people who need them. The trouble sits with everyone else: teams that adopted the pattern before they had the problem it solves. The useful question is whether your company has earned the right to split yet.

TL;DR: Microservices succeed for teams with the organizational problem they solve, and hurt everyone else. The Architecture Readiness Model has 3 stages: monolith (ship fast, under 10 engineers), modular monolith (enforce domain boundaries with tooling, post-PMF), selective microservices (extract only against a specific measured constraint, with 15+ engineers and mature DevOps). Two things most teams get wrong: a monorepo is a code storage decision rather than an architecture, and you can scale individual modules of a modular monolith without splitting anything.

Table of Contents

Monorepo is not monolith, and the difference matters
What microservices actually cost in 2026
Three architectures, three stages
Can you scale one module of a modular monolith?
How to keep a modular monolith modular
How to know which stage you are actually in
When migration is the right call
How to migrate without breaking your product
The decision

Monorepo is not monolith, and the difference matters

Before the architecture question, clear up the vocabulary. Two different decisions get mashed into one argument constantly, and the confusion drives bad calls.

Monolith vs microservices is a runtime decision. How many things do you deploy, and do they talk over a network or through function calls?

Monorepo vs multi-repo is a storage decision. How many Git repositories hold your code?

Those axes are independent. You can run 40 microservices out of a single repository. Google does exactly that: billions of lines of code in one repository, serving thousands of independently deployed services. You can also run a single deployed monolith split across 6 repositories, which is the worst of both worlds and more common than anyone admits.

This distinction has become louder in 2026 because of AI coding agents. The argument going around is that monoliths are back because agents work better with everything in one place. What agents actually reward is a monorepo: one place to read shared types, contracts, tests, and docs, and one place to make a change that spans 3 services.

That says nothing about how many things you deploy. If your team is arguing about monoliths because Claude or Cursor struggles to navigate 12 repositories, you have a repository problem with a cheap fix, and your deployment topology can stay exactly as it is.

What microservices actually cost in 2026

A warning about the numbers in this debate. Multipliers get quoted constantly, "microservices cost 4x more", "debugging takes 35% longer", and almost none of them trace back to a primary source. They circulate between blog posts that cite each other. We cut several from an earlier version of this article for exactly that reason. Treat any cost multiplier you see, including in comparison articles like this one, as unverified until someone shows you the study.

What you can price is your own situation, and the categories are consistent:

Infrastructure. Each service needs its own compute, and each one is provisioned for its peak rather than its average. 20 services sized for peak cost more than 1 service sized for peak, unless the workloads genuinely diverge.

Platform engineering. Someone maintains the pipelines, the service mesh, the observability stack, the container orchestration. At small scale that is a person's whole job funded to solve a problem you may not have.

Debugging time. One request crossing 6 services means 6 log streams and 6 places a field can go missing. This is where teams lose hours they never planned for.

Coordination. Every cross-service change needs a versioned contract and a staged rollout instead of one commit.

Run those 4 numbers for your own team before anyone else's multiplier.

The cost nobody prices in: your consistency boundary

Here is the part that hurts long after the invoice.

A monolith gives you one transactional boundary. When an order updates inventory and writes a payment record, the database either commits all of it or none of it. You get that for free.

Split those into services and you lose the guarantee. You now build it yourself. Idempotency keys, retries, dead letter queues, inbox and outbox tables, saga orchestration. Then reconciliation jobs for the cases where all of that still drifts. Each individual service gets simpler to read. The system gets significantly harder to reason about.

Your users experience this as a class of bug that barely exists in monoliths: the order that shows as paid but never shipped, the field that quietly vanished somewhere between service 3 and service 6. Your engineers experience it as an afternoon spent tracing one request across systems, half of which they don't own.

This is the cost that never shows up in a migration estimate, and the one your team pays every week afterward.

Three architectures, three stages

Martin Fowler wrote in 2015: "Almost all the successful microservice stories have started with a monolith that got too big and was broken up."

He was right. Most teams read that as a two-step process, monolith then microservices, and skip the stage in between. That skipped stage is where the regret comes from.

Here is the Architecture Readiness Model we use when helping clients think through the decision.

Schematic image showing monolithic and microservices architectures

Stage 1: Monolith. One codebase, one deployment unit. Fast to build, cheap to run, easy to debug. For MVPs and teams under 10 engineers, this is the right architecture. If you are building a SaaS application from scratch, start here. Distributing a system you don't yet understand is expensive.

Stage 2: Modular monolith. Still one deployment unit, with strict internal module boundaries. Each domain owns its code, its data models, its interfaces. Modules talk through defined APIs instead of reaching into each other's internals.

Shopify is the reference case. Their engineering team documented the restructuring in 2020: a core monolith of 2.8 million lines of Ruby and 500,000 commits, reorganised into 37 components with public entry points, worked on by hundreds of developers. One of the largest commerce platforms in the world, deliberately not split into services.

Stage 2 goes much further than most teams expect, it is far easier to reason about than a distributed system, and it sets up a clean extraction later.

Stage 3: Selective microservices. You pull out specific services with proven, independent needs. Not all of them. Because one service has to scale at a different rate than the rest of your system, and you can measure it.

Most companies jump from Stage 1 to Stage 3, then spend their engineering time managing the distributed system they built rather than building product, and wonder why shipping got slower.

Stage 2 is what earns you the right to go further.

Can you scale one module of a modular monolith?

Yes, and this is the most common objection to Stage 2, usually stated as fact: "a monolith scales as one unit, so you have to scale everything to scale anything."

That is true of a badly built monolith. It is not a property of the architecture.

Three approaches, in increasing order of effort:

1. Conditional module registration. Your application reads an environment variable at startup and registers only the modules named there. Same binary, same repository, same build. You deploy 3 instances of it: one running only orders, one running only reporting, one running everything else. Infrastructure passes the module list in at startup. This works especially well for queue-backed workloads, where you scale the workers consuming a backed-up queue and leave the rest at 1 instance.

2. Gateway routing by path. Deploy the same application 3 times and put an API gateway in front. /catalog routes to one instance group, /checkout to another, /orders to a third. Each group scales on its own metrics. From the outside it looks like 3 services. In the repository it is one codebase with one test suite and one deploy pipeline.

3. Plain horizontal scaling. Often enough on its own. Idle modules cost close to nothing in memory in a well structured application, so running 6 copies of the whole thing is cheaper than running 20 services, and dramatically cheaper in engineering time.

In the systems we have worked on, the ceiling shows up at the database long before the application layer. Read replicas, caching, connection pooling, and per-module schemas will carry you past the point most teams believe they need to split. When one module's data genuinely needs its own server, per-module schemas mean you can move it without touching the other modules.

There is a real limit here, and it is worth stating plainly because it is the strongest argument on the other side. If one part of your system has a genuinely different resource profile, a nightly batch job needing a large cluster for 40 minutes against steady low-volume query traffic the rest of the day, a separate service on its own scaling policy is cheaper and simpler than anything you can do inside one deployment unit. Divergent workload profiles are a legitimate technical reason to split, independent of team size.

So the honest version of the Stage 2 pitch: you can scale a module independently without paying the distributed systems tax. What you cannot do is release that module on its own schedule, with its own team, in its own language. That is a Stage 3 benefit, and it is an organizational one.

How to keep a modular monolith modular

"Start with a modular monolith and extract later" is the most repeated advice in software architecture, and the most frequently unfollowed. The extraction so often never happens because by month 8 the modules are not modules any more.

The failure is boring and predictable. Someone is in a hurry, imports a class from another module directly instead of going through its interface, and ships it. Nobody notices in review. It happens 200 more times. Two years later you have a monolith with folders that used to mean something.

A network call between microservices makes the boundary impossible to ignore. Inside a monolith, nothing stops you unless you build the stop.

So build it:

  • Architecture tests in CI. ArchUnit for JVM, NetArchTest for .NET, dependency-cruiser or ESLint boundary rules for TypeScript, import-linter for Python. A test that fails the build when billing imports from shipping internals is worth more than any convention document.
  • Framework-level enforcement. Spring Modulith verifies module boundaries and generates documentation from the actual structure. Other ecosystems have equivalents.
  • Separate schemas per module. One database, one schema per domain, no cross-schema joins. This is the boundary that matters most, because a shared table is the coupling that makes extraction impossible later.
  • In-memory events between modules. Modules publish events rather than calling each other directly. Swapping the in-memory dispatcher for a real queue is then a configuration change, and the extraction you planned for actually stays possible.

Skip this and Stage 2 becomes a naming convention rather than an architecture. Teams that skip it are the ones who conclude, 2 years later, that modular monoliths don't work.

How to know which stage you are actually in

Sam Newman, whose book on microservice migration patterns is the closest thing this field has to a standard reference, recommends 3 questions before adopting microservices: What are you hoping to achieve? Have you considered alternatives? How will you know if the transition is working?

Useful questions. Here is a more concrete readiness assessment to run first.

DevOps maturity. Microservices need scripted deployments, infrastructure as code, CI/CD, automated testing, and code review standards. Without those you can't run one service reliably, let alone 20. If your team deploys manually, a CI/CD pipeline is a prerequisite for migration, not a nice extra.

Team size. Below 15 engineers, coordination overhead usually exceeds the benefit. Above 15 to 20, teams start needing autonomy over their domain, and microservices start enabling it. The two-pizza rule Jeff Bezos described is a description of the minimum team unit capable of owning a service end to end.

Domain boundaries. Can your codebase be separated by business domain today? In many systems every entity depends on every other entity. Microservices won't create boundaries you don't have. They will spread the dependency mess across network calls.

A specific constraint. The trigger should be measured, not anticipated. A service handling 10x the traffic of everything else. A module whose release cadence is genuinely blocked by the rest. A domain with different availability requirements.

Organizational structure. Do you have teams that can design, build, deploy, and maintain a service without external approvals? Microservices work when architecture matches the org chart.

Gaps on that list mean Stage 2 is the right move: invest in module boundaries, enforce them in CI, and wait for a real signal.

When migration is the right call

For some systems, at the right moment, splitting is exactly right.

One of our clients, Lake, ran a vacation rental platform on a monolithic back end. The architecture worked for years, until demand grew faster than the system could handle. The monolith had a hard ceiling on the number of vacation properties it could connect. With roughly 500 properties on the platform and demand pushing far past that, staying put meant capping growth.

A specific, measurable problem. A real ceiling.

We rebuilt Lake's back end architecture in 3 months. The result: 80x growth in connected properties, from 500 to 40,000, and a 210% spike in website activity. Lake had earned the right to split, and the numbers reflected it.

Netflix's migration is the other end of the range. Their monolith was causing outages and service interdependencies no team could untangle. By Netflix's own account the move took roughly 7 years, alongside a shift to AWS, a switch to NoSQL databases, and a service-by-service rebuild. Worth remembering when a vendor quotes you a quarter.

Image illustrating Netflix Microservices Infrastructure

Netflix Microservices Infrastructure

Both started from a real constraint rather than a preference. The architecture was holding the business back in a way you could measure. That is the signal.

How to migrate without breaking your product

If a specific service genuinely needs to come out, here is how to do it without causing damage.

Image presenting process of migration of monolith systems to the microservices architecture

Set concrete goals first. Increased uptime? Independent scaling for one service? Faster release cadence for one team? The outcome sets the scope. "Modernise the architecture" describes activity, not a result.

Define domain boundaries before writing new code. Wrong boundaries mean a change in one service cascades into 5 others, which turns independent deployment into a coordination problem.

Istio is the cautionary example. They migrated to microservices, then moved the control plane back to a monolith after realising all their control plane services deployed together, shared one administrative domain, and gained nothing from being separate. Make sure the domain you are extracting is genuinely independent first.

Extract the isolated, high-value pieces first. Email, notifications, authentication, payment processing. Leave the entangled core in place, possibly forever.

Give each service its own data store. One repository per service, no exceptions. Shared databases recreate the coupling you were escaping. Individual stores can drift, so invest in master data management and keep a redundant copy in the monolith during transition.

Don't rewrite the extracted service. Keep its code clean and separate. When new functionality is needed, create a new service rather than modifying a running one.

Separate build, separate container. A build process per service, deployed in containers. This is what makes independent deployment real rather than nominal.

Keep the system running throughout. The Strangler Fig pattern, gradually replacing parts of the monolith while it keeps serving traffic, is the most reliable approach. Extract one service, run both in parallel, validate, then switch off the old path.

For examples of companies that did this well, see successful practices from Netflix, Wix, and Best Buy.

The decision

Your situationThe right move
Pre-PMF, or under 10 engineersMonolith. Ship fast, validate, skip the microservices tax on a product that may pivot 3 times
Post-PMF, growing codebase and teamModular monolith. Draw domain boundaries, enforce them in CI, scale modules with conditional registration or gateway routing
15+ engineers, mature DevOps, one service with a measured independent needExtract that service. Surgically, one at a time, with a defined outcome
Agents struggle to navigate your codebaseConsolidate into a monorepo. Leave your deployment topology alone

The architecture that works is the one matching where you are, not where you plan to be in 3 years.

At Brocoders we have worked with teams at all 3 stages: building SaaS applications from scratch on well structured monoliths, modernising legacy applications that hit real ceilings, and designing extraction strategies for teams that had genuinely outgrown their architecture.

If you are unsure which stage you are in, we are happy to look at your architecture and give you a straight answer.

Contact us

Frequently Asked Questions

What is the difference between a monolith and microservices?

A monolith is a single deployment unit where all application logic runs together. A microservices architecture breaks the application into independent services, each owning one business domain, deployed and scaled separately. In a monolith, components share a codebase and a database. In microservices, each service owns its code and its data store and communicates over a network.

Is a monorepo the same as a monolith?

No. A monolith is a runtime decision about how many units you deploy. A monorepo is a storage decision about how many Git repositories hold your code. Google runs thousands of independently deployed microservices out of a single repository containing billions of lines of code. You can have microservices in a monorepo, or a monolith spread across many repositories.

Can you scale one module of a modular monolith independently?

Yes. Three approaches work: conditional module registration, where the application reads an environment variable at startup and loads only the named modules, so you deploy the same binary multiple times with different module sets; gateway routing, where the same application is deployed several times and an API gateway routes by path prefix to instance groups that scale separately; and plain horizontal scaling, where idle modules cost almost nothing in a well structured application. What you cannot do without splitting is release a module on its own schedule with its own team.

Do AI coding agents work better with monoliths?

Agents benefit from a monorepo, not from a monolith. What helps an agent is seeing shared types, contracts, tests, and docs in one place and making a change that spans several services in a single commit. That is a repository layout question, unrelated to how many units you deploy. Consolidating 12 repositories into one is a much cheaper fix than changing your architecture.

When should you migrate from monolith to microservices?

When a specific, measurable constraint your current architecture can't solve. A service needing to scale at 10x the rate of everything else. Release cycles genuinely blocked by the current structure. Organizational scale, 15 or more engineers, where team autonomy starts requiring architectural autonomy. Migrating because microservices are more modern is not enough justification.

How do you stop a modular monolith from turning into a ball of mud?

Enforce boundaries with tooling rather than discipline. Architecture tests in CI (ArchUnit for JVM, NetArchTest for .NET, dependency-cruiser for TypeScript, import-linter for Python) that fail the build on cross-module imports. Framework-level enforcement like Spring Modulith. A separate database schema per module with no cross-schema joins. In-memory events between modules instead of direct calls. Without enforcement, module folders become naming conventions within a year.

How long does it take to migrate from monolith to microservices?

It depends heavily on the scope. Extracting a single, isolated service from a well-structured codebase can take weeks. Rebuilding a full back-end architecture with real scaling constraints — like the Lake project we delivered in 3 months — is measured in months when approached with clear goals and a focused team. Netflix's complete migration took 7 years. Scope your extraction by outcome, not by a desire to move everything.

Can a monolith handle serious scale?

Yes. Shopify's engineering team documented a core monolith of 2.8 million lines of Ruby and 500,000 commits, restructured into 37 components and worked on by hundreds of developers, powering one of the largest commerce platforms in the world. Scale comes from architecture quality, caching, and database design rather than from deploying services separately.

What team size justifies microservices?

Most engineering teams cite 15 to 20 engineers as the threshold where the overhead starts paying off. Below that, the coordination cost exceeds the autonomy benefit. The better question is whether a team can own a service end to end, design through maintenance, without approvals from other teams. If not, the organizational structure isn't ready regardless of headcount.

What is the strangler fig pattern?

A migration strategy where you gradually replace parts of a monolith by building new services alongside it instead of replacing everything at once. New functionality goes into the new service while the monolith version keeps running until the replacement is validated, then you switch off the old path. The system stays live throughout, which makes it far lower risk than a full rewrite.

4.86
Thank you for reading! Leave us your feedback!
4789 ratings

Read more on our blog