Learn-skills.dev api-versioning

Comprehensive guide to API versioning strategies, backward compatibility, deprecation, and lifecycle management. Use when user asks about "version API", "API compatibility", "breaking changes", "semantic versioning", "API evolution", "deprecation strategy", "backwards compatibility", "API migration", "version management", "sunset header", "API changelog", "schema versioning", "OpenAPI versioning", "GraphQL versioning", or mentions API lifecycle management, consumer migration, or version negotiation.

install
source · Clone the upstream repo
git clone https://github.com/NeverSight/learn-skills.dev
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/NeverSight/learn-skills.dev "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/skills-md/1mangesh1/dev-skills-collection/api-versioning" ~/.claude/skills/neversight-learn-skills-dev-api-versioning && rm -rf "$T"
manifest: data/skills-md/1mangesh1/dev-skills-collection/api-versioning/SKILL.md
source content

API Versioning and Compatibility

Strategies for versioning APIs, managing backward compatibility, handling deprecation, and coordinating version lifecycles across consumers.

1. Versioning Approaches

1.1 URL Path Versioning

The version identifier is embedded directly in the URL path.

GET /api/v1/users
GET /api/v2/users
GET /api/v1/users/42/orders

Pros:

  • Visible in URLs, logs, and documentation at a glance
  • Simple to route at the load balancer or gateway level
  • Cache-friendly since each version has a distinct URL

Cons:

  • Duplicates routes per version; every resource must be replicated
  • Encourages large, sweeping version bumps rather than incremental changes
  • Clients must update hardcoded URLs when migrating
  • Violates the REST principle that a URI should identify a resource, not a representation version

When to use: Public APIs with external consumers. Stripe uses

/v1/
, Google Cloud uses
/v1/
and
/v2/
.

1.2 Header Versioning

Accept: application/vnd.mycompany.users.v2+json    # content negotiation
X-API-Version: 2                                     # custom header

Pros:

  • URLs remain stable and resource-oriented
  • Supports fine-grained versioning per resource or operation

Cons:

  • Less discoverable; version is not visible in the URL
  • Harder to test in a browser or with simple curl commands
  • Caching proxies may not vary on custom headers by default

When to use: Internal APIs or where URL stability matters. GitHub uses

Accept: application/vnd.github.v3+json
.

1.3 Query Parameter Versioning

GET /api/users?version=2

Pros:

  • Easy to add to existing APIs without restructuring routes
  • Simple to override during testing or debugging

Cons:

  • Pollutes the query string with infrastructure concerns
  • Complicates caching because query strings affect cache keys unpredictably
  • Harder to enforce at the routing layer

When to use: Internal tooling or debugging fallback. Rarely the best choice for production APIs.

2. Semantic Versioning for APIs

Semver uses

MAJOR.MINOR.PATCH
:

  • MAJOR (v1 to v2): Breaking changes requiring consumer updates
  • MINOR (v1.1 to v1.2): Backward-compatible new features
  • PATCH (v1.1.0 to v1.1.1): Backward-compatible bug fixes

For URL versioning, only MAJOR appears in the path (

/v1/
). Communicate MINOR/PATCH through changelogs or response headers (
X-API-Version: 2.3.1
).

3. Breaking vs Non-Breaking Changes

Breaking changes (require new major version):

  • Removing or renaming a field in request or response
  • Changing a field's type (string to integer)
  • Removing an endpoint or changing its HTTP method
  • Making a previously optional field required
  • Changing authentication requirements, error formats, or pagination structure
  • Removing an enum value consumers depend on

Non-breaking changes (safe within current version):

  • Adding new optional request fields, response fields, endpoints, or query parameters
  • Adding new enum values (if consumers handle unknowns gracefully)
  • Relaxing validation constraints; adding response headers

Gray area: Changing default values, adding required fields with defaults, changing rate limits, modifying field order in JSON.

4. Deprecation Strategies

Sunset and Deprecation Headers

Use the

Sunset
header (RFC 8594) with a
Deprecation
header and migration link:

Deprecation: true
Sunset: Sat, 01 Mar 2025 00:00:00 GMT
Link: <https://api.example.com/docs/migration-v3>; rel="successor-version"

Deprecation Practices

  • Mark deprecated fields/endpoints in OpenAPI specs with
    deprecated: true
  • Log usage of deprecated endpoints to track remaining consumers
  • Ship migration guides with every major bump: breaking changes list, before/after examples, timeline, support contacts

Typical Timeline

Month 0:  Announce deprecation. New version available.
Month 3:  Notify all known consumers with migration guide.
Month 6:  Warning headers on every old-version request.
Month 9:  Reduce SLA to best-effort for deprecated version.
Month 12: Remove deprecated version. Return 410 Gone.

Public APIs with many external consumers may need 18-24 months.

5. Version Lifecycle Management

Maintain at most two to three active major versions. Define clear states:

StateDescription
PreviewAvailable for testing, may change without notice
ActiveFully supported, recommended for new integrations
DeprecatedFunctional but scheduled for removal, no new features
RetiredReturns 410 Gone with pointer to successor

