GH GambleHub

Контрактная совместимость API

Зачем нужна контрактная совместимость

Контрактная совместимость — это способность API эволюционировать без поломки существующих интеграций. В растущих системах API меняются чаще кода клиентов; совместимость позволяет выпускать фичи итеративно, не устраивая «больших переездов».

Ключевая идея: контракт — первичен, изменения проходят по правилам совместимости и проверяются автоматически.

Базовые понятия

Контракт — формальная спецификация интерфейса: ресурсы/методы/события, схемы данных, коды ошибок, лимиты, SLA, требования безопасности.
Поставщик (provider) — владелец API. Потребитель (consumer) — клиент/интеграция.

Совместимость:
  • Backward: новый поставщик работает со старыми потребителями.
  • Forward: старый поставщик работает с новыми потребителями (обычно достигается «терпимыми читателями»).
  • Full: соблюдены и backward, и forward (самый сильный вариант).
  • Аддитивность — добавление необязательных элементов без ломки существующих.

Политика версионирования

Semantic Versioning (рекомендуется):
  • MAJOR — ломающие изменения (только при выпуске новой линии API: `/v2`, `service.v2`).
  • MINOR — аддитивные изменения (новые необязательные поля/методы).
  • PATCH — исправления без изменения контракта.
  • Deprecation Policy: объявление устаревших элементов, окно поддержки (sunset), предупреждения в заголовках/метаданных, план снятия.

Безопасные vs опасные изменения

Безопасные (обычно backward-compatible)

Добавление необязательного поля в JSON/Protobuf/Avro.
Добавление нового эндпоинта/метода/события.
Расширение enum новыми значениями, если потребители толерантны к неизвестным значениям.
Повышение лимитов (например, `maxItems`) без ужесточения минимальных.
Добавление nullable с корректными дефолтами.
Изменение текста описаний/примеров.

Опасные (ломают совместимость)

Переименование/удаление полей, изменение их типа или обязательности.
Смена семантики статус-кодов/ошибок (например, было `200`, стало `204` или `404`).
Изменение формата идентификаторов (UUID → int).
Ужесточение валидации (строже минимумы/паттерны) без версии.
Изменение порядка и структуры в gRPC-стримах/событиях.
Переиспользование номеров тегов в Protobuf для новых полей.

Совместимость по стилям взаимодействия

REST/HTTP + JSON Schema

Аддитивность: новые поля помечаем как `optional`/`nullable`.
Tolerant Reader у клиента: игнорировать неизвестные поля; не полагаться на порядок.
Версионирование: мажор — в пути (`/v2`) либо в медиатипе (`application/vnd.example.v2+json`).
ETag/If-Match: для безопасных апдейтов без гонок.
Ошибки: единый формат (`type`, `code`, `title`, `detail`, `trace_id`), не меняйте значения `code` без мажора.
Пагинация: курсоры предпочтительнее offset; добавляйте поля `next_cursor`, не меняйте смысл существующих.

gRPC / Protobuf

Нумерация тегов неизменна. Удаленные теги не переиспользовать.
Новые поля — `optional`/`repeated` с разумными дефолтами на сервере.
Не меняйте порядок и обязательность сообщений в streaming-RPC.
Статусы ошибок — стабильные (`INVALID_ARGUMENT`, `FAILED_PRECONDITION`, и т. п.); новая семантика → новая версия метода/сервиса.

Event-driven (Kafka/NATS/Pulsar) + Avro/JSON Schema

Именование событий: `domain.action.v{major}`.
Новые поля — опциональные; выделяйте ядро и обогащения (`.enriched`).
Регистры схем: правила совместимости (BACKWARD/FORWARD/FULL) на тему/событие.
Расширение enum допустимо при tolerant reader на стороне потребителей.
Смена ключа партиционирования/порядка для агрегата = ломающие изменения.

GraphQL

Добавление полей/типов — безопасно; удаление/переименование — только через @deprecated и окно миграции.
Не меняйте типы/не nullable без мажора.
Контролируйте complexity/depth — лимиты являются частью контракта.

Паттерны устойчивой эволюции

Additive-first: расширяйте, не ломая.
Capability negotiation: клиенты сообщают, что поддерживают (заголовки/параметры/договоренности), сервер подстраивается.
Границы контракта: фиксируйте MGC (минимальный гарантийный контракт) и отделяйте расширения (модель обратной пирамиды).
Tolerance by default: клиенты игнорируют лишнее и корректно обрабатывают неизвестные значения enum (fallback).
Dual-write / Dual-emit: при мажорных изменениях некоторое время выпускайте `v1` и `v2` параллельно.
Sunset headers/Events: заранее уведомляйте о снятии версий.

Governance и автоматизация

