8.1 KiB
API Versioning Strategies
Why Version APIs?
API versioning allows you to evolve your API while maintaining backward compatibility for existing clients. Breaking changes require a new version.
Breaking Changes
Changes that require a new version:
- Removing or renaming fields
- Changing field types (string to integer)
- Adding required fields to requests
- Changing response structure
- Removing endpoints
- Changing HTTP status codes for same scenario
- Changing authentication mechanisms
Non-Breaking Changes
Safe changes that don't require a new version:
- Adding new endpoints
- Adding optional request fields
- Adding new fields to responses (clients should ignore unknown fields)
- Fixing bugs
- Performance improvements
- Adding new HTTP methods to existing resources
Versioning Strategies
1. URI Versioning
Most common and visible approach. Version is part of the URL path.
GET /v1/users/123
GET /v2/users/123
Advantages:
- Clear and visible in URLs
- Easy to understand and implement
- Simple routing and caching
- Can run multiple versions simultaneously
Disadvantages:
- Violates REST principle (same resource, different URIs)
- Requires updating client code to change version
- Can lead to URI proliferation
Implementation:
/v1/users
/v1/products
/v2/users # New version with breaking changes
/v2/products
2. Header Versioning
Version specified in HTTP headers (Accept header or custom header).
Accept Header:
GET /users/123
Accept: application/vnd.myapi.v1+json
GET /users/123
Accept: application/vnd.myapi.v2+json
Custom Header:
GET /users/123
API-Version: 1
GET /users/123
API-Version: 2
Advantages:
- URIs remain stable
- More RESTful (same resource, same URI)
- Separates versioning from resource identification
Disadvantages:
- Less visible (harder to debug)
- More complex routing
- Difficult to test in browser
- Cache complexity
3. Query Parameter Versioning
Version specified as query parameter.
GET /users/123?version=1
GET /users/123?version=2
# or
GET /users/123?api-version=1
GET /users/123?api-version=2
Advantages:
- Simple to implement
- Easy to test
- Visible in URLs
Disadvantages:
- Pollutes query string
- Not semantic (version not a filter)
- Can interfere with other query params
4. Content Negotiation
Client specifies desired version through content negotiation.
GET /users/123
Accept: application/vnd.myapi+json; version=1
GET /users/123
Accept: application/vnd.myapi+json; version=2
Advantages:
- Very RESTful
- Flexible content type negotiation
- Stable URIs
Disadvantages:
- Complex implementation
- Less intuitive for developers
- Harder to test
Recommended Approach
URI versioning is recommended for most APIs because:
- It's the most explicit and discoverable
- Easy to understand and debug
- Simple to implement and maintain
- Clear separation between versions
/v1/users
/v2/users
/v3/users
Version Format
Major Versions Only
Use simple major versions (v1, v2, v3) for public APIs:
/v1/users
/v2/users
Advantages:
- Simple and clear
- Easy to communicate
- Forces thoughtful breaking changes
Date-Based Versions
Some APIs use dates for versions:
/2024-01-01/users
/2024-06-15/users
Used by: Stripe, GitHub API
Advantages:
- Clear when version was released
- Easy to understand timeline
- No confusion about major/minor
Disadvantages:
- Less intuitive for clients
- Harder to understand what changed
Version Lifecycle
1. Introduction Phase
New version is released alongside existing version:
/v1/users # Still supported
/v2/users # New version available
Announce new version:
- Blog post explaining changes
- Migration guide
- Breaking changes list
- Timeline for v1 deprecation
2. Deprecation Phase
Mark old version as deprecated but keep it running:
GET /v1/users/123
Response:
Deprecation: true
Sunset: Wed, 15 Jan 2025 00:00:00 GMT
Link: </v2/users/123>; rel="successor-version"
{
"id": 123,
"name": "John Doe"
}
Deprecation Headers:
Deprecation: true- Indicates version is deprecatedSunset: <date>- When version will be removed (RFC 8594)Link: <url>; rel="successor-version"- Points to new version
3. Sunset Phase
Old version is shut down on announced date.
Return 410 Gone for deprecated endpoints:
GET /v1/users/123
Response: 410 Gone
{
"error": {
"code": "VERSION_SUNSET",
"message": "API v1 was sunset on 2025-01-15. Please use v2.",
"documentation_url": "https://api.example.com/docs/migration-v1-to-v2"
}
}
Deprecation Policy
Recommended Timeline
- Announce deprecation - At least 6 months before sunset
- Support period - Run both versions for 6-12 months
- Sunset date - Clear date communicated in advance
- Grace period - 30 days of 410 Gone responses before complete shutdown
Communication Channels
- API response headers
- Email to registered developers
- Blog posts and changelog
- Dashboard notifications
- Documentation updates
- Status page announcements
Migration Strategy
Provide Migration Guide
# Migrating from v1 to v2
## Breaking Changes
### User Resource Changes
**v1:**
```json
{
"id": 123,
"name": "John Doe",
"email": "john@example.com"
}
v2:
{
"id": 123,
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com"
}
Migration:
- Split
namefield intofirst_nameandlast_name - Update client code to use new fields
### Offer Tools
- Migration scripts
- SDK updates
- API diff viewer
- Compatibility layer (temporary)
## Version Discovery
### Root Endpoint
```http
GET /
Response:
{
"versions": {
"v1": {
"status": "deprecated",
"sunset_date": "2025-01-15",
"documentation_url": "https://api.example.com/docs/v1"
},
"v2": {
"status": "current",
"documentation_url": "https://api.example.com/docs/v2"
},
"v3": {
"status": "beta",
"documentation_url": "https://api.example.com/docs/v3"
}
}
}
Version Info Endpoint
GET /v2/version
Response:
{
"version": "v2",
"released": "2024-01-15",
"status": "stable",
"sunset_date": null
}
OpenAPI Versioning
Separate Specs per Version
openapi-v1.yaml
openapi-v2.yaml
openapi-v3.yaml
Each spec is complete and independent.
Single Spec with Servers
openapi: 3.1.0
info:
title: My API
version: 2.0.0
servers:
- url: https://api.example.com/v1
description: Version 1 (deprecated)
- url: https://api.example.com/v2
description: Version 2 (current)
Best Practices
- Version from day one - Start with /v1, not /api
- Major versions only - Use v1, v2, v3 (not v1.1, v1.2)
- Long deprecation periods - Give clients time to migrate (6-12 months)
- Clear communication - Use headers, docs, emails
- Maintain old versions - Support at least 2 versions simultaneously
- Document changes - Provide detailed migration guides
- Use semantic versioning - For internal/SDK versioning
- Never break without warning - Always announce breaking changes
- Provide tools - Migration scripts, updated SDKs
- Monitor usage - Track which versions are being used
Anti-Patterns
Avoid these mistakes:
- Breaking changes without version bump - Breaks existing clients
- Too many versions - Maintenance nightmare (max 2-3 active versions)
- Short deprecation periods - Frustrates developers
- No migration path - Makes upgrades painful
- Surprise sunsets - Breaks production apps without warning
- Inconsistent versioning - Different strategies for different endpoints
- Versioning individual endpoints - Use consistent version across API