API Versioning Strategies
API versioning allows the API contract to evolve while maintaining backward compatibility for existing clients. Common strategies: URI versioning (/v1/orders), header versioning (Accept: application/vnd.api.v2+json), and query parameter versioning (?version=2).
API versioning = building extensions on a house. Best case: add rooms without touching the old ones. Worst case: you're maintaining 8 different floor plans and nobody remembers which door leads where.
URI versioning is most visible and cacheable but creates parallel API surfaces that must be maintained simultaneously. Header versioning is cleaner but harder to test and debug. The preferred approach for REST APIs is to avoid explicit versioning through evolutionary design: use additive changes (add new fields, never remove or rename), deprecation headers (Sunset, Deprecation), and sunset dates to communicate end-of-life timelines. For gRPC, Protobuf's schema evolution rules handle most versioning concerns without changing the service URL.
The Stripe model is the industry gold standard: a single URL, no version in the path, but each API key is pinned to the API version that existed when the key was created. Stripe's backend runs all API versions simultaneously, translating requests/responses at the edge. This gives perfect backward compatibility — your old code never breaks — while allowing the API to evolve. Implementing this requires an API version registry, per-version request/response schemas, and transformation middleware. Consumer-driven contract testing (Pact) is the engineering tool that makes evolutionary design practical: each consumer specifies the exact fields it depends on, and the provider CI verifies it never breaks those contracts.
I design APIs for evolution from the start: required fields are never removed, field types are never changed, new fields are always optional with sensible defaults. I use Deprecation and Sunset headers to communicate end-of-life timelines with at least 6 months' notice. For major breaking changes (resource restructure), I introduce a new URL prefix (/v2/) but maintain the old version for the sunset period. I use Pact for consumer-driven contract tests to catch breaking changes in CI before they reach production.
Versioning every minor change. API version proliferation means maintaining v1, v2, v3, v4 in production simultaneously. Each new version is a maintenance burden. Prefer evolutionary design and only introduce a major version for genuine breaking changes.