Backend Development5 min read

API Versioning & Evolution

Design APIs that evolve without breaking clients: URL versioning, header versioning, additive changes, deprecation notices, and sunset policies.

apiversioningbackward-compatibilityrest

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

  1. Choose a versioning strategyDifferent strategies have different trade-offs for visibility, caching, and client complexity.
  2. Make backward-compatible changesBreaking changes force all clients to update simultaneously - avoid them in favor of additive changes.
  3. Deprecate with Sunset header and noticesSilent deprecation strands clients on old versions; explicit deprecation with dates allows planned migration.
  4. Maintain multiple active versionsNot all clients can migrate immediately; supporting 2-3 versions simultaneously is standard practice.
  5. 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 it
URL routing: `app.use("/v1", v1Router); app.use("/v2", v2Router); app.use("/", (req, res) => res.redirect("/v2" + req.path))` - default redirects to latest

Edge 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/rfc8594
  • Stripe API versioning: https://stripe.com/blog/api-versioning
  • Zalando API guidelines: https://opensource.zalando.com/restful-api-guidelines/
  • Semver for APIs: https://semver.org/

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.