API versioning strategies
Why versioning is needed
API changes faster than clients. Versioning allows you to release features and edit errors without breaking integrations, sets the ritual of change and makes evolution predictable. Key: default additive changes, majors - for breakage only, automatic compatibility checks and thoughtful sunset policy.
Basic principles
1. Additive-first: new optional fields/methods/events - ok; deletions and meaning changes - major.
2. MGC (minimum warranty contract) is stable; enrichment - through capabilities/'? include = '.
3. Tolerant reader: clients ignore unknown fields and correctly survive enum extensions.
4. Semantic Versioning: `MAJOR. MINOR. PATCH 'for artifacts, SDKs, and events.
5. Automate: schema-diff, linters, and CDC tests block breaking changes.
Where to store the version (addressing mechanics)
REST/HTTP
URI version: '/v1/orders' → easy to route, convenient in gateways. Minus - "obscures" the evolution of ideas.
Media/Header: 'Accept: application/vnd. example. orders. v1 + json '- exact format control; harder to debunk.
Query version: '? api-version = 1' - convenient for A/B, but easy to lose in caches/logs.
Recommendation: URI for major lines + feature/capability flags and content negation for minor extensions.
gRPC / Protobuf
Packages/services: 'package payments. v1; service PaymentsV1; '- explicit lines.
Tag numbering is unchanged; deleted tags cannot be reused.
New fields - 'optional'; breaking → new '.v2'.
GraphQL
Schema without explicit majors in the URL. Evolution through @ deprecated and migration windows; "real" major is a new endpoint scheme.
Control complexity/depth is part of the contract.
Event-driven (Kafka/NATS/Pulsar)
Event name: 'payment. authorized. v1 'is the version in the type.
Schema registry (Avro/JSON Schema/Protobuf) with compatibility modes (BACKWARD/FORWARD/FULL).
Major → dual-emit 'v1' and 'v2' for the transition period.
Version Model and Change Policy
Semantic Versioning
MAJOR - breaking changes: deleting/renaming fields, changing membership keys, different meaning of statuses, tightening validation.
MINOR - additive: new optional fields/endpoints/events, new enum values with tolerant-reader.
PATCH - fixes without changing the contract and semantics.
Deprecation & Sunset
Mark obsolete ('Deprecated: true', '@ deprecated'), publish sunset date, alternative and migration guide.
In HTTP - headers' Sunset ',' Deprecation '; in events - a separate '.deprecation. notice`.
Telemetry usage to decide whether to remove.
Versioning Strategies by Style
REST
Major lines on/v1 ,/v2.
MINOR: schema extension, '? fields =', '? include ='; secure enum extensions.
ETag/If-Match and idempotent POST for non-breakage consistency.
Errors - stable format ('type', 'code', 'trace _ id').
gRPC
Introduce new services/methods for breaking: 'PaymentsV2. Capture`.
Error statuses and deadline semantics are part of the contract; change → new method/service.
Streaming: Agree on message order and don't change it to minor.
GraphQL
Add fields and types freely; deletions - via '@ deprecated' + migration window; large redesign → new scheme ('/graphql-v2 ').
Introspection and descriptions - must-have for customer migrations.
Events
Core vs enriched: the core is stable, enriches live nearby and are versioned separately.
The partition key is unchanged within the major line.
Major migrations - 'dual-emit' + projection/consumer migration.
Negotiation and capability flags
Capabilities: client sends'X-Capabilities: risk_score,price_v2'; the server responds with an extended view.
Prefer (HTTP) and hints for partial responses.
In sockets/streams - a handshake message with a list of supported extensions.
Release of major versions without pain
1. RFC/ADR: why major is needed, what breaks, risk matrix.
2. Dual-run: parallel launch of 'v1' and 'v2' (double event publishing, two gateway routs, traffic mirroring).
3. Migration adapters: 'v1↔v2' proxies/translators for heavy clients.
4. Canary: by customer group/topic/tag in gateway.
5. Sunset plan: dates, communication, stands, test data, usage monitoring.
Platform and infrastructure roles
API Gateway/Service Mesh: routing by version, headers, paths; rate-limit и auth per-version.
Schema Registry & Catalog: Source of Truth for Specs/Schemes with Diffuse History.
CI/CD: линтеры (Spectral, Buf), schema-diff (OpenAPI-diff, Buf breaking), CDC (Pact).
Observation: the version should be included in the logs/trails/metrics.
Version testing
Schema-diff in PR: block breaking.
Consumer-Driven Contracts: Provider is tested against real consumer contracts.
Golden samples: Reference responses to versions.
E2E canary: comparison of p95/p99, errors and timeouts between versions.
Replay for events: projections are reassembled to v2 with no discrepancies.
Data and database migration
For REST/gRPC: database migrations are coordinated via expand-and-contract (add a column → start writing → switch reading → delete old).
For Events: dual-emit and consumer migration; sometimes - replaying the log on new projections.
Do not make "big explosions" - split into steps with rollbacks.
Security relationship
Scopes per version: individual OIDC-scopes/roles for v1/v2.
Secrets/claims of the token are part of the contract; their change = major.
PII/PCI - do not expand payload unnecessarily; new fields - with a minimum of privileges.
Anti-patterns
Swagger-wash: specification updated, server not (or vice versa).
Reusing protobuf tags is a quiet corruption of data.
Changing enum meanings without major.
Global '/v2 '"for the sake of cosmetics": a version without the fact of breaking.
Forgot sunset/usage metrics: it is impossible to remove the old version safely.
One common topic for different majors: a mixture of orders and keys.
Pre-release checklist
- Changes are additive or a separate major line is prepared.
- Linters and schema-diff are green (breaking did not crawl).
- Updated SDK/examples/documentation, compatibility footnotes.
- Enabled tolerant reader on clients; enum-fallback tested.
- For major - dual-run plan, adapters, canary, sunset dates and mailing.
- Metrics/logs/trails contain a version and segmentation by it.
- There is a stand and golden sets for comparing v1↔v2.
- For events, the schema registry is in BACKWARD/FULL mode, the partition keys are unchanged.
Sample templates
REST (URI + negotiation)
Route: '/api/v2/orders/{ id} '
Заголовок: `Prefer: include=items,customer`, `X-Capabilities: risk_score`
Deprecation: `Sunset: 2026-06-30`, `Link: ; rel="alternate"`
Protobuf/gRPC
proto package payments.v2;
service PaymentsV2 {
rpc Capture (CaptureRequestV2) returns (CaptureResponseV2);
}
Events
`payment. captured. v2 '(core) and' payment. enriched. v2 '(details).
For the transition period, the producer goes' v1'and' v2 '.
FAQ
When exactly is '/v2 'needed?
When invariants/semantics change, fields/methods are removed, the format of identifiers, the partitioning key, SLA/timings change so that clients break.
Can you live without an explicit version in REST?
For small systems only. For external integrations, explicit major lines are better.
How long to keep the outdated version?
Depends on the ecosystem. For external partners, usually 6-12 months with early notification and canary.
How is event versioning different from API?
Events - append-only; new semantics = new type '.v2' and dual-emit. The partitioning key is part of the contract.
Result
Versioning strategies are process and tools: default additive evolution, clear major lines, capability-negotiation, dual-run, and sunset. Add automatic compatibility checks, usage telemetry, and documentation discipline - and your APIs will evolve quickly, without "night migrations" and unexpected drops in customers.