GH GambleHub

Прямая совместимость

Что такое прямая совместимость

Прямая совместимость (forward compatibility) — это способность системы корректно работать с более новыми клиентами или данными, чем те, под которые она изначально проектировалась. Проще: старый сервер не ломается, когда к нему приходит новый клиент; старый потребитель не падает, когда встречает новое сообщение.

От обратной совместимости (когда новая система поддерживает старых клиентов) forward отличается направлением ответственности: мы проектируем протоколы и клиенты так, чтобы «пережить» будущие расширения без тотального апгрейда всей экосистемы.

Базовые принципы

1. Tolerant reader & tolerant writer

Reader игнорирует неизвестные поля/заголовки и допускает новые enum-значения с корректным fallback.
Writer не отправляет ничего, что сервер явно не объявил как поддерживаемое (capabilities).

2. Capability negotiation

Явный обмен возможностями (фичи/версии/медиа-типы) на handshake-этапе. Клиент адаптирует свое поведение к ответу сервера.

3. Дефолтная деградация

Новые возможности считаются опциональными: если сервер/консьюмер их не поддерживает, сценарий все равно завершится полезным минимумом (MGC).

4. Стабильное ядро (MGC)

Минимальный гарантийный контракт неизменен; новшества живут как расширения.

5. Контракты ошибок как часть протокола

Предсказуемые коды/причины («фича не поддерживается», «медиа-тип неизвестен») позволяют клиенту автоматически откатиться на поддерживаемый режим.

6. Версии без сюрпризов

Мажорные линии отделены; минорные расширения не требуют обновления сервера/консьюмера.

Где это особенно важно

Публичные API с долгоживущими интеграциями (партнеры, SDK в мобильных приложениях).
Событийные платформы с множеством независимых консьюмеров.
Мобильные клиенты, которые обновляются медленнее, чем бэкенд.
Эдж/IoT, где часть парка устройств редко прошивается.

Паттерны реализации по стилям

REST/HTTP

Negotiation:
  • `Accept`/медиатипы с параметрами (`application/vnd.example.order+json;v=1;profile=risk`).
  • `Prefer: include=...` для опциональных блоков.
  • Заголовок `X-Capabilities: risk_score,item_details_v2`.
Поведение клиента:
  • Отправляет запрос в базовом формате, расширения — только если сервер подтвердил capability (через OPTIONS/desc/лид-эндпоинт).
  • При `415/406/501` автоматически откатывается на поддерживаемый формат/метод.
  • Ответ сервера: неизвестные параметры — игнорировать; лишние поля — допускаются; стабилен формат ошибок (`type/code/detail/trace_id`).

gRPC / Protobuf

Стабильные сервисы: новые методы/поля — аддитивно; старый сервер спокойно игнорирует неизвестные поля запроса.
Feature discovery: метод `GetCapabilities()` возвращает списки фич/лимитов. Клиент не вызывает «v2-метод», если сервер его не объявил.
Стриминг: фиксируйте порядок минимального набора сообщений; новые «фреймы» помечайте расширениями/типами, которые старый клиент игнорирует.

GraphQL

Forward-friendly: новые поля/типы появляются на сервере — старые клиенты их просто не запрашивают.
Догадки запрещены: клиент должен держать схему (интроспекция/кодоген) и не отправлять неизвестные директивы/переменные.
Деградация: если сервер не знает кастомной директивы/feature — клиент строит запрос без нее.

Event-driven (Kafka/NATS/Pulsar, Avro/JSON/Proto)

FORWARD-совместимость схемы в реестре: старые консьюмеры могут читать сообщения, записанные новой схемой.
Аддитивные поля с дефолтами: новые продюсеры не ломают старых консьюмеров.
Core vs Enriched: ядро остается прежним, новые сведения публикуются в `.enriched` или как опциональные поля.

Практики проектирования

1. Договор на минимальный запрос (MGC)

Операция должна иметь «узкую шейку», которую поддержат все серверы много лет.

2. Фича-флаги на уровне контракта

Опишите фичи как именованные возможности: `risk_score`, `pricing_v2`, `strong_idempotency`. Клиент включает их явно.

3. Явные коды ошибок для «не поддерживается»

HTTP: `501 Not Implemented`, `415 Unsupported Media Type`, детальные `problem+json`.
gRPC: `UNIMPLEMENTED`/`FAILED_PRECONDITION`.
Events: маршрут в DLQ с `reason=unsupported_feature`.

4. Не полагаться на порядок/полные списки

Клиент должен быть готов к новым значениям enum, отсутствию новых полей и к «дополнительным» свойствам.

5. Стабильные идентификаторы и форматы