API-линтеры:
  • OpenAPI/Spectral: именование, пагинация, коды ошибок, форматы полей.
  • Buf/Protobuf: запрет переиспользования тегов, нотации пакетов.
  • AsyncAPI/Schema Registry: совместимость схем на уровне CI.
  • Каталог контрактов (SSOT): централизованный реестр схем/версий с историей диффов.
  • API Guild: гильдия/комитет, принимающий правила, шаблоны и review изменений.
  • Change Management: RFC/ADR, release notes, миграционные гайды.

Тестирование совместимости

Schema-diff в CI: блокируем ломающие изменения спек (OpenAPI-diff, Buf breaking, SR compatibility).
Consumer-Driven Contracts (CDC): Pact/похожие — проверка поставщика против контрактов конкретных потребителей.
Golden samples: эталонные запросы/ответы и события для регресса.
E2E Canary: раскатка на долю трафика/отдельные консюмер-группы.
Chaos/latency: проверка таймаутов/ретраев — изменение latency-SLO считается изменением контракта.

Миграции и депрекейт

1. Объявите депрекейт: пометьте элемент, укажите срок sunset и альтернативу.
2. Поддерживайте период совместимости: dual-write/dual-emit, мосты, адаптеры.
3. Соберите телеметрию: кто еще использует старое?
4. Коммуникация: рассылки, релиз-ноты, тестовые стенды.
5. Снятие: по истечении окна — удаление с фиксированным релизом.

Примеры изменений

REST

Было:
json
{ "id":"p1", "status":"authorized" }
Стало (аддитивно, безопасно):
json
{ "id":"p1", "status":"authorized", "risk_score": 0. 12 }

Клиенты, игнорирующие неизвестные поля, не ломаются.

Protobuf

proto message Payment {
string id = 1;
string status = 2; // don't change tag numbers optional double risk_score = 3; // additive
}

Event

`payment.authorized.v1` (ядро) + `payment.enriched.v1` (обогащение). Потребители критического пути читают ядро и не зависят от обогащений.

Антипаттерны

Swagger-wash: спецификация формально есть, но поведение сервиса расходится с ней.
Breaking by stealth: поменяли тип/статус/формат без новой версии и окна миграции.
Сырые CDC-события как публичный контракт: утечка схем БД, невозможность эволюции.
Жесткий клиент: падает при неизвестных полях/значениях; отсутствие tolerant reader.
Переиспользование protobuf-тегов: тихая коррупция данных.
Латентность как «неконтрактная»: неожиданно удлинили p95 — потребители ломаются по таймаутам.

Чек-лист совместимости (перед мерджем)

  • Изменения аддитивны (или мажорная версия подготовлена).
  • Линтеры/дифф-чеки пройдены, правила совместимости — зеленые.
  • Ошибки/коды/статусы не меняли семантику.
  • Enum расширены без запрета старых значений; клиенты — tolerant.
  • Границы MGC неизменны.
  • Обновлены примеры/документация/SDK.
  • Для мажора — план dual-write/dual-emit, sunset-дата, комм-план.
  • Тесты CDC/Golden/E2E пройдены.

FAQ

Чем backward отличается от forward совместимости?
Backward — новые серверы не ломают старых клиентов. Forward — новые клиенты не ломаются на старых серверах (через tolerant reader и аккуратные дефолты).

Когда все-таки делать `/v2`?
Когда меняются инварианты/семантика, удаляются поля/методы, требуется новая модель безопасности — проще и честнее запустить новую линию.

Можно ли жить без Schema Registry/линтеров?
Теоретически — да, практически — это частые регрессы и «скрытые» ломки. Автоматизация окупается.

Enum можно расширять?
Да, если клиенты корректно обрабатывают неизвестные значения (fallback/ignore). Иначе — мажор.

Итог

Контрактная совместимость — это правила + дисциплина + автоматизация. Проектируйте аддитивно, версионируйте ломающие изменения, применяйте tolerant reader, автоматом проверяйте диффы и CDC, планируйте депрекейт. Так API смогут быстро эволюционировать, а интеграции — оставаться стабильными.

Contact

Свяжитесь с нами

Обращайтесь по любым вопросам или за поддержкой.Мы всегда готовы помочь!

Telegram
@Gamble_GC
Начать интеграцию

Email — обязателен. Telegram или WhatsApp — по желанию.

Ваше имя необязательно
Email необязательно
Тема необязательно
Сообщение необязательно
Telegram необязательно
@
Если укажете Telegram — мы ответим и там, в дополнение к Email.
WhatsApp необязательно
Формат: +код страны и номер (например, +380XXXXXXXXX).

Нажимая кнопку, вы соглашаетесь на обработку данных.