Skip to main content

API Versioning Skill

API Versioning Skill

How to Use This Skill

  1. Review the patterns and examples below
  2. Apply the relevant patterns to your implementation
  3. Follow the best practices outlined in this skill

Production-ready API versioning skill covering all major versioning strategies, deprecation workflows, backward compatibility patterns, and client migration guidance.

When to Use This Skill

Use api-versioning when:

  • Planning versioning strategy for new APIs
  • Adding breaking changes to existing APIs
  • Creating deprecation and sunset workflows
  • Migrating clients between API versions
  • Documenting version compatibility
  • Designing client SDK versioning

Don't use api-versioning when:

  • Designing REST API structure (use restful-api-design skill)
  • Designing GraphQL schemas (use graphql-design skill)
  • Only need security review (use security-audit skill)
  • Documentation generation only

Versioning Strategies Comparison

StrategyExampleProsCons
URL Path/api/v1/usersExplicit, cacheableURL pollution
HeaderAPI-Version: 1Clean URLsHidden, harder to test
Query Param/api/users?version=1Explicit, easy testingAffects caching
Content TypeAccept: application/vnd.api.v1+jsonHTTP standardComplex, verbose

Recommendation by Use Case

Use CaseRecommended StrategyRationale
Public REST APIURL PathMost discoverable, best tooling support
Internal MicroservicesHeaderCleaner routing, flexible
GraphQL APISchema evolutionNo versions, additive changes
Mobile AppsHeader + FallbackGraceful degradation
Browser AppsURL PathEasy debugging, bookmarkable

Instructions

Phase 1: Strategy Selection

Objective: Choose appropriate versioning strategy.

  1. Evaluate requirements:

    Decision Matrix:

    [ ] Public API with external developers → URL Path (most transparent)
    [ ] Internal services with controlled clients → Header (cleaner)
    [ ] Single Page Applications → URL Path or Query Param
    [ ] Mobile apps with slow updates → Header with backwards compatibility
    [ ] Enterprise integrations → Header with long deprecation periods
    [ ] GraphQL → Schema evolution (no explicit versions)
  2. Define version format:

    Version Formats:

    Simple numeric: v1, v2, v3
    Semantic: v1.0, v1.1, v2.0
    Date-based: 2024-01-15, 2024-06

    Recommendation: Simple numeric (v1, v2) for major versions
    Use semantic versioning in changelog/documentation
  3. Document versioning policy:

    # API Versioning Policy

    ## Version Scheme
    - Major versions: `/api/v1/`, `/api/v2/`
    - Breaking changes require new major version
    - Minor/patch changes within same version

    ## Supported Versions
    | Version | Status | Support Until |
    |---------|--------|---------------|
    | v3 | Current | Ongoing |
    | v2 | Maintained | 2025-12-31 |
    | v1 | Deprecated | 2024-06-30 |

    ## Breaking Changes Definition
    - Removing endpoints or fields
    - Changing field types or formats
    - Adding required parameters
    - Changing authentication methods
    - Modifying error response structure

Phase 2: Implementation