Не меняйте формат ID/ключей партиционирования в рамках линии — это ломает forward на стороне читателей.

6. Документация «машиночитаемая»

Хостите дескрипторы: OpenAPI/AsyncAPI/Proto descriptors/GraphQL SDL. Клиенты могут сверить поддержку фич.

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

Schema-diff в режиме FORWARD/FULL: новая схема валидирует старого потребителя/сервер.
Контрактные тесты клиента: новый клиент исполняется против старого сервера со включенными/выключенными фичами.
Golden requests: набор «новых» запросов прогоняется по «старому» серверу; ожидается деградация без критических ошибок.
Chaos/latency: проверка таймаутов/ретраев — новый клиент должен корректно пережить худшие SLA старого сервера.
Canary: часть новых клиентов работает с предыдущей серверной версией — собираем телеметрию ошибок/деградаций.

Наблюдаемость и операционные метрики

Доля запросов/сообщений с неподдержанными фичами и их автоматическими откатами.
Распределение по версиям клиентов (User-Agent/метаданные/claims).
Ошибки `UNIMPLEMENTED/501/415` и маршруты в DLQ с `unsupported_feature`.
Время деградации: p95/p99 для MGC против «расширенного» ответа.

Режимы совместимости в реестре схем

FORWARD: новая запись совместима со старым читателем (нужны дефолты, опциональность).
FULL: и FORWARD, и BACKWARD; удобно для публичных контрактов.
Рекомендация: для событий — BACKWARD у продюсера и FORWARD у консьюмера (через tolerant reader), для внешних API — FULL.

Примеры

REST (capabilities + деградация)

1. Клиент делает `GET /meta/capabilities` → `{ "risk_score": false, "price_v2": true }`.
2. На `POST /orders` отправляет базовые поля; `risk_score` не запрашивает, потому что сервер его не умеет.
3. Если случайно отправил `Prefer: include=risk_score`, сервер отвечает 200 без поля `risk_score` (или `Preference-Applied: none`) — клиент не падает.

gRPC (discovery)

`GetCapabilities()` вернул список методов/фич. Клиент не вызывает `CaptureV2`, если его нет — вместо этого использует `Capture` и локально преобразует входные данные до поддерживаемого вида.

Events (FORWARD в реестре)

Продюсер добавил поле `risk_score` (nullable с дефолтом). Старый консьюмер его игнорирует; его логика использует только стабильные поля ядра.

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

Жесткий клиент: фильтрует ответ по whitelist-полям и падает на незнакомом свойстве.
Неявные фичи: клиент начинает отправлять новый параметр без проверки capabilities.
Смена форматов ID/ключей в пределах линии → старые серверы/консьюмеры перестают понимать новые запросы/сообщения.
Зашитые предположения о полном списке enum (switch без default).
Логирование как контроль потока: парсинг строк ошибок вместо контрактных кодов.

Чек-лист внедрения

  • Определен MGC; новые возможности помечены как опциональные.
  • Описан и реализован capability negotiation (эндпоинт/метаданные/handshake).
  • Клиенты игнорируют незнакомые поля и корректно обрабатывают новые enum (fallback).
  • Контракты ошибок фиксируют «не поддерживается» предсказуемо (HTTP/gRPC/Event).
  • Реестр схем настроен на FORWARD/FULL для соответствующих артефактов.
  • Автотесты: schema-diff (FORWARD), контрактные тесты клиента против старого сервера, canary.
  • Метрики: версия клиента, отказ фич, доля деградаций, p95 MGC.
  • Документация/SDK публикуют список фич и примеры деградации.

FAQ

Чем forward отличается от backward на практике?
Backward: новый сервер не ломает старых клиентов. Forward: старый сервер не ломается от новых клиентов (или старый консьюмер — от новых сообщений). В идеале вы достигаете full.

Нужно ли всегда вводить capabilities?
Если ожидаете активную эволюцию без синхронных релизов — да. Это дешевле, чем держать десятки major-линий.

Как быть с безопасностью?
Новые фичи должны требовать отдельные scopes/claims. Если сервер их не поддерживает — клиент не должен снижать безопасность, а должен отказаться от фичи.

Можно ли «угадывать» поддержку по версии сервера?
Нежелательно. Лучше спрашивать явно (capabilities) или смотреть на медиатип/схему.

Итог

Прямая совместимость — это дисциплина договариваться о возможностях и безопасно деградировать. Стабильное ядро, capability negotiation, аддитивные расширения и предсказуемые ошибки позволяют новым клиентам и данным уживаться со старыми серверами и потребителями — без массовых релизов и ночных миграций.

Contact

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

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

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

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

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

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