Architecture patterns
The operational tax of microservices
What a team needs in place before it splits into services, including discovery, tracing, per-service pipelines, contract tests, versioning, resilience and service identity.
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:
| Need | What it solves |
|---|---|
| Discovery and load balancing | Services find healthy instances of each other. See load balancing. |
| Distributed tracing (OpenTelemetry) | Follows one slow request across services. See observability. |
| Correlated logs with request IDs | Connects the log lines from each service. |
| CI/CD per service | Deploys one service without a release train. |
| Contract tests (consumer-driven, such as Pact) | Catches a provider change that breaks its callers. |
| API versioning | Changes an API without breaking its callers. See API design. |
| Resilience on every call | Timeouts, retries and circuit breakers. See resilience. |
| Service identity and mTLS | Proves 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
-
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.
-
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.
Next: strangler fig migration, which covers how to move off a monolith safely.
Related
Monoliths and microservices
When to split a system, how to draw service boundaries, and the operational cost that microservices add.
Observability: logs and traces
Structured logs, distributed tracing and OpenTelemetry, so you can ask new questions of a running system instead of guessing which dashboard to open.
Resilience patterns
The toolkit for keeping one failure from becoming an outage, from timeouts and retries to circuit breakers, bulkheads, load shedding and cells. Start here for the resilience map.