API Versioning & Evolution
Design APIs that evolve without breaking clients: URL versioning, header versioning, additive changes, deprecation notices, and sunset policies.
API versioning manages the inevitable tension between evolving your API and not breaking existing clients. The best strategy is to design for evolution: make backward-compatible changes, deprecate rather than delete, and give clients sufficient notice to migrate.
Prefer additive changes over breaking changes. Use URL versioning (/v1/, /v2/) for REST APIs - it's explicit and cacheable. Provide deprecation notices months in advance. Never silently remove fields that clients depend on.
Workflow
- Choose a versioning strategyDifferent strategies have different trade-offs for visibility, caching, and client complexity.
- Make backward-compatible changesBreaking changes force all clients to update simultaneously - avoid them in favor of additive changes.
- Deprecate with Sunset header and noticesSilent deprecation strands clients on old versions; explicit deprecation with dates allows planned migration.
- Maintain multiple active versionsNot all clients can migrate immediately; supporting 2-3 versions simultaneously is standard practice.
- Document and communicate version lifecycleUndocumented lifecycle creates uncertainty - clients don't know when to migrate.
Output Format
URL-versioned API endpoints, additive changes only without deprecation, Sunset headers on deprecated endpoints, migration guides, version lifecycle document.
Examples
Sunset header: `res.set({ "Deprecation": "true", "Sunset": "Mon, 01 Jun 2027 00:00:00 GMT", "Link": '<https://api.example.com/docs/migration>; rel="deprecation"' })`Non-breaking addition: add `{ "user": { "id": "123", "name": "Alice", "avatarUrl": null } }` - adding nullable `avatarUrl` is non-breaking; clients that don't know about it ignore itURL routing: `app.use("/v1", v1Router); app.use("/v2", v2Router); app.use("/", (req, res) => res.redirect("/v2" + req.path))` - default redirects to latestEdge Cases & Gotchas
- Mobile clients: mobile app updates lag server API by months; mobile APIs need longer deprecation windows (18-24 months) than web clients
- GraphQL versioning: GraphQL doesn't use URL versioning; use `@deprecated` on fields and additive schema evolution - avoid versioning the schema endpoint itself
- Webhook versioning: webhook payloads are pushed (not pulled) - clients can't request a version; include version in payload and document migration clearly
When to Break These Rules
- For internal APIs with a single consumer team, informal coordination and joint migration is faster than maintaining multiple versions. Formal versioning is for public APIs with independent clients.
Reference Files
RFC 8594 Sunset header: https://datatracker.ietf.org/doc/html/rfc8594Stripe API versioning: https://stripe.com/blog/api-versioningZalando API guidelines: https://opensource.zalando.com/restful-api-guidelines/Semver for APIs: https://semver.org/