Microservices
Microservices are an architectural style in which a system is built as a set of small, independently deployable services, each owning its own data store and communicating over a network (usually HTTP, gRPC, or async messages). The promise is independent team velocity; the cost is a dramatic increase in operational and integration complexity. Microservices are a trade-off, not a default — and they are usually the wrong choice for small teams and young products.
How it works
A monolith is one application: one codebase, one deployable, one database. It is easy to build, easy to test, and easy to reason about — until it isn't. As the codebase grows, builds slow down, tests take minutes, a change in shipping logic forces a full redeploy of the auth code, and 50 engineers trip over each other in the same repo.
Microservices split that application into smaller services, each owned by a team, each independently deployable, each with its own database. The shipping service deploys whenever the shipping team wants; the auth service is unaffected. Teams scale independently — if search traffic explodes, you scale search instances only, not the whole monolith.
The trade-off is brutal: every cross-service call is now a network request. Every transaction spanning services is now a distributed transaction. Every deploy is now a coordination problem. The complexity doesn't disappear — it shifts from inside the process to between processes.
What makes a service a microservice:
- Independent deployability — you can ship a new version of one service without redeploying any other. This is the defining property. If you must coordinate deploys across services, you have a distributed monolith.
- Bounded context — each service models one well-defined part of the business (orders, inventory, billing). The boundary follows the domain, not the technical layer.
- Owns its data — a service's database is not directly accessed by other services. Cross-service data access happens through the service's API, never by joining another service's tables. This is the hardest rule to follow and the most common one broken.
- Network communication — services talk to each other over a network (REST, gRPC, async messages). This is what makes them distributed systems, with all the failure modes that entails.
If a system claims to be microservices but every service shares one database, it is not microservices — it is a distributed monolith with extra network hops.
Trade-offs vs monolith:
| Dimension | Monolith | Microservices |
|---|---|---|
| Initial speed | Fast — one codebase, one deploy | Slow — must build infra first |
| Build/test time | Slows as codebase grows | Each service stays small, fast |
| Team scaling | One team trips over itself | Many teams work in parallel |
| Deploy independence | Full redeploy for any change | Ship one service at a time |
| Per-service scaling | Whole app must scale together | Scale only the hot service |
| Tech stack | One language/framework | Each service can differ |
| Failure isolation | One bug crashes everything | One service down ≠ whole site down |
| Latency | In-process calls (microseconds) | Network calls (milliseconds) |
| Data consistency | ACID transactions | Distributed transactions / sagas |
| Operational complexity | Low | High — observability, service mesh, discovery |
| Debugging | One stack trace | Distributed tracing across services |
| Cost | Lower (less infra, fewer network hops) | Higher (more services, more replicas, more ops) |
The decision is rarely 'monolith vs microservices forever'. The common pattern is: start with a monolith, split out one or two services when a real pressure appears (team scaling, isolated scaling need, separate reliability SLA), and stop splitting as soon as the pressure stops.
USE microservices when: (1) your engineering org is large enough that teams genuinely trip over each other in one codebase, (2) subsystems have sharply different scaling or reliability requirements (search needs 1000 instances, billing needs 5), (3) you need to isolate failures (a buggy service must not take down the whole site), or (4) different subsystems are owned by different teams with different release cadences. DO NOT use microservices when: the team is small (under ~20 engineers), the product is still finding product-market fit, you don't have observability infrastructure (distributed tracing, centralized logs), or you cannot afford the operational overhead. Most early-stage companies should ship a monolith. Most large platforms (Netflix, Uber, Amazon) arrived at microservices only after their monolith became unmanageable.
The distributed-system tax:
Splitting a monolith into services replaces in-process function calls with network calls. The fallout:
- Latency: an in-process call is microseconds; a network call is milliseconds — 1000x slower. Cross-service request chains add up. A 'simple' checkout that touched 4 in-process functions now pays 4× 5ms = 20ms extra.
- Partial failures: in a monolith, either the call works or the process is dead. In microservices, the auth service is up but the catalog service is slow. Every caller must handle timeouts, retries, and degraded behavior (circuit breakers, fallbacks).
- Data consistency: a monolith can wrap multi-step updates in one DB transaction. Microservices cannot — distributed transactions (2PC) are slow and brittle. Real systems use sagas, outbox patterns, and eventual consistency, all of which are harder to reason about than ACID.
- Observability: a single user request now spans 5 services. You need distributed tracing (OpenTelemetry), centralized logs (with a correlation ID), and metrics broken down per service. Without these, debugging is impossible.
- Operational load: 5 services means 5 deploy pipelines, 5 on-call rotations, 5 sets of dashboards. The SRE tax is real.
This tax is why microservices are usually the wrong default. Only pay it when the team-scaling or isolation benefit clearly outweighs.
Your company has 8 engineers and just shipped its MVP. Leadership wants to 'go microservices' from day one to 'be ready for scale'. What is the most responsible recommendation?
Pick one answer.
Five services all read and write to the same shared Postgres database. They each expose an HTTP API. Is this a microservices architecture?
Pick one answer.
In a microservices system, what is the most common replacement for an ACID transaction that spans multiple services?
Pick one answer.
Engineering mental model
Mental model. Think of Microservices as a deliberate boundary in a system. The boundary exists because something becomes harder to manage when everything is done in one place: latency, scale, failure isolation, consistency, cost, or team ownership. The useful question is not “what does Microservices mean?” but “what pressure makes this boundary worth introducing, and what new failure mode does it create?”
Before choosing Microservices, name the workload, the critical user path, the dominant bottleneck, the failure you are trying to absorb, and the trade-off you are willing to accept. If you cannot name those five things, the design is probably premature.
// Pseudocode
request = receive()
result = microservices(request)
return result
// Production questions:
// 1. What happens on timeout?
// 2. Can this operation be retried safely?
// 3. What is the bottleneck?Back-of-the-envelope reasoning
Numerical lens: write down traffic, payload size, read/write ratio, peak multiplier and durability target before choosing a component. The numbers should justify the architecture.
Interactive thought experiment: Microservices
Change the variables below and predict what breaks first in Microservices. The production lab can later reuse these same inputs.
Change one variable at a time. Predict the failure mode first, then move the slider and see whether your mental model matches the simplified system response.
If you are stuck on Microservices, start by drawing the request path and marking every network hop, stateful component, queue, cache and failure boundary. Then estimate where the system will saturate.
You increase traffic by 10× in a system using Microservices. What should you inspect first?
Pick one answer.
Which statement is the safest engineering habit when using Microservices?
Pick one answer.
You have dashboards for traffic, latency, errors and saturation. You can change the architecture, but every change has operational cost.
Production scenario: your system uses Microservices, traffic suddenly spikes, and p99 latency doubles. What is your first move?
Interview drill
Answer this without notes: When would you choose Microservices, and when would you intentionally avoid it? Mention at least one bottleneck it addresses, one failure mode it introduces, and one alternative. Then quantify the workload you are designing for.
A useful engineering lens for Microservices: define the problem it solves, the simpler design that fails first, the constraint that forces you to introduce this concept, and the new failure modes the concept creates.
Numerical sanity check
Back-of-the-envelope reasoning beats fake precision. State your traffic, payload, concurrency and growth assumptions explicitly, then calculate enough to know whether the current architecture is orders of magnitude away from the target.
Do not optimize for a memorized definition. Reason from the workload and failure mode.
Imagine the simplest version of a system using Microservices. What breaks first as traffic grows by 10×, and what would you change before reaching 100×?
Pick one answer.
What you gain, what you pay
- +Independent deployability — ship one service without touching others.
- +Team scaling — many teams work in parallel without contention.
- +Per-service scaling — scale the hot service only, not the whole app.
- +Failure isolation — a buggy service can be cordoned off without crashing the whole site.
- +Tech-stack freedom — each service can use the right language for its job.
- −Network calls replace in-process calls — 1000x latency increase per hop.
- −Distributed-system failure modes: partial failures, timeouts, retries, circuit breakers.
- −No more ACID transactions across services — sagas, eventual consistency, outbox pattern.
- −Operational complexity: observability, service discovery, API gateway, service mesh.
- −Higher cost: more services, more replicas, more infra. The per-request cost rises.
- −Harder debugging — a single user request may span 5 services.
How this breaks in production
- Distributed monolith — services share one database or must be deployed in lockstep, defeating the core benefit.
- Chatty services — a single user request fans out to 10 service calls, multiplying latency and failure surface.
- Cascade failures — one slow service causes callers to hold connections, exhausting their pools and bringing down the whole system (mitigate with circuit breakers and bulkheads).
- Schema drift — services evolve incompatible API versions and silently break each other.
- Lost observability — without distributed tracing, no one can explain why a request took 2 seconds across 6 services.
Don't fall into these traps
- •Adopting microservices before there is a team-scale or scaling problem to justify the cost.
- •Sharing one database across 'microservices' — this is a distributed monolith, not microservices.
- •Splitting along technical layers (a 'web service', a 'data service') instead of along business boundaries (orders, billing, shipping).
- •Treating network calls as free — every cross-service hop is a failure surface and a latency cost.
- •Skipping observability: no distributed tracing, no centralized logs, no per-service dashboards. Debugging becomes a forensic nightmare.
- •Naive retries without backoff or circuit breakers — a downstream outage becomes a self-inflicted DDoS.
Real systems using this
How real systems implement this
- Netflix — Netflix migrated from a monolith to hundreds of microservices over ~7 years. Each service owns its data and deploys independently. The migration was driven by team scale and the need to isolate failures — not by a single architectural decision.
- Amazon (two-pizza teams) — Amazon's famous 'two-pizza team' structure pairs small teams with small service boundaries. Each team owns a service end-to-end, including its data store. The microservices pattern emerged from the org structure, not the other way around — Conway's Law in action.
- Shopify — Shopify famously stayed on a modular monolith for years despite massive scale (Black Friday traffic), arguing that the operational simplicity of one deployable outweighed microservices' benefits until very specific pressures appeared. A reminder that even at scale, the monolith is often the right call.
Practice saying it out loud
- Q1When would you recommend a monolith over microservices? Defend the choice.
- Q2Your team split a service into 5 microservices that all share one Postgres database. What's wrong, and how would you fix it?
- Q3A user request now spans 6 services and takes 500ms when it used to take 50ms in the monolith. How do you diagnose and reduce latency?
- Q4How do you maintain data consistency across microservices without distributed transactions?
- Q5What infrastructure must exist before microservices are viable? (Service discovery, API gateway, distributed tracing, etc.)
Further reading & references
Core explanations are original NO CAP material. External references are provided for deeper study and standards.
What next?
Mark as understood once the mental model clicks.
Next recommended
Service Discovery