Skip to content

API Versioning

1. Decision

Choose URI Path Versioning (/api/v1/users) as the primary strategy for public and mobile APIs due to clear visibility, caching compatibility, and operational simplicity; choose Header Versioning (Accept / custom header) for enterprise REST APIs requiring clean URI resource semantics; choose Non-Breaking Schema Evolution (GraphQL / Protobuf) for internal microservices to avoid version branch sprawl.


2. 30-Second Answer

API Versioning manages breaking changes (removing fields, changing data types, altering endpoint behavior) across software release lifecycles without disrupting existing mobile or third-party client integrations. The four main REST versioning strategies are: URI Path (/v1/users - explicit, CDN-friendly, standard default), Query Parameter (/users?version=1 - simple optional override), Custom Header (X-API-Version: 1 - clean URIs, but opaque), and Content Negotiation / Media Type (Accept: application/vnd.company.v1+json - pure REST architectural design). For internal gRPC microservices and GraphQL, favor Non-Breaking Schema Evolution (adding new fields, marking legacy fields as @deprecated) over explicit API version numbers.


3. Decision Matrix

Option Best For Avoid When Latency Scalability Consistency Operational Complexity
URI Path Versioning (/v1/users) Public APIs, mobile backends, third-party integrations, CDN cached endpoints Strict REST purists desiring permanent static resource URIs Ultra-Low (Fast regex routing at Gateway/Proxy) High (Separate routes/controllers scale cleanly) Explicit version in URL Low
Query Parameter (/users?v=1) Optional version overrides, quick developer testing, default fallback Production APIs with heavy CDN caching dependencies Low Medium Explicit query parameter Low
Header Versioning (X-API-Version: 1) Enterprise internal APIs, clean URI semantics Heavy CDN edge caching requirements (Un-keyed headers break CDN caches) Low Medium Clean resource URLs; Header dependent Medium
Media Type / Accept Header (application/vnd.v1+json) Pure HATEOAS / REST architectural purists Public developer APIs where cURL testing and simplicity dominate Low Medium Strict REST standards High (Complex content negotiation parsing)
Non-Breaking Schema Evolution (GraphQL/Protobuf) Internal microservices (gRPC), GraphQL APIs, mobile apps with dynamic fields Fundamental breaking structural domain model rewrites Ultra-Low High (Zero version branch fragmentation) Single evolving schema contract Low to Medium

4. Selection Rules

Requirement Best Choice Rationale / Example
Public Developer API & Mobile Backends URI Path Versioning /api/v1/products is explicit, self-documenting, and CDN edge cacheable
Internal Microservices (gRPC / Protobuf) Non-Breaking Field Evolution Add new field tag string email = 3;; retain old tags for backward compatibility
Clean Resource URIs for Enterprise REST Custom Header X-API-Version: 2026-08-01 allows static /users/123 URIs
Deprecating Legacy Mobile App Versions Sunset Header + Gateway Routing Return Sunset: Wed, 11 Nov 2026 header; block v1 traffic at API Gateway when dead

5. Tradeoffs

  1. Explicit URI Versioning vs Clean Resource Semantics
  2. Gain (URI): Crystal clear; developers and CDNs instantly know which version is requested.
  3. Lose (URI): Violates strict REST purist ideology (URI represents resource, not representation version).
  4. CDN Cacheability vs Header Versioning
  5. Gain (Header): Clean URLs (/users/123).
  6. Lose (Header): CDNs ignore headers by default; requires configuring CDN Vary: X-API-Version or cache keys.
  7. Major Version Branching vs Non-Breaking Schema Evolution
  8. Gain (Evolution): Zero duplicate controller code; single evolving API codebase.
  9. Lose (Evolution): Accumulates deprecated fields over time (@deprecated).
  10. Coarse-Grained (Global v1/v2) vs Fine-Grained (Date-Based) Versioning
  11. Gain (Date-Based e.g. Stripe): Clients lock to exact changelog release dates (2026-08-04).
  12. Lose (Date-Based): Requires complex server-side middleware transformation pipelines.
  13. Sunset Policies & Deprecation Windows
  14. Gain: Forces mobile/third-party developers to upgrade before old endpoints are deleted.
  15. Lose: Managing multi-year deprecation windows and legacy code maintenance.