Default version behavior when consumer omits version: fixed default (predictable but can strand users), latest stable (risky for unexpected breaks), or require explicit version (safest but adds friction).

6. API Changelog Patterns

Maintain structured, machine-readable changelogs:

{
  "version": "2.3.0",
  "date": "2025-01-15",
  "changes": [
    { "type": "added", "endpoint": "GET /v2/users/{id}/preferences", "description": "Retrieve user preferences" },
    { "type": "deprecated", "endpoint": "GET /v2/users/{id}/settings", "description": "Use /preferences. Sunset: 2025-07-15" }
  ]
}

Categorize entries as: added, changed, deprecated, removed, fixed, security. Link to migration docs for breaking changes. Publish changelogs in documentation and as an API endpoint.

7. Backward Compatibility Patterns

Additive changes only: The safest evolution strategy is to only add, never remove or rename:

// v1 response (original)
{ "name": "Alice", "email": "alice@example.com" }

// v1 response after additive change (still v1, non-breaking)
{ "name": "Alice", "email": "alice@example.com", "phone": "+1-555-0100" }

Optional fields with defaults: New request fields must be optional with sensible defaults so existing clients are unaffected. For example, a new

role
field defaults to
"member"
when omitted.

Response envelope stability: Keep the top-level response structure (

data
,
meta
,
errors
) consistent across all changes within a version. Consumers build deserialization logic around this structure.

Tolerant reader pattern: Encourage consumers to ignore unknown fields, avoid depending on field ordering, and handle missing optional fields gracefully. Document this expectation in your API guidelines.

8. Database Schema Versioning

API version changes often require schema changes. Strategies for coexisting versions:

  • Expand-and-contract: Add new columns first, migrate data, update the API, then remove old columns.
  • Database views: Versioned views present data in the shape each API version expects while underlying tables evolve.
  • Migration sequencing: Apply migrations in order that supports both old and new API simultaneously.

Key rules:

  • Never drop a column while any active API version still reads from it
  • Use feature flags to control which API version writes to new columns
  • Test rollback scenarios: can you revert the API version without a database rollback?
  • Coordinate migration timing with deployment: schema changes must be deployed before the API code that depends on them

9. OpenAPI/Swagger Versioning

Maintain separate spec files per major version (

openapi-v1.yaml
,
openapi-v2.yaml
). Mark deprecated operations:

paths:
  /v1/users/{id}/settings:
    get:
      deprecated: true
      summary: Get user settings (use /preferences instead)
      x-sunset: "2025-07-15"

Use tools like

oasdiff
or
openapi-diff
in CI to detect breaking changes between spec versions automatically.

10. Real-World Examples

REST Endpoint Evolution

# v1 original
GET /api/v1/users/42 -> { "id": 42, "name": "Alice", "email": "alice@example.com" }

# v1 additive (non-breaking)
GET /api/v1/users/42 -> { "id": 42, "name": "Alice", "email": "alice@example.com", "created_at": "2024-01-15T10:00:00Z" }

# v2 restructured (breaking: name split)
GET /api/v2/users/42 -> { "id": 42, "first_name": "Alice", "last_name": "Smith", "email": "alice@example.com" }

GraphQL Schema Evolution

GraphQL avoids URL versioning. Evolve additively with built-in deprecation:

type User {
  id: ID!
  name: String! @deprecated(reason: "Use firstName and lastName")
  firstName: String!
  lastName: String!
  email: String!
}

Monitor field usage analytics to determine when deprecated fields can be removed safely.

11. Common Mistakes

  • Versioning too early: Do not create v2 until you have an actual breaking change. Use additive changes within v1 as long as possible.
  • Too many active versions: Four or more active major versions signals deprecation is too slow or breaking changes are too frequent.
  • Inconsistent versioning: Mixing URL versioning for some endpoints and header versioning for others confuses consumers.
  • Treating minor changes as major: Adding fields or endpoints does not need a major bump. Over-incrementing dilutes version meaning.
  • No version discovery: Provide a discovery endpoint or include version info in response headers.

12. Migration Tooling and Consumer Communication

Tooling:

  • Codemods/scripts to rewrite consumer code automatically (Stripe and Facebook invest heavily here)
  • Adapter layers that translate v1 requests to v2 format internally
  • Versioned client SDKs with clear upgrade instructions
  • Sandbox environments for pre-migration testing

Communication:

  • Changelog emails or webhooks to registered consumers
  • Developer portal announcements
  • API usage dashboards showing which deprecated endpoints a consumer still calls
  • Direct outreach for high-traffic consumers

Tracking migration progress:

Monitor the percentage of traffic still hitting deprecated versions. Use this data to:

  • Identify consumers who need targeted outreach
  • Decide whether to extend or enforce sunset deadlines
  • Justify the cost of maintaining old versions to stakeholders
  • Set data-driven go/no-go decisions for version retirement

References

  • RFC 8594: The Sunset HTTP Header Field
  • Semantic Versioning specification (semver.org)
  • OpenAPI Specification (spec.openapis.org)
  • Stripe API versioning documentation
  • GitHub API versioning approach
  • Google Cloud API design guide: Compatibility section