Leer en español · Lire en français · Auf Deutsch lesen
Section 6.3 of 6.8 · Advanced · 26 min read
Microservices: principles, benefits and costs
Video lesson
5 min · narrated, with captionsListen only (audio)
Read the transcript
Microservices are widely discussed, and easy to get wrong. You'll learn what they really are, how big a service should be, and what they cost.
Imagine a large food hall instead of one big restaurant. Each stall has its own small team, its own kitchen and its own menu, and can change its recipes without asking the other stalls. Customers order through a shared front desk, which sends each order to the right stall. The hall can grow by adding stalls, and one stall's burnt kitchen doesn't close the others. But somebody has to run the front desk, deliveries, cleaning and safety checks for every stall, and a meal from three stalls needs careful coordination. Microservices are the food hall.
Microservices build one application as a suite of small services. Each runs in its own process, is built around a business capability, can be deployed on its own by automated machinery, and talks to the others through lightweight mechanisms, such as HTTP APIs or messaging. The hardest question is where to draw the lines between the services.
The best answer is to align each service with a bounded context: a part of the business where a model and its language are consistent. An order means a basket in ordering, but a pickup and drop-off in dispatch. A context is usually what one team can own. The defining property is independent deployability: a team can release its service without releasing any other. That depends on the contracts, not the containers: backward-compatible changes and contract tests. Each service owns its data, and no other service reads or writes it directly. The price is high. Joins become calls between services, and one transaction becomes a saga that's only eventually consistent. And business logic belongs in the services. The network and the message broker only carry messages: smart endpoints and dumb pipes.
Here's the food-delivery platform as microservices. At the top are three clients: the customer app, the restaurant tablet and the courier app. Requests go through an API gateway, a single entry point that handles encryption, authentication, rate limits and routing. Keep business logic out of it, or you rebuild the old service bus. Next comes a backend for frontend for each kind of client: a thin back end, owned by that client's team, which calls the services and shapes the responses. Then the services: ordering, menu, pricing, dispatch and payments. Each owns its own database, and each may choose the kind of store that suits it. Along the bottom runs a message broker carrying events such as order placed. Each supporting part solves a real problem, but is one more thing to run.
So how big should a service be? There's no correct size in lines of code or people. A better tool is to weigh the forces on service granularity. Six forces push towards splitting: the service's scope, how often its code changes, very different scaling needs, fault tolerance, security and extensibility. Four push towards keeping things together: transactions that must succeed or fail as one, workflows that would become long chains of calls, shared code, and tightly related data. Granularity is a balance, decided case by case.
Here's the balance in practice. A Pricing service calculates basket totals, including delivery fees and promotions. Promotions change several times a week, and every release re-tests fee calculation. Someone proposes a split. Code volatility argues strongly for it. Promotions change weekly, fee rules quarterly. Fault tolerance argues moderately for it. A bad promotion rule has crashed Pricing twice. Separated, a failure could simply mean no discount is shown. Workflow argues moderately against. Every basket total needs both, so a split adds a synchronous hop and its failure modes. The decision: extract Promotions, with care. Pricing calls it with a hundred and fifty millisecond timeout and, if that fails, prices the basket without discounts and re-prices it at checkout.
The most serious failure is the distributed monolith: services that share a database, call each other in long chains, or must be released together. All the costs of distribution, none of the independence. Entity services are named after nouns, such as customer or order, and offer little more than storing and fetching records, so every business process has to call several of them. Chatty services turn one user request into dozens of calls, so latency grows with every feature. The style also needs basics first: rapid provisioning, monitoring of every service, and automated deployment with easy rollback. And teams must run what they build, on call. So microservices fit many teams with well-understood domains. They don't fit a small team, or a new product whose domain is still being discovered. They're a tool with a cost, not a destination.
The full section has every granularity force with an example, and the full list of anti-patterns. Next, in Section six point four, we look at event-driven architecture. See you there.
What you need to know
- Microservices (Lewis and Fowler, 2014) build one application as small services, each in its own process, built around a business capability, independently deployable and communicating through lightweight mechanisms.
- Service boundaries work best when aligned with bounded contexts and with teams; boundaries drawn around technical layers or database tables produce chatty, tightly coupled services.
- Independent deployability is the defining property; it depends on backward-compatible contracts, contract tests and versioning, not only on separate containers.
- Database per service makes each service its own quantum but replaces joins and transactions with API calls, events, sagas and eventual consistency.
- Supporting patterns include API gateways, backends for frontends, service discovery and service meshes; each solves a real problem and adds something to run.
- Granularity is decided by weighing disintegrators (scope, volatility, scalability, fault tolerance, security, extensibility) against integrators (transactions, workflow, shared code, data relationships).
- Typical failures are the distributed monolith, entity services, chatty services and shared libraries that couple deployments; the style needs automation, monitoring and teams that run what they build.
Definition and origin
Microservices is an architectural style in which one application is built as a suite of small services, each running in its own process, each built around a business capability, each independently deployable by automated machinery, communicating through lightweight mechanisms such as HTTP APIs or messaging, with a minimum of central management. That summary follows the article that defined the term for most of the industry: Microservices by James Lewis and Martin Fowler, published on martinfowler.com in March 2014. The article notes that the name was discussed at a workshop of software architects near Venice in May 2011 and adopted by the group in May 2012; companies such as Amazon and Netflix were already working this way.
Lewis and Fowler did not give a formal definition. They described nine common characteristics, and practitioners still use them as a checklist: componentisation via services; organisation around business capabilities; products rather than projects; smart endpoints and dumb pipes; decentralised governance; decentralised data management; infrastructure automation; design for failure; and evolutionary design. The sections below take the most architecturally significant of these in turn.
Services aligned to bounded contexts
The hardest question in microservices is where to draw the lines. The most reliable answer comes from strategic domain-driven design (Chapter 5): a bounded context is a part of the business within which a model and its language are consistent. In food delivery, 'order' means a basket being paid for in Ordering, a ticket to cook in the restaurant's kitchen context and a pickup and drop-off in Dispatch. Each context is a natural candidate for one service, or for a small number of services owned by one team.
Aligning services with bounded contexts gives them high cohesion (what changes together lives together) and low coupling (contexts talk through a few explicit contracts, translated at the edges). It also aligns them with teams, because a bounded context is usually the size of thing one team can understand and own. Services drawn around technical layers or around database tables lack both properties, and the anti-patterns later in this section are mostly the result.
Independent deployability: the defining property
If one property defines microservices, it is independent deployability: a team can change its service and release it to production without changing, testing or releasing any other service at the same time. Sam Newman, in Building Microservices (second edition, 2021), treats it as the property to protect above all others, because almost every other benefit depends on it. Faster delivery, smaller blast radius, targeted scaling and team autonomy all disappear if releases must be coordinated.
Independent deployability is a property of the contracts, not of the containers. Two services that are deployed separately but must always be released together, because a change in one breaks the other, are not independently deployable. Protecting it requires backward-compatible interface changes (add fields, never remove or rename them without a deprecation period), consumer-driven contract tests and versioned APIs, all covered in communication and APIs.
Database per service, and smart endpoints with dumb pipes
Lewis and Fowler's decentralised data management becomes, in practice, the rule of database per service: each service owns its data, and no other service reads or writes it directly. Other services get the data through the owner's API or through events the owner publishes. The rule is what makes a service a separate quantum: its schema can change, and it can be scaled, backed up and restored, without asking anyone. Each service may also pick the store that suits it (a relational database for orders, a key-value store for sessions, a search engine for menus), which is called polyglot persistence.
The price is high and must be paid knowingly. Queries that used to be a SQL join across tables now cross services. Changes that used to be one transaction now need a saga and become eventually consistent. Reporting needs a separate pipeline. Chapter 7 treats data ownership in depth.
The other characteristic with the largest architectural consequences is smart endpoints and dumb pipes. Business logic belongs in the services; the network and the message broker only carry messages. This is a direct reaction to the enterprise service bus of classic SOA, where routing rules, transformations and processes accumulated in central middleware.
A microservices topology for food delivery
Supporting patterns
A set of supporting patterns has grown up around the style. Each solves a real problem and each adds a component that must be run, so adopt them when the problem appears rather than all at once.
Common supporting patterns
| Pattern | Problem it solves | What it is | Watch out for |
|---|---|---|---|
| API gateway | Clients should not know the addresses of dozens of services or repeat authentication against each. | A single entry point that terminates TLS, authenticates, rate-limits and routes requests to services; it may combine several calls into one response. | Business logic creeping into the gateway, recreating an ESB; a single point of failure that needs high availability. |
| Backend for frontend (BFF) | A mobile app, a web app and a partner API need differently shaped data; one general API serves none of them well. | A separate, thin back end per kind of client, owned by that client's team, which calls services and shapes responses. The name is Phil Calçado's, from SoundCloud; Sam Newman popularised the pattern in 2015. | Duplicated logic across BFFs; a BFF that becomes a mini-monolith. |
| Service discovery | Service instances start, stop and move all the time; addresses cannot be configured by hand. | A registry of the current healthy instances, queried by clients or by a load balancer. Kubernetes provides it through DNS names for Services. | Stale registry entries; clients caching addresses too long. |
| Service mesh | Every service needs mutual TLS, retries, timeouts, traffic shifting and telemetry; implementing them in every language is wasteful. | An infrastructure layer of proxies (traditionally a sidecar next to each service instance) controlled centrally, such as Istio or Linkerd. | Considerable operational complexity; retries configured in both the mesh and the code multiply load. |
Fowler's prerequisites, and the organisational costs
In August 2014 Martin Fowler listed three things an organisation should have before building microservices at all. Rapid provisioning: the ability to create a new server or environment in hours, which means automation. Basic monitoring: both technical measures (errors, latency, availability) and business measures (orders per minute) for every service, so that problems are spotted quickly. Rapid application deployment: an automated pipeline that takes a service to test and production quickly, with easy rollback. Without these, many small services multiply manual work. His companion idea, the microservice premium (2015), is that the style carries a fixed cost in complexity and productivity that only pays off for systems complex enough to need it.
The organisational costs are as real as the technical ones. The style assumes long-lived teams that own their services in production, the principle Amazon's CTO Werner Vogels described in 2006 as You build it, you run it
. That means every team needs operational skills and an on-call rota. It needs a platform: deployment pipelines, observability, secrets management and service templates, usually provided by a platform team (Chapter 10). It needs agreement on cross-cutting standards (logging format, correlation identifiers, API conventions) without central approval of every change. And it needs Conway's law to be used deliberately: team boundaries and service boundaries should match, otherwise every change needs several teams to coordinate.
Granularity: how big should a service be?
There is no correct size in lines of code or in people. Lewis and Fowler found teams of about a dozen people owning a service and teams of about six owning six services, and regretted that the word 'micro' focuses attention on size. Ford, Richards, Sadalage and Dehghani give a better tool in Software Architecture: The Hard Parts (2021): weigh the forces that push towards splitting a service, which they call granularity disintegrators, against those that push towards keeping it together, the integrators.
Forces on service granularity
Granularity disintegrators and integrators, with food-delivery examples
| Force | Direction | Ask | Example |
|---|---|---|---|
| Service scope and function | Split | Does the service do one cohesive thing, or several unrelated things? | A Notifications service that also calculates loyalty points is doing two jobs. |
| Code volatility | Split | Does one part change far more often than the rest? | Promotions rules change weekly; the rest of Pricing changes quarterly. |
| Scalability and throughput | Split | Do parts need very different capacity? | Courier location updates arrive thousands of times a second; courier onboarding a few times a day. |
| Fault tolerance | Split | Would a failure in one part needlessly take down another? | A crash in the PDF receipt generator should not stop order placement. |
| Security | Split | Do parts need different access controls or audit? | Stored card tokens versus public menu data. |
| Extensibility | Split | Will new variants be added often? | New payment methods every few months, each a separate deployable. |
| Database transactions | Keep together | Must these changes succeed or fail atomically? | Creating an order and reserving its delivery slot. |
| Workflow and choreography | Keep together | Would splitting create long chains of calls for ordinary requests? | Basket, checkout and order status call each other on every request. |
| Shared code | Keep together | Do the parts share significant business logic that changes together? | Menu and Pricing share complex dish-option rules. |
| Data relationships | Keep together | Are the data tightly related, with joins and foreign keys? | Orders and order lines; splitting them gains nothing. |
Worked example
Worked example: should Pricing be split?
The food-delivery platform has a Pricing service that calculates basket totals: dish prices, option surcharges, delivery fees (which depend on distance and demand) and promotions (vouchers, first-order discounts, restaurant offers). The promotions team ships changes several times a week, and each Pricing release needs a full regression run of fee calculation. Someone proposes splitting Pricing into Fees and Promotions services. The architect weighs the forces.
| Code volatility (split) | Strong: promotions change weekly, fee rules quarterly. Separating them would let promotions ship without re-testing fees. |
|---|---|
| Scalability (split) | Weak: both are called once per basket view, at the same rate. |
| Fault tolerance (split) | Moderate: a bad promotion rule has crashed Pricing twice. If Promotions were separate, a failure could be degraded to 'no discount shown' while fees still work. |
| Security (split) | Weak: same data sensitivity. |
| Database transactions (keep) | Weak: no write needs to span both; a voucher is redeemed when the order is placed, in Ordering. |
| Workflow (keep) | Moderate: every basket total needs both. After a split, Pricing (or a BFF) must call Promotions synchronously on every basket view, adding a hop and its failure modes. |
| Shared code (keep) | Moderate: both need the basket model and currency rounding rules, which change rarely. |
| Data relationships (keep) | Weak: promotions refer to restaurants and dishes by identifier only. |
| Decision | Split, with care: extract Promotions; Pricing calls it with a 150 ms timeout and, on failure, prices the basket without discounts and flags it for re-pricing at checkout. Share only a small, versioned basket-model library. |
The decision rests on two strong disintegrators (volatility and fault isolation) outweighing moderate integrators. Had promotions needed to update the same rows as fee calculation in one transaction, the answer would probably have been different.
Writing the forces down turns a matter of taste into an argument others can check, and the record belongs in an architecture decision record.
Anti-patterns
Most failed microservice programmes fail in recognisable ways. The most serious is the distributed monolith: a system with all the costs of distribution and none of the independence, because its services share a database, call each other in long synchronous chains or depend on the same deployable libraries.
Microservice anti-patterns
| Anti-pattern | Symptom | Cause and remedy |
|---|---|---|
| Distributed monolith | Services must be released together in a fixed order; one service's outage stops all of them. | Shared database, synchronous chains and shared deployable libraries. Remedy: redraw boundaries around bounded contexts, give each service its data, move calls to events, or merge back into a modular monolith. |
| Entity services | Services named after nouns (Customer, Order, Restaurant) with create, read, update and delete operations; every business process calls several of them. | Boundaries drawn around data rather than behaviour. Michael Nygard calls this the entity service anti-pattern. Remedy: organise around business capabilities and processes. |
| Chatty services | One user request causes dozens of calls between services; latency and failure rates rise with every feature. | Boundaries cut through a cohesive workflow, or interfaces are too fine. Remedy: coarser APIs, local copies of reference data, merging services that always change together. |
| Shared libraries that couple deployments | A change to a common library forces every service to rebuild and redeploy; versions drift apart. | Business logic or domain models placed in shared code. Remedy: share only stable, technical libraries; duplicate small amounts of business code; version anything shared and allow several versions in use. |
| Microservice envy | A small team with a new product starts with thirty services and spends most of its time on infrastructure. | Choosing the style for fashion or CV value rather than drivers. Remedy: start with a modular monolith and extract services when a driver appears. |
| Nanoservices | Services so small that most of their code is plumbing; a feature needs changes in five of them. | Over-applying 'small'. Remedy: use the granularity integrators; size services by cohesion, not lines of code. |
Characteristics of microservices
| Characteristic | Rating | Reason |
|---|---|---|
| Deployability | High | Each service is released on its own, many times a day, with a small blast radius. |
| Scalability and elasticity | High | Each service scales independently; small services start quickly and scale in fine steps. |
| Fault tolerance | High (if designed for) | Failures can be isolated per service, but only with timeouts, bulkheads, fallbacks and asynchronous communication; otherwise faults cascade. |
| Evolvability | High | Small, well-bounded services can be rewritten or replaced individually. |
| Testability of one service | High | Each service is small; contract tests check its interactions. |
| Testing the whole | Low | End-to-end behaviour emerges from many services and is hard to reproduce in a test environment. |
| Performance | Low to medium | Network hops, serialisation and security checks add latency to every interaction. |
| Data consistency | Low | No shared transactions; business invariants spanning services rely on eventual consistency and sagas. |
| Simplicity | Low | Many moving parts, many failure modes, much infrastructure. |
| Overall cost | Low (expensive) | Platform, tooling, observability and people to run dozens of services. |
When microservices fit, and when they do not
Microservices fit organisations with many teams working on one product who need to release independently, systems whose parts have genuinely different scalability, availability or security needs, and domains that are understood well enough to draw stable boundaries. They need investment in a platform and in operational skills before the first service is split off.
They do not fit a small team, a new product whose domain is still being discovered (boundaries drawn too early are expensive to move across the network), a system whose core workflows need strong transactional consistency across most of its data, or an organisation without automated deployment and monitoring. Fowler's MonolithFirst advice (Chapter 5) and the reversals that some companies have documented, such as Segment's 2018 consolidation of more than 140 destination services back into one, show that the style is a tool with a cost, not a destination.
Check your understanding
Two services are deployed in separate containers but must always be released together. Are they independently deployable?
No. Independent deployability is about being able to change and release one without the other; a coupled contract or shared database makes them one unit for release purposes.
What is the difference between an API gateway and a backend for frontend?
A gateway is a general entry point handling cross-cutting concerns such as TLS, authentication, rate limiting and routing; a BFF is a client-specific back end that shapes data for one kind of front end and is owned by that front end's team.
Which granularity integrator most strongly argues against splitting two parts that must update data atomically?
Database transactions: splitting them would replace one ACID transaction with a distributed workflow such as a saga.
Why are services named Customer, Order and Restaurant with only create, read, update and delete operations a warning sign?
They are entity services: boundaries around data rather than behaviour, so every business process must call several services, creating chatty, tightly coupled interactions.
Sources and further reading
Have a question about this section? ArchitectBot, the course’s AI study assistant, answers from the course first.
Ask: “We have 25 microservices and every release needs four teams to coordinate. How do I tell whether we have a distributed monolith, and what should we fix first?”