awesome-everything EN
↑ Обратно к восхождению

API

OpenAPI: сделать контракт источником истины, а не доками

Суть OpenAPI окупается, только когда спеку проверяют — генерируют клиентов, валидируют запросы и роняют CI на ломающем диффе. Спека, которую никто не проверяет, — это документация, которая врёт.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на junior-высоте — поверхность
◷ 16 min

Бэкендер добавляет поле tenantId в payload создания заказа и помечает его required — изменение в одну строку, выкатил в пятницу. К понедельнику три мобильных клиента, партнёрская интеграция и внутренняя админка возвращают 422 на каждый заказ. Никто из них не слал tenantId; никто не знал, что оно есть. Контракт говорил, что поле опционально вчера и обязательно сегодня, и ничто в пайплайне этого не заметило. «Доки» были страницей в Confluence, отредактированной полгода назад. Настоящий контракт жил у кого-то в голове и поменялся, никому не сказав.

Спека — это контракт, иначе фикция

OpenAPI — это машиночитаемое описание твоего HTTP API: каждый путь, форма каждого запроса и ответа, каждый статус-код, каждая схема авторизации, записанные в YAML или JSON. Столько у большинства команд есть. Вопрос, который решает, помогает оно или вредит, лежит на уровень глубже: спека — это источник истины или описание истины, записанное где-то ещё?

Когда спека — побочный документ (написали один раз, выгрузили на портал доков, никогда не сверяли с работающим сервисом), она гниёт за месяцы. Код меняется, спека — нет, и теперь у тебя два контракта: тот, что сервер обеспечивает, и тот, что заявляет спека. Клиенты интегрируются по спеке, спека — фикция, и интеграция падает в проде. Это contract drift, и это исход по умолчанию, пока что-то активно ему не мешает.

Фикс — не «писать доки лучше». Фикс — сделать спеку несущей: код генерируется из неё, запросы валидируются по ней, а её дифф гейтит каждый pull request. Контракт, который нельзя нарушить без падения билда, — это контракт. Всё остальное — страница в вики.

Spec-first vs code-first: где живёт истина

Получить спеку можно двумя способами, и разница в том, какой артефакт авторитетен.

Spec-first (design-first): ты пишешь OpenAPI-документ руками, ревьюишь его как дизайн API и генерируешь серверные заглушки, типизированных клиентов и моки из него. Спека — источник истины; код ей соответствует. Фронтенд может строить против сгенерированного мока в тот же день, когда бэкенд только начал, потому что контракт существует раньше любой реализации.

Code-first: ты пишешь сервер с аннотациями или декораторами (@ApiProperty, type hints в FastAPI, springdoc), а инструмент генерирует спеку из работающего кода. Код — источник истины; спека — его отражение. Меньше церемоний, и спека не разойдётся с хендлером, из которого её извлекли, — но может разойтись с тем, чего ждали потребители, потому что никто не отревьюил контракт как контракт до выкатки.

Ни то ни другое не ошибка. Решение сеньора такое: spec-first, когда API потребляют несколько команд или внешние партнёры и контракт нужно ревьюить и держать стабильным; code-first, когда одна команда владеет обоими концами и скорость важнее предварительного дизайна. Что не опционально в любой модели — это enforcement: спека должна валидироваться и диффиться, как бы она ни родилась.

ИзмерениеSpec-firstCode-first
Источник истиныНаписанная руками спекаКод сервера + аннотации
Контракт ревьюят до кода?Да — это дизайн-артефактНет — рождается из реализации
Параллельная работа клиент/серверС первого дня (мок из спеки)После компиляции сервера
Риск driftСпека vs реализация (валидируй на edge)Контракт vs ожидания потребителя
Лучше всего дляПубличные API, много потребителейОдна команда владеет обоими концами

Что реально даёт OpenAPI 3.1

Спека отрабатывает не самим документом, а тулингом, который открывает. Важны четыре выигрыша:

  • Сгенерированные типизированные клиенты и серверные заглушки. Прогони генератор по спеке и получи SDK на TypeScript, Go, Swift, Kotlin — с методами, моделями, типами ошибок. Класс интеграционных багов («я слал userId, серверу нужен user_id») исчезает, потому что запрос больше никто не пишет руками. Команды, выкатывающие публичные API, генерируют SDK из спеки именно чтобы убить эту категорию целиком.
  • Валидация запросов на edge. Middleware валидирует входящие запросы по спеке до того, как они дойдут до хендлера. Битое тело отклоняется точным 400 с именем виноватого поля, а твоя бизнес-логика мусора не видит.
  • Доки, которые не разойдутся молча. Когда отрендеренные доки генерируются из той же спеки, что гейтит CI, «доки врут» перестаёт быть возможным — доки и есть контракт.
  • Мок-серверы. Инструмент отдаёт фейковые ответы прямо из примеров спеки, и потребители интегрируются раньше, чем появился бэкенд.

