How do you version microservice APIs without breaking consumers?
Learn how to version microservice APIs safely: backward-compatible changes, parallel versions, deprecation timelines and contract tests to protect consumers.
Expected Interview Answer
You version APIs by making only backward-compatible (additive) changes within a version and introducing a new explicit version (via URL path, header, or media type) whenever a breaking change is unavoidable, then run old and new versions in parallel until consumers migrate.
Backward-compatible evolution means adding optional fields and endpoints rather than removing or renaming existing ones, following tolerant-reader and Postel's-law principles so clients ignore unknown fields. When a breaking change is required, you publish v2 alongside v1, communicate a deprecation timeline, monitor usage per version, and retire the old version only after consumers have moved. Consumer-driven contract tests (like Pact) catch breaking changes before release, and semantic versioning signals intent.
- Consumers upgrade on their own schedule
- No sudden breakage from a single deploy
- Additive changes avoid new versions entirely
- Clear deprecation timelines reduce surprises
- Contract tests catch breaks before release
- Parallel versions enable gradual migration
AI Mentor Explanation
Think of a cricket board updating its playing conditions: it never yanks a rule mid-series but announces the new format for the next season while the current one keeps running, giving teams time to adapt. Old and new rulesets coexist across formats, just as v1 and v2 of an API run in parallel until every team has adjusted.
Step-by-Step Explanation
Step 1
Prefer additive changes
Add optional fields and new endpoints instead of removing, renaming, or retyping existing ones to stay backward compatible.
Step 2
Design tolerant readers
Have clients ignore unknown fields so producers can extend responses without breaking consumers.
Step 3
Choose a versioning scheme
For breaking changes, expose an explicit version via URL path, header, or media type and follow semantic versioning.
Step 4
Run versions in parallel
Serve v1 and v2 simultaneously so consumers migrate on their own schedule rather than all at once.
Step 5
Guard with contract tests
Use consumer-driven contract tests (e.g., Pact) in CI to catch breaking changes before they ship.
Step 6
Deprecate deliberately
Announce timelines, monitor per-version usage, and retire the old version only after consumers have moved.
What Interviewer Expects
- Distinction between breaking and non-breaking changes
- Knowledge of versioning schemes (URL, header, media type)
- Backward compatibility and tolerant-reader principles
- Running multiple versions in parallel with deprecation
- Consumer-driven contract testing
- Semantic versioning to signal intent
Common Mistakes
- Removing or renaming fields within an existing version
- Forcing all consumers to upgrade in one big-bang cutover
- No deprecation policy or usage monitoring per version
- Overusing new versions for changes that could be additive
- Not testing contracts, so breaks reach production
Best Answer (HR Friendly)
“We change our service's interface carefully so existing users keep working: we usually just add new things instead of removing old ones. When a bigger change is unavoidable, we run the old and new versions side by side and give everyone time to switch before retiring the old one.”
Code Example
GET /v1/orders/123 HTTP/1.1
Host: api.example.com
Accept: application/json
HTTP/1.1 200 OK
Deprecation: true
Sunset: Wed, 31 Dec 2026 23:59:59 GMT
Link: </v2/orders/123>; rel="successor-version"
{
"id": 123,
"total": 49.90
}
--- migrated consumer ---
GET /v2/orders/123 HTTP/1.1
Host: api.example.com
Accept: application/json
HTTP/1.1 200 OK
{
"id": 123,
"total": { "amount": 49.90, "currency": "USD" }
}Follow-up Questions
- URL path versioning vs header versioning: which do you prefer and why?
- What counts as a breaking change in a REST API?
- How do consumer-driven contract tests prevent breakage?
- How do you communicate and enforce a deprecation timeline?
- How does GraphQL handle evolution differently from REST versioning?
MCQ Practice
1. Which change is backward compatible?
Adding an optional field is additive; tolerant readers ignore it, so existing consumers keep working.
2. What is the safest way to ship a breaking API change?
Publishing a new explicit version alongside the old lets consumers migrate on their own schedule without breakage.
3. What do consumer-driven contract tests achieve?
Tools like Pact verify the provider still satisfies each consumer's expected contract, flagging breaks in CI.
Flash Cards
What is a backward-compatible change? — An additive change (new optional field or endpoint) that does not break existing consumers.
Name three API versioning schemes. — URL path (/v2), custom header, and media-type (content negotiation) versioning.
What is the tolerant-reader principle? — Clients ignore unknown fields so producers can extend responses safely.
How do you retire an old API version? — Announce a deprecation timeline, monitor usage, and remove it only after consumers migrate.
Continue Learning
Related Interview Questions
How do you evolve an event schema without breaking downstream consumers?
hard
How do you test microservices (unit, contract, integration, end-to-end)?
medium
What is contract testing and how does consumer-driven contract testing work?
medium
Several services share a contract through a common library. How do you evolve that contract when the services deploy independently?
hard