
Monolith vs microservices: the pros and cons, and why a modular monolith comes first
Most comparisons end in it depends. Here are the four things it depends on, one product priced both ways at public cloud rates, and the one test that keeps module boundaries honest.
Monolith vs microservices: which should most teams choose?#
Most teams should start with a modular monolith, one deployable split into modules with enforced boundaries, and move a part to its own service only when its team, release pace or load demands it. Martin Fowler made the same call in his MonolithFirst note of 3 June 2015. There he wrote that almost every system built as microservices from scratch "ended up in serious trouble".
His reason still holds: good service boundaries are hard to draw before you know the domain. Also, moving a feature between services costs far more than moving it between modules. So the monolith vs microservices question is really about timing.
Four inputs decide when a part should leave:
Team size
Does the part have its own team that waits on everyone else's release?
Releases per week
Does it need to ship far more often than the rest?
Parts that scale differently
Does its load rise and fall on its own pattern?
Operations maturity
Can the team already provision, monitor and deploy services without manual steps?
If the first three are no, the part stays a module. And if the fourth is no, every part stays a module, whatever the first three say.
What does running eight services cost compared with one monolith?#
In this worked example at AWS list prices read on 30 October 2026, eight services cost $230.39 a month against $159.29 for the same product as one monolith. Both run on Amazon Elastic Kubernetes Service (EKS), a managed cluster of Kubernetes, the open-source system that runs containers, with AWS Fargate billing the containers by the second.
Show data table
| Item | Value |
|---|---|
| Monolith: 2 tasks of 1 vCPU, 2 GB | 159.29 USD |
| Eight services: 16 pods of 0.25 vCPU, 0.5 GB | 230.39 USD |
Eight services cost $230.39 a month against $159.29 for the same product as one monolith.
The setup is simple, so the sum can be redone. First, the monolith runs as two copies, each with 1 vCPU, a virtual share of a processor, and 2 GB of memory. Second, the eight services run two copies each at the smallest Fargate size, 0.25 vCPU and 0.5 GB. Then both sit on one EKS cluster behind one Application Load Balancer, the AWS service that spreads web traffic across copies. Finally, a month is 24 hours times 30 days, as in the load balancer page's own examples.
And the gap grows with every service. In the worked example, each service at two small copies adds $17.77 a month. So sixteen services reach $372.57 a month at the same AWS prices.
Show data table
| Stage | Value |
|---|---|
| 2 services | 123.75 USD |
| 4 services | 159.29 USD |
| 8 services | 230.39 USD |
| 12 services | 301.48 USD |
| 16 services | 372.57 USD |
Each service at two small copies adds $17.77 a month, so sixteen services reach $372.57.
Price your own service count
Enter how many services you run; it prices the monthly bill and the upkeep hours with the worked example's sizes and assumption.
Monthly bill
Enter a service count
- Difference from the $159.29 monolith
- Enter a service count
- Upkeep hours a month
- Enter a service count
An illustrative model of the worked example above. Modelled, not measured.
Still, the cloud bill is the small number. Say each service takes four hours a month of upgrades, pipeline fixes and on-call handoffs, an illustrative assumption rather than a measurement. Then eight services take 32 hours a month of someone's time before anyone ships a feature.
What is the difference between monolithic and microservices architecture?#
A monolith ships as one deployable over one database, while microservices split the system into separately deployed services that each own their data and talk over the network. James Lewis and Martin Fowler set out the microservice style on 25 March 2014. In their account, services form around business capabilities, and each one manages its own data and ships through automated deploys.
Microsoft's Azure Architecture Center guide, updated 11 July 2025, puts the data rule plainly. In its words, microservices "are responsible for persisting their own data or external state". In contrast, Google Cloud's overview calls a monolith "a single, unified unit" whose components share resources and data.
The modular monolith sits between the two. It keeps one deployable and one process, so a call between modules is still a plain function call. Instead of a network wall, the boundary is a build rule that refuses any module reaching into another's internals.
What are the monolithic architecture pros and cons?#
A monolith is simpler to build, test, debug and run, but as the codebase and team grow, every change waits on one shared release and one shared scaling unit. One process means one stack trace, one test run and one thing to deploy. Because every call stays in memory, there is no network hop to time out.
But the disadvantages of monolithic architecture show up with growth. For example, a bug fix in billing ships on the same release as a half-done change in the catalogue. Also, a busy part forces the whole application to scale, since there is only one unit to copy.
Those are the pressures that start most migrations. In a 2019 study on arXiv, Fritzsch, Bogner and Wagner traced 14 system migrations through 16 interviews across 10 companies. As a result, they named maintainability and scalability as the main reasons to move. In particular, most of those companies chose a rewrite over splitting their old code.
Show data table
| Item | Value |
|---|---|
| Systems whose migration was studied | 14 |
| In-depth interviews | 16 |
| Companies | 10 |
Fritzsch, Bogner and Wagner traced 14 system migrations through 16 interviews across 10 companies.
Size alone is not the problem, though. Shopify's February 2019 account describes a codebase in Ruby on Rails, a web framework, worked on by more than a thousand developers, and it stayed one codebase. Meanwhile, a 2026 arXiv benchmark by Swayam Amol Katole tested both styles and found them alike at 50 virtual users, splitting only at 100.
What are the disadvantages of microservices over monolithic architecture?#
Microservices turn function calls into network calls, trade one database for eventual consistency between many, and multiply the pipelines, upgrades and on-call rotations a team must run. Martin Fowler's trade-offs note of 1 July 2015 lists three costs. First, remote calls are slow and can fail. Second, strong consistency gets so hard that everyone must manage eventual consistency, where data agrees only after a delay.
Then comes the cost most teams feel first. In Fowler's words, "You need a mature operations team to manage lots of services". Twilio Segment said the same in July 2018, calling the overhead of its services "a huge tax on our team".
Some lines of the bill are fixed, and some grow with each service. In the worked example at AWS prices read on 30 October 2026, the control plane and the load balancer cost the same both ways.
| Line item | Monolith | Eight services |
|---|---|---|
| Fargate compute | 71.09 | 142.19 |
| EKS control plane | 72.00 | 72.00 |
| Application Load Balancer hours | 16.20 | 16.20 |
| Total | 159.29 | 230.39 |
Finally, there is a cost of falling behind. On the Amazon EKS pricing page, read 30 October 2026, a cluster costs $0.10 an hour in standard support. Once its Kubernetes version passes the end of standard support, extended support costs $0.60 an hour. So more services, each with its own upgrade work, make it easier to slip into the higher rate.
$0.10
Standard Kubernetes version support
$0.60
Extended Kubernetes version support
Once a cluster's Kubernetes version passes the end of standard support, its charge rises from $0.10 to $0.60 an hour.
| Option | US dollars per cluster per hour |
|---|---|
| Standard Kubernetes version support | $0.10 |
| Extended Kubernetes version support | $0.60 |
Source: Amazon Web Services, Amazon EKS pricing, read 30 October 2026
What is a modular monolith, and why is it the right default?#
A modular monolith is one deployable split into modules that own their data and talk only through published interfaces, so it keeps a monolith's simple operations and gains service-style boundaries. Shopify chose it by name in February 2019. In that post, its team wrote: "We chose to evolve Shopify into a modular monolith". So the code stayed in one codebase, with boundaries defined and respected between components.
The idea now has a body of writing behind it. In a January 2024 review on arXiv, Ruoyu Su and Xiaozhou Li included 64 studies on the modular monolith. They found three frameworks for building one: Service Weaver, Spring Modulith and Light-hybrid-4j.
Show data table
| Item | Value |
|---|---|
| Studies included | 64 studies |
| Studies defining the modular monolith (RQ1) | 47 studies |
| Studies naming frameworks (RQ2) | 17 studies |
| Studies naming cases (RQ3) | 6 studies |
Su and Li included 64 studies on the modular monolith.
Google researchers went further in a 2023 HotOS workshop paper. In their view, "microservices conflate logical boundaries (how code is written) with physical boundaries (how code is deployed)". So they wrote the app as a logical monolith and let a runtime decide where each part ran. Against an eleven-service deployment, their prototype cut latency by up to 15 times and cost by up to 9 times.
15x
Latency cut, up to
9x
Cost cut, up to
Against an eleven-service deployment, the prototype cut latency by up to 15 times and cost by up to 9 times.
| Option | times lower, up to |
|---|---|
| Latency cut, up to | 15x |
| Cost cut, up to | 9x |
In short, the module boundary is the part of microservices most teams want. The network is the part they pay for.
Which documentation should you build a modular monolith from?#
For a Spring Boot codebase, start with the Spring Modulith fundamentals page for how modules map to packages, then its verification page for the check that fails a broken boundary. Here Spring Boot is the common Java framework for web services, and Spring Modulith is its add-on for module rules.
- Spring Modulith fundamentals: each direct sub-package of the main package is a module. Then
ApplicationModules.builds the model of those modules from the code.of - Spring Modulith verification:
verify()refuses cycles between modules. It also refuses any reference to another module's internal types.
Read them in that order, because the check only means something once the packages match the modules you intend.
What is the smallest check that keeps module boundaries honest?#
A single test that builds ApplicationModules from the application class and calls verify fails the build when a module reaches into another module's internals or the modules form a cycle. First, put each module in a direct sub-package of the main package, such as example.order and example.inventory. Second, build the model from the application class. Then call verify() inside a test, so a broken boundary fails like any other test.
package example;
import org.junit.jupiter.api.Test;
import org.springframework.modulith.core.ApplicationModules;
class ModularityTests {
@Test
void verifiesModuleBoundaries() {
// Fails on a module cycle or on access to another module's internal types
ApplicationModules.of(Application.class).verify();
}
}The test needs the spring-modulith-core library on the test classpath. As a result, the rule that keeps a modular monolith honest runs on every build, not only in a design document.
Which teams moved back from microservices to a monolith?#
Segment folded over 140 services into one in July 2018, and Prime Video cut one monitoring service's infrastructure cost by over 90% as a monolith in March 2023. Alexandra Noonan's Goodbye Microservices post for Twilio Segment, dated 10 July 2018, gives the before state. Back then, three full-time staff spent most of their time keeping the system alive. Also, on-call pages for load spikes were common.
Over 140
Destination services before
1
Destination services after
Segment folded over 140 services into one in July 2018.
| Option | services in the destination pipeline |
|---|---|
| Destination services before | Over 140 |
| Destination services after | 1 |
Source: Twilio Segment (Alexandra Noonan), Goodbye Microservices, 10 July 2018
The Prime Video case is narrower but sharper. In a 22 March 2023 post, Marcin Kolny wrote that the team's distributed design hit "a hard scaling limit at around 5% of the expected load". That design passed data between parts run by AWS Step Functions, a workflow service. Once the team moved everything into one process, the cost fell. In his words, "Moving our service to a monolith reduced our infrastructure cost by over 90%".
Still, neither case says services are wrong. Instead, both priced the split for one workload and found it did not pay.
When is the modular monolith the wrong fit for a part?#
A part has earned its own service when it has its own team, a release pace the shared release blocks, or load unlike the rest, and deploys and monitoring are already automated. Fowler's MicroservicePrerequisites note of 28 August 2014 sets that floor. A team should start a new server in hours, monitor services well, and deploy them fast.
The Azure guide, updated 11 July 2025, sizes a service to its team: something "a single small team of developers can write and maintain". So a part with its own team and its own pace is the natural first service. For instance, a search index that scales on its own, with its own store, fits better as a service than inside the billing and catalogue modules.
Modular monolith
2 of 3 reasons to split, floor missing
A part has a reason to split, but the prerequisites come first: automated deploys, fast provisioning and monitoring.
- Yes: Several teams. 12 people is more than one team (the rule of thumb is under 10 for one).
- No: A faster release pace. 5 releases a week fits one shared release.
- Yes: Its own scaling. 1 part with a load pattern the rest does not share.
| When | Then |
|---|---|
| A small team (under 10 people, an example to change) with no part that scales differently | Monolith or modular monolith |
| Without all three prerequisites: automated deploys, fast provisioning and monitoring | Modular monolith, because the prerequisites come first |
| A part with its own team, a release pace well above the rest or its own scaling pattern, plus all three prerequisites | That part becomes a service while the rest stays modular |
Three cases point away from the modular monolith. First, a part that must run in another language or runtime, such as a machine learning model server, is better as its own service. Second, a part under separate rules for data or audit is easier to fence off as a service. Finally, a part with its own team and steady release pressure deserves its own deploy, once the operations floor is in place.
In what order should you split a monolith into services?#
Split one part at a time, starting with the one whose team, release pace or load differs most, and route traffic to it while the monolith keeps serving the rest. Sam Newman's 2019 book Monolith to Microservices is built on that idea of changing a running system step by step instead of rewriting it.
AWS Prescriptive Guidance names six patterns for the job. Among them are decompose by business capability and the strangler fig pattern. With the strangler fig pattern, a routing layer sits in front of the monolith, sends one route to the new service, and leaves the rest alone until the old part is switched off.
In practice the order is short:
- Pick the part the four inputs flag most strongly.
- Make it a clean module first, with
verify()passing, so the cut line is already drawn. - Move its data to its own store, then route its traffic to the new service.
- Watch the bill and the on-call load for a quarter before the next one.
Meanwhile, avoid the big rewrite. Fritzsch and colleagues found that most companies in their 2019 study rewrote rather than split. But a rewrite stops feature work while the old system keeps changing.
Where should you go from here?#
Start from a modular monolith, check the four inputs every quarter, and move a part out only when those inputs say it has earned its own service. Until then, the worked example gives you the bill to expect. And the verify() test keeps the module lines straight.
For the frontend half of the same choice, read micro-frontends with module federation. Then read the step-by-step strangler migration for micro-frontends. Once parts do split, how split services call each other covers the calls between them. If you are starting a new product, see how we approach custom software development. If you are building the operations floor, see our DevOps and deploy pipeline work. Otherwise, the Fowler notes and the two Spring Modulith pages are enough to make the call on your own.