Every API ages. Fields get added, semantics shift, names change. The question is not whether to change but how to do so without making your customers' day worse than yesterday's.
Additive vs Breaking Changes
| Additive (safe) | Breaking (need a version) |
|---|---|
| New endpoint | Removing an endpoint |
| New optional field in request | Making an existing field required |
| New field in response | Removing or renaming a field |
| New optional query param | Changing default behaviour |
| New status code in error response (when documented as possible) | Changing a status code's meaning |
Most evolution is additive. Save versions for the rare moments where breaking change is unavoidable.
Versioning Strategies
1. URL versioning
GET /v1/orders
GET /v2/orders
- + Visible, simple to test, easy to route.
- + Browsers, curl, and humans handle it without thought.
- − Implies "v1" and "v2" are different products even when they overlap heavily.
The most common choice. Stripe, GitHub, Twitter use variants of this.
2. Header versioning
GET /orders
Accept: application/vnd.api+json; version=2
- + URL stays canonical.
- + Cleaner caching boundaries.
- − Harder to test in a browser; tooling matters more.
3. Date versioning
GET /orders
Stripe-Version: 2024-09-30
- + Each customer pins to a date; new clients pick the latest by default.
- + No breaking changes silently arrive.
- − Server must maintain logic for many versions simultaneously.
Stripe popularised this; great for billing-grade stability.
4. Don't version (GraphQL)
GraphQL APIs typically deprecate fields rather than version the whole schema:
type Order {
total: Int! @deprecated(reason: "Use totalCents")
totalCents: Int!
}
Clients keep working; new ones use the new field; the old field is removed only after adoption metrics fall to near zero.
Picking One
For new APIs: URL versioning is the safest default unless you have a specific reason. Pick /v1, document it, then strain to never need a /v2.
Deprecation Policy
What you publish matters more than the mechanism. A good policy includes:
- How long deprecated features remain (commonly 6–12 months for paid APIs).
- How customers are notified — email, dashboard, response headers, changelog.
- Migration documentation with concrete diffs.
- Optional: a developer dashboard showing which deprecated calls a customer still makes.
Telling Clients Now
Two standard headers:
Deprecation: Sat, 01 Jun 2025 00:00:00 GMT
Sunset: Sat, 01 Dec 2025 00:00:00 GMT
Link: <https://docs.example.com/migrate-v2>; rel="deprecation"
- Deprecation — the date the feature was deprecated (or true if simply deprecated).
- Sunset — the date the feature will be removed.
- Link — pointer to migration docs.
Good SDKs surface these as warnings; great ones tag the call site.
Tracking Adoption
Before you remove anything, you need data:
- Per-customer usage of each deprecated path.
- Trend over time (going to zero or stuck?).
- Top-N customers still on the old version.
Reach out to those customers individually before sunset day. Big customers, in particular, deserve a phone call. Don't surprise people with 410 Gone.
What Removal Looks Like
The day after sunset:
GET /v1/orders
→ 410 Gone
{
"error": {
"type": "version_removed",
"message": "v1 was sunset on 2025-12-01. See https://docs.example.com/migrate-v2"
}
}
410 Gone is the right code — it tells caches and clients the resource is permanently gone, not temporarily down.
Internal vs Public APIs
- Public APIs — slow, deliberate, with policy and notice.
- Internal APIs — can move faster if all callers are known. Use the same patterns at smaller scale to keep good habits.
The Hardest Truth
The best version is the one you never had to ship. Spend the design effort up front: model resources well, name fields generously, allow extension via input objects rather than positional arguments, default to optional fields. Most APIs that need v2 within a year did so because v1 was rushed.
Cert Mapping
| Cert | Scope |
|---|---|
| AWS SAA | API Gateway stages, deployments, custom domains |
| AWS Data Engineer | Schema evolution in data APIs |
The final lesson covers what makes the difference between an API people tolerate and one they recommend: documentation and developer experience.