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).

Натискаючи кнопку, ви погоджуєтесь на обробку даних.