API Versioning Strategies — URL, Header & Query Tradeoffs
Should we put /v1 in the path is a classic interview trap. Prefer additive, non-breaking evolution; version only when you must break contracts; then compare URL vs media-type/header vs query (api-version=) with Sunset and a migration window.
- 1Gist
- 2Maps
- 3Q&A
- 4Sandbox
Voice readout needs Web Speech Synthesis in this browser.
How you evolve a public contract
Prefer
Additive first; version only on breaks; then pick a strategy for the audience
New optional fields and endpoints ship under the current version. Removals, type changes, and status-meaning shifts get a new version run in parallel until Sunset.
- Path /vN is discoverable for browsers and bookmarks.
- Dated header or query (Stripe / Azure) keeps URLs stable.
- Never silent-break a field meaning under the same version.
Alternative
Ship /v2 because we might break later, or default undated traffic to latest
YAGNI majors. Clients hard-code forever. Dropping a version while enterprise traffic is still above 1% is how you page on a Friday.
- Optional-field-only /v2 is a wasted major.
- Path + header versions that disagree confuse gateways.
- Lockstep internal APIs do not need public /v1 theater.
Change, then lifecycle
Version is a compatibility tool, not a release stamp.
- 1
Classify the change
Additive (optional field, new endpoint) vs breaking (remove, rename, type, status meaning, tighter auth). - 2
Ship or fork
Additive → no new version. Breaking → URL, header, or dated query. - 3
Run parallel
Advertise both. Deprecation + Sunset headers. Migration guide. - 4
Retire on metrics
Drain old version. Keep enterprise windows. Then remove.
Overview
"Should we put /v1 in the path?" is a classic interview trap. The strong answer is: prefer additive, non-breaking evolution; version only when you must break contracts; then compare URL vs media-type/header vs query with clear deprecation.
Three common strategies
- URL path:
/v1/orders— visible, cache-friendly, easy in gateways; proliferates routes and docs. - Header / media type:
Accept: application/vnd.myapi.v2+jsonorApi-Version: 2026-01-15— clean paths; harder to explore in browsers; CDN/cache variance (Vary). - Query (Azure-style):
/orders?api-version=2024-06-01— explicit per request; can break naive caches if not careful; good for dated versions.
| Strategy | Pros | Cons |
|---|---|---|
/v1 | Obvious, bookmarks work | Duplicates surface, clients hard-code forever |
| Header / media type | Single path aesthetic | Discoverability, intermediary surprises |
| Dated query/header | Fine-grained (Stripe/Azure) | Matrix of behaviors to test; document defaults |
Breaking vs additive
Additive (usually no new major version): new optional fields, new endpoints, new optional query params, new enum values with documented unknown handling.
Breaking: remove/rename fields, change types, change auth requirements, change status-code meaning, make optional fields required.
Rule: default to additive; version when old clients cannot safely ignore the change.
Compatibility and deprecation
- Publish a compatibility promise (Stripe-style dated versions or semver majors).
- Use Deprecation / Sunset headers (RFC 8594) and docs changelogs.
- Keep old versions long enough for enterprise upgrade cycles; provide migration guides.
- Never silent-break a field meaning under the same version.
When not to version
- Internal-only APIs with lockstep deploys.
- Purely additive public changes.
- Fixing a clear bug that all clients already mishandle — still communicate.
- Over-versioning every weekly tweak — version fatigue is real.
Flow
- 1
1 Additive change?
- yes2 Ship no new version
- no3 Breaking?
- 2
2 Ship no new version
- 3
3 Breaking?
- yes4 Pick URL header query
- 4
4 Pick URL header query
- next5 Deprecation + Sunset
- 5
5 Deprecation + Sunset
- next6 Retire after drain
- 6
6 Retire after drain
Lesson map
API Versioning Strategies — URL, Header & Query Tradeoffs
Should we put /v1 in the path is a classic interview trap. Prefer additive, non-breaking evolution; version only when you must break contracts; then compare URL vs media-type/header vs query (api-version=) with Sunset and a migration window.
Architecture. Architecture
Select a node to see why it exists, or an edge to see the protocol, direction, effect, and consequence.
Mermaid export
flowchart TB a["1 Additive change?"] n["2 Ship no new version"] b["3 Breaking?"] v["4 Pick URL header query"] a -->|yes| n a -->|no| b b -->|yes| v
Sandbox: classify change (Python)
Press Run. Snippets must be self-contained — no network, files, or native modules.
Deprecation window helper (TypeScript)
Press Run. Snippets must be self-contained — no network, files, or native modules.
Pitfalls
Is that additive? What do old mobile clients deserialize? Do you dual-write both fields for a window, or cut a dated version? Write the Sunset date you would put in the header.
Interview Q&A
/v1 or header?
Answer
Both valid. Path is discoverable; header/dated keeps URLs stable. Defend with audience (public SDKs vs browsers vs generated Azure-style clients).
What is a breaking change?
Answer
Anything a well-behaved old client cannot ignore — removals, type changes, tighter auth, semantic status shifts, optional→required.
How to deprecate?
Answer
Parallel run, Deprecation/Sunset headers (RFC 8594), changelog, metrics, then retire. Keep enterprise upgrade windows.
Should every microservice share one version?
Answer
Prefer per-API versioning. Avoid a monorepo-wide /v27 big bang that couples unrelated contracts.
Relation to idempotency?
Answer
Version the contract of retry headers/bodies carefully. Pointer to api-idempotency-keys if the retry surface changes — do not re-teach keys here.
Azure api-version query?
Answer
Dated ?api-version=YYYY-MM-DD on every operation, including nextLink. Cache keys must include the query. Missing version is often 400, not "latest".
Stripe dated versions?
Answer
Account or request pins a version. Additive fields appear; breaking behavior is forked. Clients upgrade when ready. See Stripe's public versioning docs.
New enum value?
Answer
Additive if clients are told to treat unknown values as opaque. Breaking if old clients crash on an unexpected string.
Internal lockstep APIs?
Answer
Often no public version. A header for debugging is enough. Do not cargo-cult /v1 for two services you deploy together.
Fix a bug that all clients mishandle?
Answer
Still communicate. You may ship under the same version if no well-behaved client depended on the bug — say that out loud.