Ch. 10 · Microservices

Microservices API Versioning and Evolution

Evolve service APIs without breaking consumers using additive changes, explicit versioning and consumer-driven contracts.

~2 min readadvancedupdated Oct 5, 2026

Two services that become independent deployments need a plan for changing their interface without a coordinated release. Additive changes are safe; removals and type changes are not. Versioning and contract tests make the boundary explicit instead of accidental.

Before you start

You should understand REST or RPC APIs, and event-driven systems help. This article covers interface evolution; it assumes separate deploy pipelines.

Step-by-step walkthrough

Step 1: Prefer additive changes

Adding an optional field, a new endpoint, or a new event type does not break existing consumers, so treat these as backward compatible and ship them without a version bump. Consumers must ignore unknown fields, which is the tolerance side of the same rule.

Step 2: Version deliberately when you must break

A breaking change needs a new version, whether in the URI (/v2/orders), a media type, or a message header. Run the old and new versions side by side and let consumers migrate. A version is a contract with a date, not a vanity number.

Step 3: Deprecate with evidence

Publish a deprecation timeline, add a Sunset header, and track per-consumer usage so you know who still calls the old version. Remove the old version only after usage reaches zero, not on a guess.

Worked scenario

An additive field keeps the existing consumers working.

{
  "id": "o-1",
  "total": 42,
  "currency": "USD",
  "giftWrap": false
}
JSON

Walk through the example

giftWrap is a new optional field, so a consumer that predates it simply ignores it and continues reading id and total. Renaming total, by contrast, would break every consumer, so it would belong in a new version. The additive rule keeps the fleet moving without lock-step deploys.

Common mistake

Making a breaking change and assuming “no one uses that field”; without usage telemetry the assumption is a guess. Another is versioning events implicitly by changing their schema in place, which silently breaks every stored and queued message.

Verify the behavior

Run consumer-driven contract tests against the provider for each consumer and fail the build on a breaking diff. Assert that consumers tolerate unknown fields by adding one and re-running their tests. Track old-version usage and confirm it reaches zero before removal.

Interview exercise

You must rename a widely used field. What is the safe path?

Answer and reasoning

Add the new field alongside the old one, have consumers migrate to the new name, and keep both in sync for a deprecation window. Publish the timeline and sunset the old field once usage drops to zero. Only if that window is impossible do you cut a new version and run it in parallel, because a rename in place is a breaking change for every consumer at once.

Continue learning

Compare contract strategy in Contract evolution and Contract testing. Read the Google API design guide and try the Microservices interview questions.

More in Microservices

read ✓Microservices · hard

Microservices Anti-Corruption Layer

Protect a service's domain model from a foreign or legacy model with a translation layer at the boundary.

~2 min readread →
read ✓Microservices · hard

Microservices Backend for Frontend

Use a per-client BFF to aggregate services and shape responses, without letting it become a shared god service.

~2 min readread →
read ✓Microservices · hard

Microservices Distributed Locking

Coordinate exclusive access across instances with leases and fencing, and prefer partitioning or idempotency when you can.

~2 min readread →
esc