Як організувати версіонування REST API?
Коли ви змінюєте API (видаляєте поля, змінюєте структуру), старі клієнти можуть перестати працювати. Версіонування дозволяє підтримувати кілька версій одночасно.
Junior Level
Версіонування API — це спосіб керування змінами, щоб не зламати існуючих клієнтів.
Навіщо потрібне версіонування?
Коли ви змінюєте API (видаляєте поля, змінюєте структуру), старі клієнти можуть перестати працювати. Версіонування дозволяє підтримувати кілька версій одночасно.
Основні стратегії:
1. Версія в URL — найпоширеніший підхід в індустрії (GitHub, Stripe, AWS). Його найпростіше маршрутизувати і дебажити:
/api/v1/users
/api/v2/users
2. Версія в заголовку:
GET /users
Accept-Version: v2
3. Версія в типі контенту:
GET /users
Accept: application/vnd.myapi.v2+json
Що вважається “ламаючою” зміною (Breaking Change)?
- Видалення або перейменування полів
- Зміна структури (об’єкт став масивом)
- Нові обов’язкові поля
- Зміна типу даних (Integer → String)
Middle Level
Стратегії версіонування
1. URI Versioning (/v1/users)
- Плюс: Легко налаштовувати кешування на CDN. Просте логування і маршрутизація
- Мінус: URL змінюється при кожній новій версії
2. Header Versioning (Accept-Version: v2)
- Плюс: Один ендпоінт віддає різні версії
- Мінус: Вимагає заголовок
Vary: Accept-Version, інакше кеш видасть V1 користувачу V2
3. Media Type Versioning (Accept: application/vnd.myapi.v2+json)
- Плюс: Найближче до духу REST за Роєм Філдінгом (творцем REST), оскільки використовує Content Negotiation. Однак на практиці рідко застосовується через складність налагодження.
- Мінус: Складніше у реалізації і налагодженні
Contract Testing (Pact)
У мікросервісах версіонування часто замінюється контрактним тестуванням:
- Споживач (Consumer) описує, які поля йому потрібні
- Постачальник (Provider) перевіряє при кожній збірці, чи не зламав він очікування клієнтів
- Дозволяє робити “плавні” оновлення без зміни мажорної версії
API Gateway і Blue-Green Deployment
API Gateway (Kong, Spring Cloud Gateway) може виконувати роль Version Router:
- 10% трафіку із заголовком
v2→ нова версія (Canary Release — канарковий реліз: нова версія отримує невеликий відсоток трафіку, наприклад 10%. Якщо помилок немає — трафік поступово переводиться повністю). - 90% → стара версія
Продуктивність
- Sunset Policy (RFC 8594): Завжди встановлюйте дату “смерті” старої версії. Використовуйте заголовок
Sunset - Backward Compatibility Layer: Створіть “адаптер” на рівні API Gateway, який трансформує запити старого формату в новий
Edge Cases
- Database Schema: Найскладніше — якщо нова версія вимагає зміни схеми БД
- Рішення: Стратегія “Expand and Contract” (Розшир і стисни) — міграція БД без downtime: 1) Expand — додаємо нову колонку, не видаляючи стару. 2) Мігруємо дані. 3) Contract — видаляємо стару колонку.
- Side Effects: Якщо v1 пише в таблицю A, а v2 — в таблицю B, переконайтеся, що аналітика враховує обидва джерела
Senior Level
Управління контрактом
Версіонування — це управління контрактом між постачальником і споживачем. Будь-яка зміна, що вимагає змін у коді клієнта, є “ламаючою”.
Що ламає контракт?
- Видалення або перейменування полів у JSON
- Зміна структури (об’єкт став масивом)
- Зміна логіки валідації (нове обов’язкове поле)
- Зміна типу даних (Integer → String)
Інфраструктура і Тестування
Contract Testing (Pact)
Дозволяє робити “плавні” оновлення без зміни мажорної версії:
- Consumer описує очікування
- Provider перевіряє сумісність при кожній збірці
API Gateway і Canary Release
API Gateway виконує роль Version Router:
- Canary Release: 10% трафіку на v2, 90% на v1
- Моніторинг помилок на новій версії перед повним переключенням
Продуктивність і Highload
Sunset Policy (RFC 8594)
Використовуйте заголовок Sunset для попередження клієнтів про відключення версії:
Sunset: Sat, 31 Dec 2026 23:59:59 GMT
Backward Compatibility Layer
Якщо підтримка старої версії дорога — створіть проксі-адаптер на API Gateway. Це дозволить видалити старий код з основного мікросервісу.
Діагностика
- Metric:
api_version_usage_total: Рахуйте запити до кожної версії. Це єдиний спосіб аргументовано відключити стару версію - Deprecation Header: Використовуйте
Deprecation: trueдля автоматизованого моніторингу використання застарілих методів - Database Strategy: Expand and Contract — спочатку додаємо нові колонки, потім мігруємо, потім видаляємо старі
🎯 Шпаргалка для співбесіди
Обов’язково знати:
- 3 стратегії: URL (
/v1/users), заголовок (Accept-Version: v2), Media Type (application/vnd.myapi.v2+json) - URL Versioning — найпоширеніший підхід (GitHub, Stripe, AWS), простіше маршрутизувати
- Breaking Changes: видалення/перейменування полів, зміна структури, нові обов’язкові поля, зміна типу даних
- Contract Testing (Pact) дозволяє оновлювати API без зміни мажорної версії
- Sunset Policy (RFC 8594): заголовок
Sunsetпопереджає про відключення версії - Expand and Contract — міграція БД без downtime: додати колонку → мігрувати → видалити стару
- API Gateway може виконувати роль Version Router з Canary Release (10% на v2)
Часті уточнюючі запитання:
- Що ламає контракт API? — Видалення полів, зміна структури, нові обов’язкові поля, зміна типу даних
- Чому Media Type Versioning рідко використовується? — Складніше у реалізації і налагодженні, хоча ближче до духу REST
- Що таке Contract Testing? — Consumer описує очікування, Provider перевіряє сумісність при кожній збірці
- Як мігрувати БД при версіонуванні? — Expand and Contract: додаємо нове, мігруємо дані, видаляємо старе
Червоні прапорці (НЕ говорити):
- «Версія в URL — це антипатерн» — це найпоширеніший підхід в індустрії
- «Non-breaking changes не потребують версіонування» — навіть додавання поля може зламати клієнтів
- «Contract Testing замінює версіонування повністю» — дозволяє робити плавні оновлення, але не скасовує його
- «Media Type Versioning — найкращий вибір» — теоретично вірний, але на практиці рідко застосовується
Пов’язані теми:
- [[Що таке REST]]
- [[Що таке RESTful API дизайн]]
- [[Що таке Content-Type header]]
- [[Що таке Accept header]]
- [[Як правильно іменувати REST endpoints]]