Objective: Implement chosen versioning strategy.

  1. URL Path Versioning:

    # FastAPI with URL versioning
    from fastapi import FastAPI, APIRouter

    app = FastAPI()

    # Version 1 router
    v1_router = APIRouter(prefix="/api/v1")

    @v1_router.get("/users")
    async def get_users_v1():
    return {"version": "v1", "users": [...]}

    # Version 2 router
    v2_router = APIRouter(prefix="/api/v2")

    @v2_router.get("/users")
    async def get_users_v2():
    # New response format
    return {
    "version": "v2",
    "data": [...],
    "pagination": {...}
    }

    app.include_router(v1_router)
    app.include_router(v2_router)
    // Express with URL versioning
    import express from 'express';

    const app = express();

    // Version 1 routes
    const v1Router = express.Router();
    v1Router.get('/users', (req, res) => {
    res.json({ version: 'v1', users: [] });
    });

    // Version 2 routes
    const v2Router = express.Router();
    v2Router.get('/users', (req, res) => {
    res.json({ version: 'v2', data: [], pagination: {} });
    });

    app.use('/api/v1', v1Router);
    app.use('/api/v2', v2Router);
  2. Header Versioning:

    # FastAPI with header versioning
    from fastapi import FastAPI, Header, HTTPException

    app = FastAPI()

    @app.get("/api/users")
    async def get_users(api_version: str = Header(default="v2", alias="API-Version")):
    if api_version == "v1":
    return {"version": "v1", "users": [...]}
    elif api_version == "v2":
    return {"version": "v2", "data": [...], "pagination": {...}}
    else:
    raise HTTPException(status_code=400, detail=f"Unsupported API version: {api_version}")
    // Express with header versioning
    const versionMiddleware = (req, res, next) => {
    req.apiVersion = req.headers['api-version'] || 'v2';
    next();
    };

    app.use(versionMiddleware);

    app.get('/api/users', (req, res) => {
    switch (req.apiVersion) {
    case 'v1':
    return res.json({ version: 'v1', users: [] });
    case 'v2':
    return res.json({ version: 'v2', data: [], pagination: {} });
    default:
    return res.status(400).json({ error: 'Unsupported version' });
    }
    });
  3. Content Negotiation:

    from fastapi import FastAPI, Request, HTTPException

    app = FastAPI()

    @app.get("/api/users")
    async def get_users(request: Request):
    accept = request.headers.get("accept", "")

    if "application/vnd.api.v1+json" in accept:
    return {"version": "v1", "users": [...]}
    elif "application/vnd.api.v2+json" in accept:
    return {"version": "v2", "data": [...]}
    else:
    # Default to latest
    return {"version": "v2", "data": [...]}

Phase 3: Deprecation Workflow

Objective: Gracefully deprecate old API versions.

  1. Add Sunset headers:

    from fastapi import FastAPI
    from fastapi.responses import JSONResponse
    from datetime import datetime

    app = FastAPI()

    @app.get("/api/v1/users")
    async def get_users_v1():
    response = JSONResponse(content={"users": [...]})

    # RFC 8594 Sunset header
    response.headers["Sunset"] = "Sat, 30 Jun 2024 23:59:59 GMT"
    response.headers["Deprecation"] = "true"
    response.headers["Link"] = '</api/v2/users>; rel="successor-version"'

    return response
  2. Document deprecation:

    # Deprecation Notice: API v1

    ## Timeline
    - **Deprecated:** January 1, 2024
    - **Read-only:** April 1, 2024
    - **Sunset:** June 30, 2024

    ## Breaking Changes in v2

    ### Response Format
    v1:
    ```json
    { "users": [...] }

    v2:

    { "data": [...], "pagination": {...} }

    Removed Endpoints

    • GET /api/v1/users/all → Use pagination with GET /api/v2/users

    Changed Fields

    • user.fullNameuser.name
    • user.registereduser.createdAt (ISO 8601 format)

    Migration Guide

    See Migration Guide v1 to v2

  3. Create migration guide:

    # Migration Guide: v1 to v2

    ## Quick Migration Checklist

    - [ ] Update base URL from `/api/v1` to `/api/v2`
    - [ ] Update response parsing for new format
    - [ ] Handle pagination in list endpoints
    - [ ] Update field names per mapping table
    - [ ] Test all API calls

    ## Response Format Changes

    ### Before (v1)
    ```javascript
    const response = await fetch('/api/v1/users');
    const { users } = await response.json();

    After (v2)

    const response = await fetch('/api/v2/users');
    const { data, pagination } = await response.json();
    const users = data;

    Field Mapping

    v1 Fieldv2 FieldNotes
    fullNamenameCombined first/last
    registeredcreatedAtISO 8601 format
    isActivestatusEnum: active/inactive

    Code Examples

    Python SDK

    # v1
    from myapi import Client
    client = Client(version='v1')
    users = client.users.list()

    # v2
    client = Client(version='v2')
    result = client.users.list()
    users = result.data
    has_more = result.pagination.has_next

Phase 4: Client SDK Versioning

