Question 15 · Section 6

How to organize REST API versioning?

When you change an API (remove fields, change structure), old clients may stop working. Versioning allows supporting multiple versions simultaneously.

Language versions: English Russian Ukrainian

Junior Level

API versioning is a way to manage changes without breaking existing clients.

Why is versioning needed?

When you change an API (remove fields, change structure), old clients may stop working. Versioning allows supporting multiple versions simultaneously.

Main strategies:

1. Version in URL — the most common approach in the industry (GitHub, Stripe, AWS). Easiest to route and debug:

/api/v1/users
/api/v2/users

2. Version in header:

GET /users
Accept-Version: v2

3. Version in content type:

GET /users
Accept: application/vnd.myapi.v2+json

What counts as a “breaking” change (Breaking Change)?

  • Removing or renaming fields
  • Changing structure (object became an array)
  • New required fields
  • Changing data types (Integer → String)

Middle Level

Versioning strategies

1. URI Versioning (/v1/users)

  • Pro: Easy to configure caching on CDN. Simple logging and routing
  • Con: URL changes with each new version

2. Header Versioning (Accept-Version: v2)

  • Pro: One endpoint serves different versions
  • Con: Requires a Vary: Accept-Version header, otherwise the cache will serve V1 to a V2 user

3. Media Type Versioning (Accept: application/vnd.myapi.v2+json)

  • Pro: Closest to the spirit of REST per Roy Fielding (creator of REST), as it uses Content Negotiation. However, in practice it’s rarely used due to debugging complexity.
  • Con: Harder to implement and debug

Contract Testing (Pact)

In microservices, versioning is often replaced by contract testing:

  • The Consumer describes which fields it needs
  • The Provider checks at each build whether it broke client expectations
  • Allows “smooth” updates without changing the major version

API Gateway and Blue-Green Deployment

An API Gateway (Kong, Spring Cloud Gateway) can act as a Version Router:

  • 10% of traffic with header v2 → new version (Canary Release — the new version gets a small percentage of traffic, e.g., 10%. If there are no errors — traffic is gradually shifted completely).
  • 90% → old version

Performance

  • Sunset Policy (RFC 8594): Always set a “death date” for the old version. Use the Sunset header
  • Backward Compatibility Layer: Create an “adapter” at the API Gateway level that transforms old-format requests into the new format

Edge Cases

  • Database Schema: The hardest part — when the new version requires a DB schema change
    • Solution: “Expand and Contract” strategy — DB migration without downtime: 1) Expand — add a new column without removing the old one. 2) Migrate data. 3) Contract — remove the old column.
  • Side Effects: If v1 writes to table A, and v2 — to table B, make sure analytics accounts for both sources

Senior Level

Contract management

Versioning is about managing the contract between provider and consumer. Any change that requires changes in the client’s code is “breaking”.

What breaks the contract?

  • Removing or renaming fields in JSON
  • Changing structure (object became an array)
  • Changing validation logic (new required field)
  • Changing data types (Integer → String)

Infrastructure and Testing

Contract Testing (Pact)

Allows “smooth” updates without changing the major version:

  • Consumer describes expectations
  • Provider checks compatibility at each build

API Gateway and Canary Release

API Gateway acts as a Version Router:

  • Canary Release: 10% traffic to v2, 90% to v1
  • Monitor errors on the new version before full switch

Performance and Highload

Sunset Policy (RFC 8594)

Use the Sunset header to warn clients about version deprecation:

Sunset: Sat, 31 Dec 2026 23:59:59 GMT

Backward Compatibility Layer

If supporting the old version is expensive — create a proxy adapter on the API Gateway. This allows removing old code from the main microservice.

Diagnostics

  • Metric: api_version_usage_total: Count requests per version. This is the only way to reasonably justify deprecating an old version
  • Deprecation Header: Use Deprecation: true for automated monitoring of deprecated endpoint usage
  • Database Strategy: Expand and Contract — first add new columns, then migrate, then remove old ones

Interview Cheat Sheet

Must know:

  • 3 strategies: URL (/v1/users), header (Accept-Version: v2), Media Type (application/vnd.myapi.v2+json)
  • URL Versioning — most common approach (GitHub, Stripe, AWS), easiest to route
  • Breaking Changes: removing/renaming fields, changing structure, new required fields, changing data types
  • Contract Testing (Pact) allows updating APIs without changing the major version
  • Sunset Policy (RFC 8594): Sunset header warns about version deprecation
  • Expand and Contract — DB migration without downtime: add column → migrate → remove old
  • API Gateway can act as Version Router with Canary Release (10% to v2)

Common follow-up questions:

  • What breaks an API contract? — Removing fields, changing structure, new required fields, changing data types
  • Why is Media Type Versioning rarely used? — Harder to implement and debug, although closer to REST spirit
  • What is Contract Testing? — Consumer describes expectations, Provider checks compatibility at each build
  • How to migrate DB during versioning? — Expand and Contract: add new, migrate data, remove old

Red flags (DO NOT say):

  • “URL versioning is an anti-pattern” — it’s the most common approach in the industry
  • “Non-breaking changes don’t require versioning” — even adding a field can break clients
  • “Contract Testing fully replaces versioning” — allows smooth updates but doesn’t eliminate it
  • “Media Type Versioning is the best choice” — theoretically correct, but rarely used in practice

Related topics:

  • [[What is REST]]
  • [[What is RESTful API design]]
  • [[What is Content-Type header]]
  • [[What is Accept header]]
  • [[How to properly name REST endpoints]]