Конкретно OpenAPI 3.1 выравнивает язык схем с JSON Schema 2020-12, так что твои ключевые слова валидации — это настоящий JSON Schema, а не подмножество эпохи 3.0. Цена этой мощи: nullable: true больше нет — теперь пишешь type: ["string", "null"] — а exclusiveMinimum/exclusiveMaximum принимают число, а не булево. 3.1 ещё добавляет верхнеуровневый webhooks для документирования событий, которые твой API шлёт, а не только принимает. Многие инструменты по-прежнему дефолтят в 3.0, так что проверь поддержку генератора до апгрейда.

Почему это работает

Генераторы кода не магия — они кодируют мнения. Спека с рыхлыми схемами (additionalProperties: true, без массивов required, нетипизированные object-блобы) генерирует рыхлых, нашпигованных any клиентов, что убивает смысл. Дисциплина, которая делает генерацию ценной, — это плотные схемы: явные типы, явный required, переиспользуемые компоненты. Генератор честен ровно настолько, насколько честен контракт, который ты ему скармливаешь.

Детект ломающих изменений: гейт, который ловит пятничный деплой

У истории с tenantId один чистый фикс: инструмент, который диффит новую спеку против предыдущей версии и роняет PR на ломающем изменении. oasdiff — частый выбор: он классифицирует 450+ категорий изменений и знает, какие ломающие. Добавление required-поля запроса, удаление поля ответа, которое клиент может читать, сужение типа, удаление значения enum, удаление эндпоинта — всё ломающее, всё ловится до мержа.

Вшитый в CI, чек туп и эффективен: пайплайн диффит спеку из PR против main, и если появляется ломающее изменение, билд красный, а инженер видит ровно какое поле сломалось — раньше любого клиента. Пятничный деплой не доходит до прода, потому что контракт обеспечивается тем же гейтом, что и тесты. Тонкость в 3.1: так как nullability переехала в массив типов, удаление "null" из type: ["string", "null"] регистрируется как смена типа, а не как смена nullable — дифф всё равно ловит это, просто по другому правилу, поэтому смотри на категорию, которую сообщает инструмент, а не только на количество.

Выбери лучший вариант

Публичный API потребляют мобильные приложения, которые ты не можешь принудительно обновить, и три партнёрские интеграции. Выбери стратегию enforcement.

Викторина

PR добавляет новое required-поле в тело запроса. Что должен сделать здоровый API-пайплайн?

Викторина

Твои написанные руками доки говорят, что поле опционально, но сервер теперь отклоняет запросы без него. Как это называется и что это предотвращает?

Расставь шаги по порядку

Расставь шаги spec-first пайплайна, который предотвращает drift:

  1. 1 Написать / обновить OpenAPI-спеку и отревьюить её как контракт
  2. 2 CI диффит спеку против предыдущей версии (oasdiff) и роняет билд на ломающем изменении
  3. 3 Сгенерировать типизированных клиентов, серверные заглушки и мок из спеки
  4. 4 Валидировать входящие запросы по спеке на edge до того, как отработает хендлер
  5. 5 Рендерить доки из той же спеки, чтобы они не могли противоречить контракту
Вспомните перед уходом
  1. 01
    Коллега говорит: «у нас уже есть OpenAPI-спека, значит, мы защищены от ломающих изменений». Почему это не так и чего на самом деле не хватает?
  2. 02
    Когда выбрать code-first вместо spec-first, и какой enforcement всё равно нужен в любом случае?
Итог

OpenAPI — это машиночитаемое описание HTTP API, но описание бесполезно, пока оно не источник истины. Сбойный режим — contract drift: написанные руками доки говорят одно, сервер обеспечивает другое, клиенты интегрируются против фикции, и изменение в одну строку вроде добавления required-поля ломает каждого потребителя в проде. Spec-first считает написанную руками спеку авторитетной и генерирует из неё клиентов, заглушки и моки; code-first генерирует спеку из аннотаций. В любом случае спеку надо обеспечивать — сгенерированные типизированные клиенты убивают целый класс багов с запросами вручную, валидация на edge отклоняет битый ввод до хендлера, а дифф ломающих изменений (oasdiff) роняет PR в тот момент, когда кто-то удаляет поле, сужает тип или добавляет required. OpenAPI 3.1 выравнивает схемы с JSON Schema 2020-12, выкидывает nullable ради массивов type и добавляет webhooks. Уплотни схемы переиспользуемыми $ref-компонентами, явным required и схемами авторизации, а затем дай пайплайну сделать контракт невозможным сломать молча.

Продолжить восхождение ↑OpenAPI: тест с выбором ответа
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.