Стратегії версіонування API
Навіщо потрібне версіонування
API змінюється швидше, ніж клієнти. Версіонування дозволяє випускати фічі і правити помилки без поломок інтеграцій, задає ритуал змін і робить еволюцію передбачуваною. Ключ: адитивні зміни за замовчуванням, majors - тільки для ломок, автоматичні перевірки сумісності і продумана політика sunset.
Базові принципи
1. Additive-first: нові опціональні поля/методи/події - ок; видалення і зміна сенсу - мажор.
2. MGC (мінімальний гарантійний контракт) стабільний; збагачення - через capabilities/'? include ='.
3. Tolerant reader: клієнти ігнорують невідомі поля і коректно переживають розширення enum.
4. Semantic Versioning: `MAJOR. MINOR. PATCH'для артефактів, SDK і подій.
5. Automate: schema-diff, лінтери і CDC-тести блокують breaking-зміни.
Де зберігати версію (механіки адресації)
REST/HTTP
URI-версія: '/v1/orders'→ просто маршрутизувати, зручно в шлюзах. Мінус - «затуляє» еволюцію уявлень.
Медіатип/заголовок: `Accept: application/vnd. example. orders. v1 + json'- точне управління форматом; складніше дебажити.
Query-версія: 'api-version = 1'- зручно для A/B, але легко втратити в кешах/логах.
Рекомендація: URI для major-ліній + feature/capability flags і контент-негаціяція для мінорних розширень.
gRPC / Protobuf
Пакети/сервіси: `package payments. v1; service PaymentsV1;'- явні лінії.
Нумерація тегів незмінна; видалені теги не перевикористати.
Нові поля - «optional»; breaking → новий'.v2'.
GraphQL
Схема без явних major в URL. Еволюція через @deprecated і вікна міграції; «справжній» major - нова ендпоінт-схема.
Контролюйте complexity/depth - це частина контракту.
Event-driven (Kafka/NATS/Pulsar)
Ім'я події: `payment. authorized. v1'- версія в типі.
Реєстр схем (Avro/JSON Schema/Protobuf) з режимами сумісності (BACKWARD/FORWARD/FULL).
Major → dual-emit'v1'і'v2'на перехідний період.
Модель версій і політика змін
Semantic Versioning
MAJOR - ламаючі зміни: видалення/перейменування полів, зміна ключів партіонування, інший сенс статусів, посилення валідації.
MINOR - адитивні: нові необов'язкові поля/ендпоінти/події, нові enum-значення при tolerant-reader.
PATCH - виправлення без зміни контракту і семантики.
Deprecation & Sunset
Позначайте застаріле ('Deprecated: true','@deprecated'), публікуйте sunset-дату, альтернативу і гайд міграції.
У HTTP - заголовки «Sunset», «Deprecation»; у подіях - окреме'.deprecation. notice`.
Ведіть телеметрію usage для прийняття рішення про зняття.
Версійні стратегії за стилями
REST
Major-лінії на/v1 ,/v2.
MINOR: розширення схем,'? fields =','? include ='; безпечні enum-розширення.
ETag/If-Match і ідемпотентні POST для узгодженості без ломок.
Помилки - стабільний формат ('type','code','trace _ id').
gRPC
Вводьте нові сервіси/методи для ломок: `PaymentsV2. Capture`.
Статуси помилок і семантика deadline - частина контракту; зміна → новий метод/сервіс.
Стрімінг: домовтеся про порядок повідомлень і не змінюйте його в minor.
GraphQL
Додавайте поля і типи вільно; видалення - через «@deprecated» + вікно міграції; великий редизайн → нова схема ('/graphql-v2').
Інтроспекція та описи - must-have для міграцій клієнтів.
Events
Core vs enriched: ядро стабільне, збагачення живуть поруч і версіонуються окремо.
Ключ партіонування незмінний в межах major-лінії.
Major-міграції -'dual-emit'+ міграція проекцій/консьюмерів.
Negotiation і capability-прапори
Capabilities: клієнт відправляє'X-Capabilities: risk_score,price_v2`; сервер відповідає розширеним представленням.
Prefer (HTTP) і «hints» для часткових відповідей.
У сокетах/стрімах - handshake-повідомлення зі списком підтримуваних розширень.
Випуск major-версій без болю
1. RFC/ADR: чому потрібен major, що ламається, матриця ризиків.
2. Dual-run: паралельний запуск «v1» і «v2» (подвійна публікація подій, два gateway-роути, дзеркалювання трафіку).
3. Міграційні адаптери: проксі/транслятори «v1↔v2» для важких клієнтів.
4. Канарка: за групами клієнтів/топіками/тегами в gateway.
5. Sunset-план: дати, комунікація, стенди, тестові дані, моніторинг використання.
Ролі платформи та інфраструктури
API Gateway/Service Mesh: маршрутизація за версією, заголовками, шляхами; rate-limit и auth per-version.
Schema Registry & Catalog: джерело істини для спек/схем з історією дифів.
CI/CD: линтеры (Spectral, Buf), schema-diff (OpenAPI-diff, Buf breaking), CDC (Pact).
Observability: версія повинна потрапляти в логи/трейси/метрики.
Тестування версій
Schema-diff в PR: блокувати breaking.
Consumer-Driven Contracts: провайдер перевіряється проти контрактів реальних споживачів.
Golden samples: еталонні відповіді на версії.
E2E-канарка: порівняння p95/p99, помилок і таймаутів між версіями.
Replay для подій: проекції пересобираються на v2 без розбіжностей.
Міграція даних і баз
Для REST/gRPC: міграції БД координуються через expand-and-contract (додай колонку → почни писати → переключаючи читання → вдали старе).
Для Events: dual-emit та міграція консьюмерів; іноді - репрогравання логу на нові проекції.
Не робіть «великих вибухів» - дробіть на кроки з відкатами.
Взаємозв'язок з безпекою
Scopes per version: окремі OIDC-scopes/ролі для v1/v2.
Секрети/claim'и токена - частина контракту; їх зміна = major.
PII/PCI - не розширюйте payload без потреби; нові поля - з мінімумом привілеїв.
Антипатерни
Swagger-wash: специфікація оновлена, сервер - ні (або навпаки).
Перевикористання protobuf-тегів - тихе псування даних.
Зміна enum-смислів без major.
Глобальне '/v2'« заради косметики »: версія без факту ломки.
Забули sunset/usage-метрики: неможливо зняти стару версію безпечно.
Один загальний топік для різних major: змішування порядків і ключів.
Чек-лист перед релізом
- Зміни адитивні або підготовлена окрема major-лінія.
- Лінтери і schema-diff зелені (breaking не проліз).
- Оновлені SDK/прикладів/документації, виноски про сумісність.
- Включений tolerant reader в клієнтах; enum-fallback протестований.
- Для major - план dual-run, адаптери, канарка, sunset-дати і розсилка.
- Метрики/логи/трейси містять версію і сегментацію по ній.
- Є стенд і golden-набори для порівняння v1↔v2.
- Для подій - реєстр схем в режимі BACKWARD/FULL, ключі партіонування незмінні.
Приклади шаблонів
REST (URI + negotiation)
Маршрут: `/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'( ядро) і'payment. enriched. v2'( деталі).
На перехідний період з продюсера йде «v1» і «v2».
FAQ
Коли точно потрібен '/v2'?
Коли змінюються інваріанти/семантика, видаляються поля/методи, змінюється формат ідентифікаторів, ключ партіонування, SLA/таймінги так, що ламаються клієнти.
Можна жити без явної версії в REST?
Тільки для дрібних систем. Для зовнішніх інтеграцій - краще явні major-лінії.
Який термін тримати застарілу версію?
Залежить від екосистеми. Для зовнішніх партнерів зазвичай 6-12 місяців з раннім повідомленням і канаркою.
Чим версіонування подій відрізняється від API?
Події - append-only; нова семантика = новий тип'.v2'і dual-emit. Ключ партіонування - частина контракту.
Підсумок
Стратегії версіонування - це процес та інструменти: адитивна еволюція за замовчуванням, ясні major-лінії, capability-negotiation, dual-run і sunset. Додайте до цього автоматичні перевірки сумісності, телеметрію використання і дисципліну документації - і ваші API будуть розвиватися швидко, без «нічних міграцій» і несподіваних падінь у клієнтів.