Objective: Version client SDKs alongside API.

  1. SDK versioning strategy:

    SDK Version: 2.3.1
    │ │ │
    │ │ └── Patch: Bug fixes
    │ └──── Minor: New features, backwards compatible
    └────── Major: Breaking changes (often tied to API version)

    Mapping:
    - SDK 1.x.x → API v1
    - SDK 2.x.x → API v2
    - SDK 3.x.x → API v3
  2. Multi-version SDK support:

    // SDK supporting multiple API versions
    class APIClient {
    constructor(options: {
    apiKey: string;
    version?: 'v1' | 'v2' | 'v3';
    baseUrl?: string;
    }) {
    this.version = options.version || 'v3';
    this.baseUrl = options.baseUrl || 'https://api.example.com';
    }

    async getUsers(): Promise<UsersResponse> {
    const url = `${this.baseUrl}/api/${this.version}/users`;
    const response = await fetch(url, {
    headers: { 'Authorization': `Bearer ${this.apiKey}` }
    });

    if (this.version === 'v1') {
    const { users } = await response.json();
    return { data: users, pagination: null };
    }

    return response.json();
    }
    }
  3. Deprecation warnings in SDK:

    class APIClient {
    constructor(options: ClientOptions) {
    if (options.version === 'v1') {
    console.warn(
    '[DEPRECATION WARNING] API v1 is deprecated and will be removed on 2024-06-30. ' +
    'Please upgrade to v2. See https://docs.example.com/migration'
    );
    }
    }
    }

Examples

Example 1: URL Path Versioning (Complete)

# OpenAPI specification with URL versioning
openapi: 3.1.0
info:
title: User API
version: 2.0.0
description: |
## API Versioning

This API uses URL path versioning:
- `/api/v1/*` - Legacy (deprecated, sunset 2024-06-30)
- `/api/v2/*` - Current stable version
- `/api/v3/*` - Beta (subject to change)

## Version Support Policy
- Current version: Full support
- Previous version: 12 months maintenance
- Older versions: Deprecated, no support

servers:
- url: https://api.example.com/api/v2
description: Production (v2)
- url: https://api.example.com/api/v1
description: Legacy (deprecated)

paths:
/users:
get:
summary: List users
responses:
'200':
description: Success
headers:
X-API-Version:
schema:
type: string
description: API version used

Example 2: GraphQL Schema Evolution

# Schema evolution without explicit versions

type User {
id: ID!
name: String!

# Deprecated field - use 'name' instead
fullName: String @deprecated(reason: "Use 'name' field instead. Will be removed 2024-06-30")

# New fields added (non-breaking)
avatar: String
preferences: UserPreferences
}

type Query {
user(id: ID!): User

# Deprecated query
getUser(id: ID!): User @deprecated(reason: "Use 'user' query instead")

# New query added (non-breaking)
users(
first: Int
after: String
filter: UserFilter
): UserConnection!
}

Example 3: Sunset Header Implementation

from fastapi import FastAPI, Response
from fastapi.middleware.base import BaseHTTPMiddleware
from datetime import datetime
import pytz

class DeprecationMiddleware(BaseHTTPMiddleware):
DEPRECATED_VERSIONS = {
'v1': {
'sunset': datetime(2024, 6, 30, 23, 59, 59, tzinfo=pytz.UTC),
'successor': '/api/v2'
}
}

async def dispatch(self, request, call_next):
response = await call_next(request)

# Check if deprecated version
path = request.url.path
for version, info in self.DEPRECATED_VERSIONS.items():
if f'/api/{version}/' in path:
sunset_date = info['sunset'].strftime('%a, %d %b %Y %H:%M:%S GMT')
response.headers['Deprecation'] = 'true'
response.headers['Sunset'] = sunset_date
response.headers['Link'] = f'<{info["successor"]}>; rel="successor-version"'
break

return response

app = FastAPI()
app.add_middleware(DeprecationMiddleware)

Integration

  • Skill: restful-api-design - Complete REST API design
  • Skill: graphql-design - GraphQL schema evolution
  • Agent: senior-architect - Architecture decisions
  • Skill: security-audit - Version security review

OpenAPI Extensions

x-api-lifecycle:
status: deprecated
sunset: 2024-06-30T23:59:59Z
successor: /api/v2
migration-guide: https://docs.example.com/migration

x-version-history:
- version: v3.0.0
date: 2024-01-15
changes: "Added batch operations"
- version: v2.0.0
date: 2023-06-01
changes: "New pagination format"

Troubleshooting

IssueSolution
Clients not migratingAdd sunset headers, send deprecation emails
Version conflictsUse strict version matching
Cache issuesInclude version in cache keys
SDK compatibilityTest SDKs against all supported versions

