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¶
- Explicit URI Versioning vs Clean Resource Semantics
- Gain (URI): Crystal clear; developers and CDNs instantly know which version is requested.
- Lose (URI): Violates strict REST purist ideology (URI represents resource, not representation version).
- CDN Cacheability vs Header Versioning
- Gain (Header): Clean URLs (
/users/123). - Lose (Header): CDNs ignore headers by default; requires configuring CDN
Vary: X-API-Versionor cache keys. - Major Version Branching vs Non-Breaking Schema Evolution
- Gain (Evolution): Zero duplicate controller code; single evolving API codebase.
- Lose (Evolution): Accumulates deprecated fields over time (
@deprecated). - Coarse-Grained (Global v1/v2) vs Fine-Grained (Date-Based) Versioning
- Gain (Date-Based e.g. Stripe): Clients lock to exact changelog release dates (
2026-08-04). - Lose (Date-Based): Requires complex server-side middleware transformation pipelines.
- Sunset Policies & Deprecation Windows
- Gain: Forces mobile/third-party developers to upgrade before old endpoints are deleted.
- Lose: Managing multi-year deprecation windows and legacy code maintenance.
6. Interview Counter Questions¶
- What constitutes a "Breaking Change" versus a "Non-Breaking Change" in an API contract?
- How does Stripe implement Date-Based API Versioning and Response Transformations?
- How does the HTTP
Sunsetheader (RFC 8594) work for API deprecation? - Why is URI Path Versioning preferred over Header Versioning for public CDN-cached APIs?
- How do you implement Non-Breaking Schema Evolution in gRPC Protocol Buffers?
- How does GraphQL handle versioning without version numbers (
@deprecateddirective)? - How do you handle database schema migrations when supporting both
v1andv2APIs simultaneously? - How does an API Gateway handle version routing and traffic splitting (e.g. 90% v1, 10% v2)?
- What is the risk of using default fallback versions when a client omits version headers?
- How long should an engineering team maintain a deprecated API version for mobile clients?
7. Gold Standard Answers¶
- 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.
- 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. Sunset: Wed, 11 Nov 2026 00:00:00 GMTHTTP response header explicitly communicates the exact date/time an API endpoint will be permanently decommissioned, allowing automated developer alerts.- CDN edge caches key on the HTTP request URI path by default. URI path versioning (
/v1/products) ensures CDNs cache distinct versions cleanly without customVaryheader configuration. - 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. - 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. - Use non-breaking DB migrations: Add new columns as nullable or with defaults. Never delete old columns until all active API versions (
v1andv2) referencing them are completely decommissioned. - API Gateway matches URL prefixes (
/v1/*\(\rightarrow\) Service V1;/v2/*\(\rightarrow\) Service V2) or readsX-API-Versionheader to route traffic to corresponding Kubernetes service subsets. - 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 tov1or return400 Bad Request. - 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
latestversion, breaking legacy mobile clients on deployment. - Using Header Versioning behind a CDN without setting
Varyheaders or custom cache keys, servingv1responses tov2requests. - Deleting database columns used by
v1APIs immediately after releasingv2. - 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
v1andv2APIs are active.