When is introducing a new version of an HTTP API contract the right answer, and what would you do instead? How many versions would you support at once?
answer
- a version is a migration imposed on everyone
- ladder: add field → add resource → new representation → opt-in flag
- version when the model itself is wrong
- N+1 versions = permanent carrying cost
- publish the support policy before v1 ships
basics
~20 sVersion only when a change cannot be made additively and the model genuinely must change. Prefer new fields, new endpoints or new representations. Support as few concurrent versions as the customer base tolerates — commonly two — and publish that policy up front.
solid answer
~50 sA new version is a **migration imposed on every consumer**, so it should be the last resort. Cheaper alternatives, in order: add a field or an endpoint alongside the old one; introduce a new resource that models the new concept properly and let the old one keep working; use a new representation via content negotiation for one endpoint rather than bumping the whole API; hide the change behind an opt-in parameter or flag. Version when the underlying model is genuinely wrong — the change is semantic, not additive; when compatibility shims have accumulated to the point of blocking development; or when a security or correctness fix requires different behaviour. On concurrency: each supported version is permanent carrying cost — code, tests, docs, support, security patching. Two concurrent versions (current plus previous) is the common ceiling; date-pinned models trade a bigger matrix for cheap per-change transformations. Publish the policy before the first version ships, so "how long do I have?" is answered in the docs, not by negotiation.
go deeper
Know that a new version should be rare and that adding fields or endpoints is usually preferable.
Give the ladder of alternatives and name the situations that genuinely justify a version.
Argue from carrying cost and consumer migration cost, and set a concrete concurrent-version policy with sunset rules.
Own it as governance: who may declare a break, how compatibility is machine-enforced, the published support window, and the budget line that triggers a sunset.
## Versioning is a cost transfer A new version does not make change free; it moves the cost to consumers, who must retest and redeploy code that was working. Multiply by every integration you have. That framing changes the default answer from "bump the version" to "what would let us avoid this?" ## The ladder of cheaper options **Add rather than change.** New field alongside the old; dual-write both; migrate consumers with telemetry; retire the old field on its own deprecation schedule. Ugly in the schema, invisible to callers. **New endpoint or resource.** If the model is wrong, model it correctly next to the old one — `/payment_intents` alongside `/charges` — rather than redefining an existing resource under a new prefix. Consumers move when their needs justify it, which is the migration you actually want. **New representation.** Content negotiation lets one endpoint offer an old and a new shape without touching URIs or any other endpoint. Scope of change: one resource. **Opt-in behaviour.** A documented parameter or flag that enables the new behaviour, defaulted off, with a plan to flip the default later after measuring. Good for behavioural changes such as defaults, ordering or pagination. **Fix the client instead.** Sometimes the break is confined to a handful of internal consumers who can be updated in the same change window. Coordinating three teams for a week is far cheaper than carrying a version for three years. ## When a version is genuinely right - The **domain model changed** — resources, identity or relationships are wrong, and papering over it produces a schema no one can reason about. - **Compatibility debt is blocking** — enough shims, dual-written fields and conditional branches have accumulated that feature work has slowed measurably. A clean break here buys back velocity. - **Correctness or security demands different behaviour** — the old semantics are unsafe and cannot remain available. - **Cross-cutting change** — auth model, error envelope, pagination style changing everywhere at once; per-endpoint tactics do not scale to that. The honest signal is the *ratio*: if the new version differs on 5% of endpoints, you should not have versioned; if it differs on most, versioning is the right container for the change. ## How many at once Every supported version is code paths, test suites, documentation sets, support knowledge, dependency upgrades and security patches — paid every month, forever, by the provider. Common policies: - **Two** (current plus previous, with a defined sunset for the previous when a new one ships). Simple, predictable, the usual public-API answer. - **One**, with pure additive evolution and no numbered versions at all. Only viable with a strong tolerant-reader contract and consumers you can influence. - **Many, cheaply** — the date-pinned model, where the matrix is large but each increment is one small transformation rather than a whole parallel implementation. What matters more than the number is **publishing it before the first version ships**, together with the minimum support window in months or years. If the policy arrives only when you want to switch something off, every shutdown becomes a negotiation with your largest customer. ## Organisational reality Decide who may declare a breaking change and who must approve it; make additive-vs-breaking a machine-checked property in CI so the decision is deliberate rather than accidental; and track carrying cost explicitly — engineer-time spent on old versions is a budget line, and when it exceeds the cost of a funded migration programme, that is your signal to sunset.
- What is the signal that you should have avoided versioning and made an additive change instead?Look at how much of the surface actually differs. If v2 is identical to v1 for the large majority of endpoints, you forced every consumer to migrate for a change affecting a few — a new field, a new resource or a negotiated representation would have delivered the same value at a fraction of the cost. Consistently low deltas mean the versioning unit is too coarse.
- Is running an API with no numbered versions at all realistic?Yes, if the compatibility contract is strict and published: only additive changes, clients must be tolerant readers, and breaking changes appear as new resources rather than redefinitions. Large internal platforms and some public APIs run this way. It demands strong CI enforcement and the discipline to model new concepts as new resources instead of quietly redefining old ones.
saying these in an interview costs you the question
- Bumping the major version for any change, including additive ones
- Assuming supporting old versions is free because the code still compiles
- Publishing the deprecation and support policy only when the first shutdown is needed
- Treating a version bump as a chance to rewrite everything, maximising consumer migration cost
- Keeping every version alive forever and calling that good customer service while velocity collapses