DevelopmentSenior

Microservices

Microservices are an architecture in which a system is built as a set of services that can each be deployed on their own. The defining property is independent deployability, not size — a service is the right size when one team can own it end to end. Everything good about the style follows from that independence, and everything hard about it follows from the fact that the calls between services now cross a network.

Defining property
Deploy independently
Data rule
One store per service
Failure mode
Distributed monolith

What makes a service a microservice

Ignore the "micro" for a moment — it has caused more damage than any other syllable in this field, because it invites people to measure services in lines of code. The property that matters is this: can this service be changed, tested and deployed to production without deploying anything else? If yes, it is a microservice. If a release requires two services to go out together, you have one service in two repositories.

Three things follow from that test, and they are the actual rules of the style. A service owns its data — no other service reads its tables, ever. A service is owned by one team, which is what makes independent deployment an organisational fact rather than a technical possibility. And a service is aligned to a business capability — orders, pricing, notifications — not to a technical layer.

Each service is deployed independently and owns its own store, so no service reads another's tables. The red path is a synchronous call chain: three hops of 99.9% availability multiply out to 99.7%, which is worse than any single service in it. The dashed amber edge is the alternative — search reacts to an event, so it can be down without stopping checkout.

The alignment rule is the one worth dwelling on, because getting it wrong is silent. Services named "database service", "validation service" and "API service" are layers with network calls between them: every feature touches all three, so nothing can be released independently and you have paid the full distributed-systems price for zero independence. Services named after what the business does are touched by one feature at a time, which is where the whole benefit comes from.

What you need before you start

Fowler put this as a height restriction: you must be this tall to use microservices. The list is short, it is not negotiable, and every item on it is something that a monolith lets you get away with not having.

  • Automated deployment

    Deploying is a pipeline, not a person. With twenty services, a deploy that needs a human step happens twenty times as often — and the manual step is what will stop teams releasing independently.

  • Observability

    Distributed tracing, aggregated logs with a correlation id, per-service metrics. Without them a request that failed somewhere across four services is not debuggable, only guessable.

  • Rapid provisioning

    A new service must be running in production in hours, not after a ticket queue. If standing one up is expensive, teams will bolt features onto existing services and the boundaries will drift.

  • Teams that own their service

    Including in production, including at 3am. A service operated by a separate ops team is not independently deployable in practice, whatever the diagram says.

Data is the hard part

The rule is simple: each service owns its own store, and no other service touches it. The consequences of that rule are where most of the real work lives, and they surprise people who arrived expecting the difficulty to be about deployment.

You lose the join. A report that needs order data and customer data can no longer ask the database for both; it asks two services, or it reads from a view built for that purpose. You also lose the transaction. An operation that spans two services cannot be atomic, so "take payment and reserve stock" becomes a sequence of steps with a defined behaviour for the case where step two fails after step one succeeded — a saga, with compensating actions written by you.

And you inherit eventual consistency as a product question, not a technical one. If stock is reserved a second before payment clears, someone has to decide what a customer sees during that second and what happens if the payment then fails. That decision belongs to the business, it has to be made explicitly for every cross-service flow, and pretending it away is how systems end up double-charging people.

Synchronous, asynchronous, and the availability bill

Every call between services is a call that can be slow, fail, or — worst of the three — succeed on the other side while returning an error to you. Choosing how each call is made is the main design activity in this style.

A synchronous call couples availability. If checkout calls billing, which calls the ledger, and each is up 99.9% of the time, checkout is now up 99.7% — worse than any component in the chain, and worse than the monolith that did the same work in one process. Add retries and you also add load exactly when the downstream service is already struggling, which is how a slow dependency becomes an outage.

An asynchronous edge breaks that coupling. If checkout publishes an event and search consumes it, search can be down for an hour without a single customer noticing at checkout; it catches up when it returns. The cost is that search is now stale for that hour, and someone has to be comfortable with that. This is the trade in one sentence: synchronous buys freshness and pays in availability, asynchronous buys availability and pays in staleness.

MechanismWhat it protects againstWhat it costs you
TimeoutA caller holding a thread until a dead dependency answers, which exhausts the pool.A slow-but-fine call may be cut off
Retry with backoffTransient failures — a restarted pod, one dropped packet.Load added to a struggling service
Circuit breakerRetrying into a dependency that is genuinely down, and the cascade that follows.A degraded answer while it is open
Idempotency keyThe retry that charges the customer twice because the first call actually succeeded.State the receiver must keep

The last row is the one juniors are never warned about and seniors have all been burned by. A network failure does not tell you whether the operation happened; the call may have succeeded and the response been lost. Any retry of anything that changes state must therefore be safe to repeat, which means the receiver has to recognise a request it has already processed. Retries without idempotency are not resilience — they are a machine for duplicating side effects.

How this shows up in real delivery

The honest summary of two decades of practice is that microservices solve an organisational problem with a technical mechanism. If your constraint is that twelve teams cannot ship without coordinating, this style removes that constraint and is worth the bill. If your constraint is that the code is tangled, or that deployment is scary, or that the database is slow, this style adds a network to the problem and charges you for it monthly.

Where it degrades

  • A shared database, which is the single change that turns a set of services into a distributed monolith.
  • Services named after technical layers, so every feature touches all of them and nothing releases independently.
  • Releases that require several services to go out in a fixed order — the definition of not being independent.
  • Long synchronous call chains, where availability multiplies down below what the monolith achieved.
  • Retries without idempotency keys, which duplicate side effects under exactly the conditions retries exist for.
  • One service per developer, which produces a topology that reflects hiring rather than the domain.
  • No distributed tracing, which turns every cross-service incident into an archaeology exercise.

When to use it

Use it when

  • Several teams genuinely block each other on one deployment and you have measured the cost.
  • Components have genuinely different scaling profiles, availability targets or compliance boundaries.
  • Continuous deployment, distributed tracing and rapid provisioning already work.
  • Teams can run their own service in production, out of hours included.
  • The business can answer what a user should see while two services are temporarily inconsistent.

Avoid it when

  • A small team — the coordination problem this solves does not exist yet, and the bill still arrives.
  • The domain boundaries are not understood yet, so the service boundaries would be guesses that are expensive to move.
  • The real problem is tangled code — splitting along existing coupling produces a distributed version of it.
  • Deployment is still manual, which multiplies by the number of services rather than staying constant.
  • Strong consistency across the whole system is a business requirement rather than a habit.

Found this useful?

Share it with someone who is working on the same problem.