Skip to main content

API versioning strategy exists

ab-000365 · api-design.versioning-evolution.versioning-strategy
Severity: criticalactive

Why it matters

An unversioned API has no mechanism to introduce breaking changes without breaking every existing consumer simultaneously. The first time a field is renamed, an endpoint is removed, or a response type changes, all integrators are affected with no warning or migration path. iso-25010:2011 compatibility.interoperability and maintainability.modifiability both require that the API surface area be stable across releases — and versioning is the mechanism that makes stability promises credible. Adding /v1/ retroactively after you have external consumers is far more disruptive than doing it before launch.

Severity rationale

Critical because an API with no versioning cannot introduce breaking changes without immediately breaking all consumers, making the API contract permanently fragile.

Remediation

Add URL-based versioning to all endpoints now, before external consumers lock in on your paths:

// Before:
/api/users
/api/orders

// After:
/api/v1/users
/api/v1/orders

For GraphQL, version via @deprecated directives and a documented deprecation timeline rather than URL versioning. For gRPC, add package versioning to your .proto files: package myapi.v1;. If you already have consumers on unversioned paths, add a redirect layer from /api/ to /api/v1/ while you migrate client code.

Detection

  • ID: api-design.versioning-evolution.versioning-strategy

  • Severity: critical

  • What to look for: Check whether the API has a versioning strategy. Look for: URL-based versioning (/api/v1/, /v2/), header-based versioning (Accept: application/vnd.api+json; version=2, API-Version: 2), query parameter versioning (?version=2), or content negotiation. Also check: if a version indicator exists, is it applied consistently to ALL endpoints? For GraphQL: check for schema versioning via @deprecated directives or schema stitching. For gRPC: check for package versioning in .proto files (package api.v1;).

  • Pass criteria: A versioning strategy is in place and documented -- either URL-based (most common), header-based, or framework-appropriate. Count all endpoints and verify that at least 100% include the version indicator consistently. Do not pass when versioning exists on some endpoints but not others.

  • Fail criteria: No versioning of any kind. OR versioning exists but is inconsistently applied (some endpoints versioned, others not, e.g., /api/v1/users alongside /api/orders). OR multiple versioning approaches coexist without documentation.

  • Skip (N/A) when: The API is internal-only with no external consumers AND the project is pre-launch with no published documentation. Signal: no public API docs, no SDK, no webhook endpoints, no third-party integrations.

  • Detail on fail: Note what's missing (e.g., "No version prefix in any URL path. No version headers. 14 endpoints at /api/resource with no versioning. When breaking changes are needed, there's no mechanism to protect existing consumers."). Max 500 chars.

  • Remediation: Add URL-based versioning (the most straightforward approach) to all endpoints now, before you have external consumers:

    // Before:
    /api/users
    /api/orders
    
    // After:
    /api/v1/users
    /api/v1/orders
    

    For GraphQL, version via @deprecated directives on fields and a documented deprecation timeline rather than URL versioning.

    For gRPC, use package versioning: package myapi.v1; in your .proto files.

External references

Taxons

History