How do you version an API?
“You need to change a public API in a way that breaks existing clients. How do you version it, how long do you support old versions, and how do you avoid needing a new version at all?”
What this tests
- Versioning mechanisms: URL, header, media type — and their tradeoffs
- Backward-compatible evolution as the primary strategy
- Deprecation as a process with telemetry and dates
- Difference between public and internal APIs
Answers by level
Read the beginner answer first and notice what is missing.
The first strategy is not needing a version: additive changes only. Add fields, never remove or rename; add optional parameters; add new endpoints; make readers tolerant of unknown fields. Most changes can be expressed that way, and the "breaking change" often turns out to be a new field with a sensible default. Breaking changes are removals, type changes, semantic changes to an existing field, and stricter validation.
When a break is unavoidable: URL versioning (/v2/) is visible, cacheable and easy to route, at the cost of implying the whole API changed. Header or media-type versioning keeps URLs stable and allows per-resource versions but is invisible in logs and harder for consumers to discover. For most public APIs the URL version wins on clarity. Internally, a version is a smell — coordinate the deploy or use the additive path.
Support is a process: emit deprecation headers with a sunset date, track usage per version per client from gateway logs, contact the clients still on the old version, and remove only when usage is zero or the date has passed. Twelve months is common for public APIs; keeping every old version forever is a maintenance cost that compounds.
Green flags · Red flags
- Leads with additive, backward-compatible evolution
- Names what counts as breaking (removal, type or semantic change, stricter validation)
- Compares URL vs header versioning honestly
- Deprecation with sunset headers, per-client usage telemetry, and dates
- Treats internal versioning differently from public
- "Just bump to v2 whenever something changes."
- Repurposes an existing field with new semantics
- No telemetry on who uses the old version
- Versions every internal service API like a public one
Follow-up questions
amount from an integer of cents to a decimal string. How?Scenario
status: "shipped" and the business now distinguishes "shipped" from "delivered". Changing the meaning of "shipped" would break partner integrations that trigger invoicing on it. Design the change without a v2, and describe the deprecation path if one is later required.