Guide

Microservices move complexity out of the code and into operations. Each split adds a network call, a pipeline, a set of dashboards and a set of trust decisions. Martin Fowler's list of prerequisites is a good minimum: rapid provisioning, basic monitoring and rapid application deployment should exist before you split. A team running dozens of services needs more than that.

The big idea

A monolith has one set of logs, one deploy and one stack trace. A system of services has many of each, and one request often spans several of them. Without the right tools, debugging becomes guesswork. The operational work is what makes a split pay off.

Walking through it

The main needs, and what each one solves:

NeedWhat it solves
Discovery and load balancingServices find healthy instances of each other. See load balancing.
Distributed tracing (OpenTelemetry)Follows one slow request across services. See observability.
Correlated logs with request IDsConnects the log lines from each service.
CI/CD per serviceDeploys one service without a release train.
Contract tests (consumer-driven, such as Pact)Catches a provider change that breaks its callers.
API versioningChanges an API without breaking its callers. See API design.
Resilience on every callTimeouts, retries and circuit breakers. See resilience.
Service identity and mTLSProves who is calling on internal APIs.

Tracing shows its value on a simple chain. A request enters the gateway, which waits on orders, which waits on payments, which waits on a card API. Without a trace ID carried through all of them, you see only the gateway's slow response. With a trace, you see the whole chain, and the fix is in the card API call.

Contract tests answer a specific risk. With independent deploys, a change to the Payments API could break its callers without anyone noticing. Each consumer publishes what it expects, and the provider's pipeline verifies those expectations before it releases. Pact is the common tool.

Versioning discipline is simple to state. Make additive changes only. Never remove or repurpose a field while a caller depends on it.

A rough heuristic: once you pass a few dozen services, you need a platform team. Otherwise every product team reinvents the same infrastructure, and each does it a little badly.

Common mistakes

  • Splitting before basic monitoring and fast deploys exist. Each split then makes debugging slower.
  • Skipping contract tests with independent deploys. Providers break callers without knowing it.
  • Trusting internal calls. Without service identity, anything that can reach the network can call anything.
  • No timeouts on internal calls. One slow service then ties up callers all the way up the chain.

Check yourself

  1. Why does tracing matter once you have many services?

    A slow request crosses many services. Without a trace ID carried through them, you can only guess which one is slow.

  2. What does a consumer-driven contract test catch?

    A provider change that breaks what its callers expect, before the provider releases it.

Key takeaways

  • Splitting moves complexity into operations: discovery, tracing, pipelines, contracts and identity.
  • Put monitoring and fast deploys in place before you split.
  • Contract tests and API versioning protect callers when services deploy independently.
  • Resilience patterns belong on every internal call.

Further learning

Josh Evans's "Mastering Chaos" is the best inside account of running microservices at Netflix scale, including the failure modes that led to circuit breakers and bulkheads.

Mastering Chaos - A Netflix Guide to MicroservicesInfoQ

Next: strangler fig migration, which covers how to move off a monolith safely.