API contract governance: контракт как закон для двух команд
Сцена · поле переименовали в чате
Утро, прод, у половины пользователей пустой экран вместо списка заказов.
Фронт ищет свой баг и не находит. К обеду выясняется: бэкенд вчера выкатил
«безобидный рефакторинг», поле total в ответе стало totalCount. В
чате даже было предупреждение, три дня назад, между мемом и обсуждением
обеда.
Разбор полётов упирается в стену. Бэкенд прав: они предупредили и улучшили имя. Фронт прав: никто не подписывался читать весь чат. Виноватых нет, потому что нет закона, который нарушили. Есть привычка «обычно оно так работает», а привычка не версионируется и не проверяется в CI.
Это последний урок раздела, и он про самый нагруженный документ из всех: контракт между двумя командами. Здесь сходится всё, что мы собирали: спека из урока 05, API-запись из урока 06, CI из урока 07 и postmortem из урока 08.
Карта урока · пять принципов
Контракт работает, когда выполняются пять условий. Они выросли из реальной практики, и каждое закрывает конкретный способ устроить сцену выше:
- Контракт обязывает обе стороны.
- Schema-first: сначала схема, потом код.
- Контракт описывает поведение, а не реализацию.
- У каждого API есть владелец, и он назначен по типу API.
- Автоматизация вместо доверия: что можно проверить в CI, проверяет CI.
Чем контракт между командами отличается от типа в коде
Мы уже встречали контракт в уроке про принципы архитектуры: форма плюс поведение плюс граница. Внутри одного репозитория его охраняет компилятор: переименовал поле, сборка упала, ты же её и починил. Цена нарушения секунды.
Контракт между командами живёт в другой весовой категории. Стороны деплоятся независимо, компилятора между ними нет, а цена нарушения измеряется чужим простоем. Поэтому контракт здесь перестаёт быть типом и становится законом: документом с владельцем, процедурой изменения и машинной проверкой. Governance в названии урока это ровно эти три вещи.
Принцип 1 · контракт обязывает обе стороны
Никто не меняет контракт в одностороннем порядке. Ни бэкенд «улучшая имена», ни фронт, начавший зависеть от поля, которое в схеме помечено как внутреннее.
Из этого следуют две процедуры:
- Breaking change не запрещён, он оформляется: предложение, окно депрекации, migration plan, согласие потребителей. Удаление поля это проект, а не коммит.
- Нарушение контракта это инцидент. Не «ну бывает», а полноценный incident record и blameless postmortem из прошлого урока: что в процессе позволило выкатить ломающее изменение без процедуры.
Жёсткость второго пункта не ради наказания. Пока нарушение контракта стоит дёшево, его будут совершать; когда оно автоматически порождает разбор, в процессе быстро появляются недостающие проверки.
Принцип 2 · schema-first
Схема пишется до кода. Не «код написали, схему сгенерировали для документации», а наоборот: schema-first: OpenAPI или Protobuf файл предлагается, ревьюится, утверждается, и только после этого стороны пишут реализацию.
Что это даёт:
- Ревью контракта отдельно от кода. Спор «нужно ли это поле» происходит над диффом схемы в десять строк, а не над PR на тысячу.
- Параллельная работа. Утверждённая схема позволяет фронту строить UI на моках, пока бэкенд пишет реализацию. Очередность исчезает.
- Кодген обеих сторон. Типы клиента и сервера, валидаторы и моки генерируются из одного исходника, расхождение становится технически невозможным. Инструменты кодгена разбираем в уроке про GraphQL, gRPC и кодоген.
Schema-first это тот же ход, что RFC перед решением в уроке 06: спор переносится в дешёвую фазу, пока ошибка стоит десять строк текста, а не неделю переписывания.
Принцип 3 · поведение, не реализация
Контракт описывает, что обещано наблюдателю, и молчит обо всём остальном. Две типовые утечки реализации в контракт:
- Бэкенд зеркалит базу. Ответ API повторяет таблицу:
user_id,created_ts, флаги, которые имеют смысл только внутри. Теперь любая миграция схемы БД это breaking change для клиентов. База должна уметь меняться, не двигая контракт. - Фронт диктует внутренности. «Сделайте нам эндпоинт, который ходит в эти три таблицы и возвращает join» это требование к реализации. Корректная форма: «нам нужны такие-то данные на такой-то экран с такой задержкой», а как бэкенд их соберёт, его дело.
Проверка на утечку: можно ли полностью переписать реализацию (сменить базу, язык, разбить сервис надвое), не тронув контракт? Если нет, в контракте торчит реализация.
дальше после входа
Урок целиком открыт всем, кто вошёл. Войди по своему email, чтобы читать дальше.
Вход пока по приглашению. Если хочешь учиться, напиши Vassiliy.