API Versioning
Strategies for versioning APIs, managing breaking changes, and deprecating old versions gracefully.
When to Use This Skill
-
Choosing an API versioning strategy
-
Planning for breaking changes
-
Deprecating API versions
-
Managing multiple API versions
-
Designing for API evolution
Why Version APIs?
APIs are contracts with clients. Breaking changes break clients.
Without versioning:
- Change field name → All clients break
- Remove endpoint → All clients break
- Change behavior → Unexpected client behavior
With versioning:
- Old clients use old version
- New clients use new version
- Gradual migration possible
Versioning Strategies
URL Path Versioning
https://api.example.com/v1/users https://api.example.com/v2/users
Pros:
- Clear and explicit
- Easy to understand
- Easy to route
- Easy to cache
Cons:
- Version embedded in client code
- Multiple URLs for same resource
- Not truly RESTful (URL should identify resource)
Header Versioning
GET /users Accept: application/vnd.example.v1+json
or custom header: GET /users API-Version: 1
Pros:
- Clean URLs
- More RESTful
- Version separate from resource
Cons:
- Hidden from URL
- Harder to test in browser
- Requires header support
Query Parameter Versioning
GET /users?version=1 GET /users?api-version=2023-01-01
Pros:
- Easy to add
- Optional (can default)
- Easy to test
Cons:
- Can be forgotten
- Pollutes query string
- Caching complexity
Content Negotiation
Accept: application/vnd.example+json; version=1
Pros:
- Standard HTTP mechanism
- Flexible
Cons:
- Complex to implement
- Hard to discover
Strategy Comparison
Strategy Visibility Implementation Caching Recommendation
URL Path High Easy Easy Best for public APIs
Header Low Medium Medium Good for internal APIs
Query Param Medium Easy Complex Good for simple cases
Content Neg Low Complex Medium Rarely used
Versioning Schemes
Integer Versions
v1, v2, v3
Pros: Simple, clear major changes Cons: Coarse-grained
Best for: Public APIs with infrequent breaking changes
Semantic Versioning
v1.2.3 (major.minor.patch)
Major: Breaking changes Minor: New features (backward compatible) Patch: Bug fixes
Pros: Fine-grained, predictable Cons: More complex
Best for: Libraries, SDKs
Date-Based Versioning
2023-01-15, 2023-06-01
Pros: Clear when version was current Cons: Doesn't indicate change magnitude
Best for: Frequently changing APIs (Stripe, GitHub)
Example (Stripe): Stripe-Version: 2023-10-16
What Requires a New Version?
Breaking Changes (New Major Version)
Always breaking:
- Removing endpoint
- Removing field
- Changing field type
- Changing field meaning
- Renaming field
- Adding required field
- Changing authentication
- Changing error format
Non-Breaking Changes (No Version Needed)
Safe changes:
- Adding new endpoint
- Adding optional field
- Adding new enum value
- Adding optional parameter
- Relaxing validation
- Adding new error codes
Version Management
Running Multiple Versions
Option 1: Separate codebases /v1/* → v1 service /v2/* → v2 service
Pros: Full isolation Cons: Duplication, maintenance burden
Option 2: Shared codebase with branching if (version == 1) { return formatV1(data); } else { return formatV2(data); }
Pros: Single codebase Cons: Code complexity grows
Option 3: Transformation layer Internal model → Version-specific transformer → Response
Pros: Clean separation Cons: Requires transformation code
Version Routing
API Gateway pattern:
Client → Gateway → Route by version → Service
Gateway responsibilities:
- Parse version from URL/header
- Route to appropriate backend
- Transform if needed
- Handle defaults
Deprecation Strategy
Lifecycle Phases
- Current: Active development
- Maintained: Bug fixes only
- Deprecated: No changes, sunset announced
- Sunset: Removed
Timeline example: v1: Current (12 months) v1: Maintained when v2 launches (6 months) v1: Deprecated (6 months) v1: Sunset
Deprecation Communication
Headers: Deprecation: true Sunset: Sat, 1 Jul 2024 00:00:00 GMT Link: <https://api.example.com/v2/docs>; rel="successor-version"
Response body: { "data": {...}, "_deprecation": { "message": "This API version is deprecated", "sunset": "2024-07-01", "successor": "https://api.example.com/v2" } }
Migration Support
Provide:
- Migration guide documenting all changes
- Mapping of old → new endpoints
- Code examples for common operations
- SDK updates with compatibility layer
- Sandbox environment for testing
Best Practices
Default Version
Options:
- Require explicit version (recommended for public APIs)
- Default to latest (dangerous for stability)
- Default to oldest supported (conservative)
Recommendation: Require version, fail without it
Version in Response
Include version info in responses:
{ "data": {...}, "_meta": { "api_version": "v2", "deprecated": false } }
Graceful Degradation
When version unknown:
- Return error with supported versions
- Redirect to documentation
- Return latest version with warning
HTTP 400 Bad Request { "error": "Unknown API version", "supported_versions": ["v1", "v2"], "documentation": "https://docs.example.com/api" }
Testing Multiple Versions
Test matrix:
- All supported versions
- Breaking change boundaries
- Deprecation warnings
- Sunset behavior
Automated tests:
- Contract tests per version
- Backward compatibility tests
- Migration path tests
Real-World Examples
Stripe
Date-based: 2023-10-16 Header: Stripe-Version Default: Account's API version Rollback: Can pin to older version Upgrades: Preview in dashboard
GitHub
Date-based: 2022-11-28 Header: X-GitHub-Api-Version Default: Latest Preview features: Accept header
URL path: /v1/, /v2/ Discovery document for each version Long deprecation cycles (years)
Twilio
Date-based: 2010-04-01 URL path includes date Very long support windows
Anti-Patterns
-
Too many versions → Consolidate, set deprecation schedule
-
Breaking changes in minor versions → Follow semantic versioning strictly
-
No deprecation warnings → Always communicate before breaking
-
Instant sunset → Give clients time to migrate (6-12 months minimum)
-
Version per endpoint → Keep all endpoints in sync per version
Related Skills
-
api-design-fundamentals
-
API design patterns
-
idempotency-patterns
-
Safe API operations
-
quality-attributes-taxonomy
-
Maintainability attributes