Технологии и Инфраструктура → Версионирование API
Версионирование API
1) Зачем это нужно
Версионирование — это управляемый способ изменять контракты между сервисами и клиентами без поломок. В продуктах с большим числом интеграций (платежи, KYC/AML, игры, биллинг, отчетность) одновременно живут «старые» и «новые» клиенты. Правильная политика версии:- снижает риск инцидентов при релизах,
- позволяет планово вводить улучшения и безопасность,
- дает бизнесу предсказуемые сроки миграций партнеров.
2) Классификация изменений
PATCH (не ломающие): исправления без изменения контракта (добавление необязательных полей, фиксы валидации).
MINOR (расширяющие): back-compatible расширения (новые эндпоинты, поля с default).
MAJOR (ломающие): изменение структуры, семантики или удаление полей/эндпоинтов.
Рекомендуется SemVer `MAJOR.MINOR.PATCH` для артефактов (SDK/схемы), при этом «внешний» номер API может быть упрощен (v1, v2).
3) Модели версионирования REST
1. В URI:
`GET /v1/payments/{id}`
Плюсы: прозрачно, кэшируемо, легко маршрутизировать. Минусы: дублирование документации, сложнее тонко эволюционировать.
2. В заголовках (content negotiation):
`Accept: application/vnd.company.payments.v2+json`
Плюсы: гибкость, отсутствие «мусора» в URL, удобная эволюция медиатипа. Минусы: сложнее дебажить в браузере, нужен дисциплинированный клиент.
3. В кастомном заголовке:
`X-API-Version: 2` (или `Api-Version: 2025-09-01`)
Плюсы: просто на шлюзе. Минусы: нет стандартности, осторожно с кэшем.
4. Версия-дата (date-based):
Хорошо для финтех/отчетности: предсказуемые «разрезы» изменений (например, ежеквартальные).
5. Версионирование ресурса/модели:
Один и тот же эндпоинт может возвращать разные представления: `fields=...` или `profile=lite|full`. Это дополнение, не замена версионности.
Рекомендация: для внешних интеграций — `URI vN` + Deprecation/Sunset заголовки; для внутренних — можно `Accept` или заголовок версии, если шлюз и платформа это поддерживают.
4) GraphQL
Предпочтение — без глобальных версий. Эволюция через добавление полей/типов и деприкацию (`@deprecated(reason: "...")`).
Ломающие изменения — только в «больших» окнах (versioned schema namespace) с миграционным гайдом.
Следите за «n+1» и за тем, чтобы не менять meaning существующих полей.
5) gRPC / Protobuf
Номера полей неизменяемы. Удаленные поля помечайте как `reserved`.
Добавляйте поля как optional с безопасными default.
Используйте buf breaking/lint для автоматической проверки совместимости.
Версионируйте пакеты/модули: `package payments.v1;` → `payments.v2`.
6) Событийные API (EDA)
Схема события — такой же контракт. Храните ее в Schema Registry (Avro/JSON-Schema/Protobuf).
Топики и версии: `payments.v1.authorized`, `payments.v2.authorized`.
Ломающие изменения — новый тип события или новый топик.
Гарантии эволюции: backward-compatible для консумеров в течение периода LTS.
7) Политика деприкации и EOL
Внедрите прозрачные правила:- Deprecation: метки в changelog и в спецификациях (OpenAPI/GraphQL SDL), заголовок
- `Deprecation: true` и когда возможно `Sunset: Tue, 31 Mar 2026 00:00:00 GMT`.
- Коммуникации: email/портал партнера, webhooks-уведомления, release notes.
- Сроки: MINOR — 3–6 месяцев поддержки, MAJOR — 9–18 месяцев (зависит от критичности).
- Временные окна: фиксируйте в «Политике версионирования API».
8) Маршрутизация и релизы
API Gateway (Kong/Apigee/Nginx/Envoy): правила по префиксу `/v1/`, по заголовку или медиатипу.
Пример маршрута:
if ($http_accept ~ "vnd. company. payments. v2") { proxy_pass http://payments-v2; }
Canary/Blue-Green/Shadow: катите новую версию на 1–5% трафика, сравнивайте метрики и логи контрактных ошибок.
Feature Flags: скрываем поведение, не меняя контракт.
Zero-downtime миграции: при MAJOR обеспечьте двойную запись/чтение (dual-write/read) схемы данных.
9) Контракт-тестирование и контроль совместимости
OpenAPI Diff (или swagger-diff) — проверьте, что MINOR/PATCH не ломают схемы.
Spectral линтинг — стиль, обязательные метаданные (версия, Deprecation).
Consumer-Driven Contracts (Pact) — гарантирует, что провайдер не ломает клиентов.
buf breaking для protobuf.
CI должен падать при ломающих изменениях без повышения MAJOR.
10) Документация и SDK
Версионируйте спеки: `/docs/api/v1/openapi.json`, `/docs/api/v2/…`.
Генерируйте SDK по версиям (npm/maven/pypi) с SemVer и changelog.
Помечайте устаревшие методы в SDK аннотациями/Deprecated.
11) Observability по версиям
Собирайте метрики раздельно:- RPS/латентность/ошибки на версию (`api_version` лейбл).
- Карты использования эндпоинтов: кто еще на v1?
- Алерты: «>10% 4xx due to contract mismatch», «старые клиенты > X% после T-даты».
12) Кэширование и версии
Версия в пути улучшает кэшируемость CDN.
При заголовочных/медиатип версиях — внимательно с Vary:- `Vary: Accept, X-API-Version`.
- Не меняйте семантику ответа в MINOR так, чтобы ломать кеш-ключи.
13) Безопасность
Не шифруйте и не вшивайте версию в JWT— версию определяет запрос, а не токен.
Не раскрывайте внутренние номера сборок. Во внешних сообщениях используйте «v1/v2».
При MAJOR пересмотрите валидацию, лимиты, маскирование PII.
14) Миграции и авто-помощники
Публикуйте «Migration Guide v1 → v2»: таблицу соответствий полей, примеры запросов/ответов, edge-кейсы.
Предлагаете линтеры для клиентов (CLI), которые подсветят устаревшие поля.
Для крупных партнеров — sandbox с двумя версиями и тест-датасетом.
15) Анти-паттерны
«Вечный v1»: отсутствие дедлайнов и метрик использования.
Скрытые ломающие изменения в MINOR/PATCH.
«Версия в query string» (`?v=2`) — ломает кэш и читаемость.
«Один эндпоинт — сто значений флага»: трудно тестировать/документировать.
16) Чек-лист внедрения
1. Выбрана модель (URI/Accept/Header) для внешних и внутренних клиентов.
2. SemVer для спецификаций и SDK, автоматический breaking-check в CI.
3. Deprecation/Sunset политика и шаблоны коммуникаций.
4. Gateway-маршрутизация + канарейки, дашборды по версиям.
5. CDC/Contract tests на критичные интеграции (платежи, KYC, отчетность).
6. Документация/SDK/миграционный гид опубликованы одновременно с релизом.
7. План EOL с датами и ответственными.
17) Примеры
17.1 REST (URI + заголовки)
Запрос:
GET /v2/withdrawals/12345
Accept: application/json
Idempotency-Key: 4a1c-…-9f
Ответ:
json
{
"id": "12345",
"status": "PENDING_REVIEW",
"amount": {"value": "100. 00", "currency": "EUR"},
"limits": {"daily": "500. 00"},
"created_at": "2025-10-02T10:00:00Z",
"links": [{"rel": "cancel", "href": "/v2/withdrawals/12345/cancel", "method": "POST"}]
}
Заголовки деприкации (на v1):
Deprecation: true
Sunset: Tue, 31 Mar 2026 00:00:00 GMT
Link: </v2/docs>; rel="successor-version"
17.2 Content Negotiation (медиатип)
GET /payments/987
Accept: application/vnd. company. payments. v2+json
Vary: Accept
17.3 GraphQL (деприкация поля)
graphql type Payment {
id: ID!
amount: Money!
status: PaymentStatus!
method: PaymentMethod!
legacyCode: String @deprecated(reason: "Use field `method`")
}
17.4 gRPC (protobuf)
proto package payments. v2;
message Withdrawal {
string id = 1;
Money amount = 2;
string status = 3; // previously enum, now string with documented values reserved 4; // legacy field removed in v1 -> v2
}
17.5 Событийная модель
Топики:- `wallet.v1.balance.updated`
- `wallet.v2.balance.changed` (новое событие с расширенной схемой)
Схемы хранятся в Registry, продюсер не публикует события со схемой, нарушающей совместимость.
18) Контекст iGaming/финтех (практика)
Платежи: ввод v2 для новых PSP, где `status`/`decline_reason` расширены; на шлюзе маппинг v1 → v2 для отчетности.
KYC: MINOR добавляет поле `pep_screening`, клиенты v1 игнорируют, v2 — требует.
Ответственные игры/лимиты: MAJOR меняет модель лимитов (суточные/недельные). Двойной экспорт в отчетность до EOL v1.
Отчетность регуляторам: фиксированные версии-даты: `reporting-2025-01`.
19) Мини-политика (пример для wiki)
Модель: для внешних API — `URI /vN/`; для внутренних — `Accept: …vN+json` или `X-API-Version: N`.
SemVer: спецификации и SDK публикуются как `N.minor.patch`. MAJOR требует RFC/ADR.
Совместимость: MINOR/PATCH — без ломающих изменений. Ломающее → только через MAJOR.
Deprecation/EOL: объявление за ≥90 дней; Sunset-дата в заголовках; LTS-ветка для критичных клиентов.
Контроль: OpenAPI diff / buf breaking в CI, CDC для ключевых интеграций.
Observability: метрики/логи с лейблом `api_version`.
Релизы: canary 5% ≥ 24ч, затем поэтапно до 100% при зеленых метриках.
Итог
Версионирование — это не про «/v2 в URL», а про процесс: явные правила эволюции, автоматические проверки совместимости, управляемые релизы и уважение к интеграциям. Введите понятную политику, автоматизируйте контроль и наблюдаемость — и изменения перестанут быть угрозой для продукта.