Microservices vs monolith: key differences and when to choose each
Microservices vs monolith compared on 6 criteria, including deployment, scaling and data, plus the modular monolith middle path and named write-ups.
A monolith is one application that is built, deployed and scaled as a single unit, while microservices split an application into independently deployable services that talk over a network. Microservices let teams release and scale parts separately, at the price of remote calls, distributed data and more operational work. Martin Fowler's guideline from 2015 is to avoid microservices unless the system is too complex to manage as a monolith.
What is the difference between microservices and a monolith?
James Lewis and Martin Fowler described the microservice architecture in 2014 as a way of designing applications as suites of independently deployable services, and said common characteristics are organisation around business capability, automated deployment, intelligence in the endpoints and decentralised control of languages and data. They define a monolith as a single logical executable in which all request-handling logic runs in one process.
The table compares the two on six criteria, with the evidence given in the sections below.
| Criterion | Monolith | Microservices |
|---|---|---|
| Deployment | One build and one deployment, so a change to a small part means rebuilding and redeploying the whole | Each service deploys independently, so one service can be updated without redeploying the rest |
| Scaling | The whole application scales, for example by running many instances behind a load balancer | Each service scales on its own, so only the busy part gets more resources |
| Team structure | Teams share one codebase, and a good modular structure is hard to keep over time | Teams are organised around business capabilities and each owns its services |
| Data | Usually one database, so one transaction can cover several modules | Each service owns its data, so consistency is often eventual and handled with compensating operations |
| Failure handling | One process, so a bug can take down every feature | Services fail separately if callers handle faults, but every remote call can fail |
| Complexity | Simpler to start and to debug, harder to keep modular as it grows | More moving parts and a need for mature DevOps practice |
How does a monolith work, and what are its limits?
A monolith runs all of its logic in a single process, so the language's own classes, functions and namespaces divide the code. Fowler and Lewis note that a monolith can be run and tested on a developer's laptop and scaled horizontally by running many instances behind a load balancer. AWS's comparison page says the monolithic approach suits a simple application or a prototype, since it needs less planning.
The same article lists the frustrations that appear as a monolith grows. Change cycles are tied together, so a small change requires rebuilding and deploying the whole application. A good modular structure is hard to keep over time. Scaling means scaling the whole application rather than the part that needs more resource. Within a process, however, calls between components are method or function calls, and transactions can keep several resources consistent, which Fowler and Lewis note is common practice inside monoliths.
How do microservices work, and what do they cost?
Microsoft's Azure Architecture Center describes a microservices architecture as a collection of small, autonomous services, each implementing a single business capability within a bounded context. Its listed benefits are independent deployment, small focused teams, fault isolation, independent scaling and data isolation. Its listed challenges are complexity, development and testing, lack of governance, network congestion and latency, data integrity, management, versioning and the skill set a team needs.
Many of those challenges follow from one fact about remote calls. Fowler and Lewis state that remote calls are more expensive than in-process calls, so remote APIs need to be coarser-grained, and that a naive conversion from in-memory method calls to remote calls leads to chatty communication that performs badly.
They also note that distributed transactions are notoriously difficult to implement, so microservice systems accept eventual consistency and fix problems with compensating operations. Microsoft adds that a successful microservice architecture requires a mature DevOps culture, and that teams should check whether they have the skills for highly distributed systems.
The article on Docker for beginners covers the container packaging that AWS says is usually used to deploy each service.
What is a modular monolith?
A modular monolith keeps all the code in one codebase and one deployment, but defines boundaries between components and enforces them. Shopify engineer Kirsten Westeinde used the term in a post of 21 February 2019, saying Shopify chose to evolve into a modular monolith rather than adopt microservices. The post says the company outgrew its unstructured monolith in 2016, and that calling components directly avoids API version management and laggy calls.
Fowler describes the same idea as the logical way to start a monolith-first strategy: design the monolith carefully, paying attention to modularity at the API boundaries and in how the data is stored. The following Python sample, run on Python 3.9, shows two modules inside one process. The inventory function is the only code that touches stock, and the orders function calls it directly inside one database transaction.
import sqlite3
db = sqlite3.connect(":memory:")
db.executescript("""create table stock(sku text primary key, qty int);
create table orders(id integer primary key, sku text);
insert into stock values('pen', 1);""")
def reserve(sku): # inventory module: the only code that touches stock
db.execute("update stock set qty = qty - 1 where sku = ?", (sku,))
left = db.execute("select qty from stock where sku = ?", (sku,)).fetchone()[0]
if left < 0:
raise ValueError("out of stock")
def place_order(sku): # orders module: calls inventory in the same process
with db: # one transaction covers both tables
db.execute("insert into orders(sku) values(?)", (sku,))
reserve(sku)
for attempt in (1, 2):
try:
place_order("pen")
print("attempt", attempt, "ok")
except ValueError as err:
print("attempt", attempt, err)
print(db.execute("select count(*) from orders").fetchone()[0], "order row")The output shows that the second order is rejected and its insert is undone.
attempt 1 ok
attempt 2 out of stock
1 order rowIf orders and inventory were separate services with separate databases, the rollback would not come for free. The services would need compensating operations, which is the cost Fowler and Lewis describe.
What do published engineering write-ups say?
Two named companies have written about running a monolith at scale, one after leaving microservices and one after structuring its monolith, and both posts describe their own systems only.
Twilio Segment engineer Alexandra Noonan wrote on 10 July 2018 that her team consolidated over 140 services into a single service for one product pipeline. The post says 3 full-time engineers had been spending most of their time keeping the system alive.
After the change, one engineer could deploy the service in minutes, and the number of improvements to shared libraries went from 32 under microservices to 46 a year later. The post also lists trade-offs: fault isolation is difficult, in-memory caching is less effective, and updating a dependency may break several destinations.
Shopify engineer Philip Müller wrote on 16 September 2020 that Shopify's core monolith had over 2.8 million lines of Ruby code and 500,000 commits, and that the goal of its components was for developers to feel they are working on a much smaller app than they actually are. He describes making such a change to a system under active development as in large part a people problem.
Neither post proves that one style is better. They show that a team can outgrow microservices as well as a monolith, and that the decision depends on the system, as the next section explains.
Which should you choose, and when should you not use microservices?
The criterion for each choice below is the one the cited source uses. Fowler's 2015 guideline is the starting point: do not consider microservices unless the system is too complex to manage as a monolith, and build the majority of systems as a single monolithic application with good modularity.
He reports that almost all the successful microservice stories he had heard began with a monolith that got too big, and that almost all the systems he had heard of that were built as microservices from scratch ended up in serious trouble. He also notes the view is not unanimous.
- A student project or first product should be a monolith, because the criterion is fast feedback and low operating cost, and AWS says the approach suits simple applications and prototypes.
- A growing product whose changes keep colliding should become a modular monolith first, since Shopify's criterion was enforced boundaries without a network between components.
- A large organisation with many teams that need independent releases, and a mature DevOps practice, is the case Microsoft describes for microservices.
- A monolith that needs one part scaled or released separately can peel that part off as a service, and Fowler describes starting with a monolith and gradually peeling off microservices at the edges as a common approach.
- A microservice estate that costs more to run than it returns can be consolidated, as Segment did for one pipeline, after checking the fault-isolation cost.
The cloud services that either style runs on are compared in AWS, Azure and Google Cloud for beginners.
What are the limits of this comparison?
The evidence has gaps, and they are worth stating.
- Fowler's observations are accounts he had heard, and his 2014 article with Lewis said not enough time had passed to make a full judgement.
- The Segment and Shopify figures are the companies' own and describe one pipeline and one codebase each, in 2018 to 2020.
- AWS and Microsoft publish the comparison pages above and sell cloud services, so their pages are a vendor's view.
- The comparison rests on reasoning and named experience, not on measured outcomes from one system run both ways.
Where does the programme fit?
The Forward Deployed Engineer (FDE) Cohort is a Leap by Masai programme for engineers with hands-on technical experience, run as an in-person start in Goa, an online build phase and an in-person hackathon in Bangalore for qualifying participants, covering production Python, APIs, cloud deployment, LLMs, retrieval, agents, security and client engagement. The page does not list an architecture module, but its topics include legacy SQL databases, SOAP services, containers and deployment on AWS or Azure, which are pieces of an existing client system.
Frequently asked questions
What is the difference between microservices and monolith?
A monolith is one application built, deployed and scaled as a single unit, with all logic in one process. Microservices are many small services, each deployed independently and each owning its data, which talk over a network. Microservices add flexibility in releases and scaling and add remote calls, distributed data and operational work.
When should you not use microservices?
Martin Fowler's 2015 guideline is not to consider microservices unless the system is too complex to manage as a monolith, and he says most systems should be single monolithic applications. Teams without mature DevOps practice, and prototypes or simple applications, are cases that Microsoft and AWS also flag.
What is a modular monolith?
A modular monolith keeps all code in one codebase and one deployment but defines and enforces boundaries between components. Shopify used the term in 2019 for its approach: components with public interfaces inside one codebase, called directly, with no network between them and no API versioning between components.
Are microservices faster than a monolith?
Not by default. Fowler and Lewis state that remote calls are more expensive than in-process calls, so splitting a monolith adds network cost to every call that crosses a service boundary. Microservices can scale a busy service independently, but speed depends on the design, not on the style.
Can a monolith be scaled?
Yes. Fowler and Lewis note that a monolith can be scaled horizontally by running many instances behind a load balancer. The limit they name is that scaling means scaling the whole application rather than only the part that needs more resource.
How do microservices keep data consistent?
Usually without distributed transactions, which Fowler and Lewis call notoriously difficult to implement. Services accept eventual consistency and use compensating operations to fix problems. Microsoft's guidance adds that each service owns its data, so a change that spans services is unlikely to be one atomic transaction.
Next step
Choosing between these styles is easier with practice on a real system. Engineers with hands-on experience who want to work inside client systems and deployments can read about the Forward Deployed Engineer (FDE) Cohort.