6. Interview Counter Questions

  1. What constitutes a "Breaking Change" versus a "Non-Breaking Change" in an API contract?
  2. How does Stripe implement Date-Based API Versioning and Response Transformations?
  3. How does the HTTP Sunset header (RFC 8594) work for API deprecation?
  4. Why is URI Path Versioning preferred over Header Versioning for public CDN-cached APIs?
  5. How do you implement Non-Breaking Schema Evolution in gRPC Protocol Buffers?
  6. How does GraphQL handle versioning without version numbers (@deprecated directive)?
  7. How do you handle database schema migrations when supporting both v1 and v2 APIs simultaneously?
  8. How does an API Gateway handle version routing and traffic splitting (e.g. 90% v1, 10% v2)?
  9. What is the risk of using default fallback versions when a client omits version headers?
  10. How long should an engineering team maintain a deprecated API version for mobile clients?

7. Gold Standard Answers

  1. Non-Breaking: Adding a new optional field, adding a new endpoint. Breaking: Removing a field, renaming a field, changing data type (integer \(\rightarrow\) string), changing response status code, altering validation rules.
  2. Stripe uses Date-Based versioning (Stripe-Version: 2026-08-04). The backend code always writes to the latest data model; a chain of backward-compatibility transformation middleware rewrites the payload to match the client's requested date.
  3. Sunset: Wed, 11 Nov 2026 00:00:00 GMT HTTP response header explicitly communicates the exact date/time an API endpoint will be permanently decommissioned, allowing automated developer alerts.
  4. CDN edge caches key on the HTTP request URI path by default. URI path versioning (/v1/products) ensures CDNs cache distinct versions cleanly without custom Vary header configuration.
  5. Never change existing tag numbers (string name = 1;). Add new fields with new tag numbers. Mark deprecated fields as [deprecated = true]. Old clients ignore new tags; new clients receive default values for missing tags.
  6. GraphQL avoids version numbers entirely. Add new fields to the schema; mark outdated fields using @deprecated(reason: "Use newField instead"). Introspection tools and Apollo Client alert developers automatically.
  7. Use non-breaking DB migrations: Add new columns as nullable or with defaults. Never delete old columns until all active API versions (v1 and v2) referencing them are completely decommissioned.
  8. API Gateway matches URL prefixes (/v1/* \(\rightarrow\) Service V1; /v2/* \(\rightarrow\) Service V2) or reads X-API-Version header to route traffic to corresponding Kubernetes service subsets.
  9. If un-versioned requests default to latest, any new API deployment with breaking changes will silently crash all legacy clients that omitted the version parameter! Default to v1 or return 400 Bad Request.
  10. Mobile apps require 12-24 months because users delay updating native apps. Public third-party APIs require 1-3 years with active Sunset headers and email outreach before hard retirement.

8. Common Mistakes

  • Making breaking changes (renaming/deleting fields) without incrementing major API version numbers.
  • Defaulting un-versioned API requests to the latest version, breaking legacy mobile clients on deployment.
  • Using Header Versioning behind a CDN without setting Vary headers or custom cache keys, serving v1 responses to v2 requests.
  • Deleting database columns used by v1 APIs immediately after releasing v2.
  • Maintaining 5+ major API versions indefinitely without enforcing Sunset deprecation deadlines.

9. Keywords

API Versioning, URI Path Versioning, Query Parameter Versioning, Header Versioning, Media Type, HATEOAS, Breaking Changes, Non-Breaking Changes, Stripe Versioning, Sunset Header (RFC 8594), gRPC Protobuf Evolution, GraphQL @deprecated, Vary Header, API Gateway Traffic Splitting.


10. Cheat Sheet

  • Public & Mobile API Standard: Use URI Path Versioning (/api/v1/users). Simple, explicit, CDN cache-friendly.
  • Internal gRPC & GraphQL: Use Non-Breaking Schema Evolution (Add new fields/tags; use @deprecated; never delete/rename tags).
  • Breaking Changes: Removing fields, renaming fields, changing data types (int \(\rightarrow\) string). Increment major version (v1 \(\rightarrow\) v2).
  • Non-Breaking Changes: Adding new optional fields or new endpoints. No version increment needed!
  • Deprecation Standard: Return HTTP header Sunset: <Date> to notify clients of upcoming endpoint retirement dates.
  • DB Migrations: Keep old database columns intact while both v1 and v2 APIs are active.

11. Related Decisions