Best Practices Checklist

  • Document versioning policy publicly
  • Use semantic versioning in changelog
  • Implement Sunset headers for deprecated versions
  • Provide minimum 6-month deprecation period
  • Create comprehensive migration guides
  • Version SDK alongside API
  • Test all versions in CI/CD
  • Monitor version usage analytics
  • Send proactive deprecation notifications
  • Maintain backwards compatibility when possible

References


Success Output

When successful, this skill MUST output:

✅ SKILL COMPLETE: api-versioning

Completed:
- [x] Versioning strategy selected and documented
- [x] Version routing implemented (URL/header/content negotiation)
- [x] Deprecation workflow established with Sunset headers
- [x] Migration guide created for breaking changes
- [x] Client SDK versioning aligned with API versions
- [x] OpenAPI spec updated with version metadata

Versioning Details:
- Strategy: URL Path Versioning (/api/v1, /api/v2)
- Supported Versions: v2 (current), v1 (deprecated, sunset 2024-06-30)
- Deprecation Headers: Sunset, Deprecation, Link (successor-version)

Outputs:
- API versioning policy document
- Deprecation notice for v1
- Migration guide v1 → v2
- OpenAPI spec with x-api-lifecycle metadata
- SDK version mapping documentation

Completion Checklist

Before marking this skill as complete, verify:

  • Versioning strategy documented in API policy
  • Version routing implemented and tested
  • Sunset headers added to deprecated versions
  • Deprecation notice published with timeline
  • Migration guide created with code examples
  • Breaking changes documented with before/after
  • Field mapping table created for changed schemas
  • SDK version mapping documented (SDK 1.x → API v1)
  • OpenAPI spec includes x-api-lifecycle metadata
  • All versions tested in CI/CD pipeline
  • Version analytics tracking implemented
  • Deprecation warnings added to SDK

Failure Indicators

This skill has FAILED if:

  • ❌ No versioning strategy documented
  • ❌ Multiple versioning methods mixed inconsistently
  • ❌ Deprecated versions have no sunset date
  • ❌ Breaking changes not documented
  • ❌ No migration guide for version changes
  • ❌ Client SDKs not versioned with API
  • ❌ Sunset headers missing or incorrect format
  • ❌ Version routing breaks existing clients
  • ❌ OpenAPI spec lacks version metadata
  • ❌ No version usage analytics

When NOT to Use

Do NOT use this skill when:

  • Designing new API from scratch (use restful-api-design or graphql-design first)
  • Only adding non-breaking changes (use additive evolution)
  • Internal APIs with single controlled client (versioning overhead not needed)
  • GraphQL APIs using schema evolution (use graphql-design skill)
  • Need only security/auth changes (use api-security-patterns)
  • Documenting existing versions (use api-documentation skill)
  • Performance optimization only (no version changes needed)

Use alternative skills:

  • restful-api-design - Complete REST API design
  • graphql-design - GraphQL schema evolution patterns
  • api-documentation - OpenAPI/Swagger documentation
  • api-security-patterns - Authentication and authorization

Anti-Patterns (Avoid)

Anti-PatternProblemSolution
Mixing strategiesConfuses clients, hard to maintainChoose ONE versioning strategy
No deprecation periodBreaks clients without warningMinimum 6-month deprecation period
Removing old versions earlyClient outagesFollow published sunset timeline
No migration guideClients can't upgradeCreate detailed migration docs with examples
Breaking changes in minor versionsViolates semver expectationsOnly break in major versions
No version analyticsCan't measure adoptionTrack version usage in metrics
Implicit version changesSilent behavior changesRequire explicit version in request
No SDK versioningSDK/API version mismatchVersion SDK major versions with API

Principles

This skill embodies these CODITECT principles:

  • #2 First Principles - Understand versioning strategies and tradeoffs
  • #5 Eliminate Ambiguity - Explicit version in every request
  • #6 Clear, Understandable, Explainable - Migration guides explain changes
  • #7 Inform, Don't Ask - Deprecation headers proactively notify clients
  • #8 No Assumptions - Document all breaking changes and migration paths
  • #9 Search Before Create - Follow industry standards (RFC 8594, semver)

Full Principles: CODITECT-STANDARD-AUTOMATION.md


Status: Production-ready Strategies Covered: URL, header, query, content negotiation Standards: RFC 8594 Sunset Header, Semantic Versioning Integration: REST, GraphQL, gRPC, client SDKs