Узнайте, как генерировать OpenAPI из описания поведения с Claude Code, сравнивать спецификацию с реализацией API и создавать простые примеры валидации для клиента и сервера.

OpenAPI — это общее описание вашего API: какие есть эндпоинты, что вы отправляете, что возвращаете и как выглядят ошибки. Это соглашение между сервером и любым, кто вызывает его (веб‑приложение, мобильное приложение или другой сервис).
Проблема — дрейф. Рабочий API меняется, а спецификация остаётся прежней. Или спецификация «причищается», чтобы выглядеть красивее, чем реальность, в то время как реализация продолжает возвращать странные поля, пропускать статус‑коды или иметь несовместимые формы ошибок. Со временем люди перестают доверять OpenAPI‑файлу, и он становится ещё одним документом, который все игнорируют.
Дрейф обычно вызывает обычное рабочее давление: быстрый фикс уходит в прод без обновления спека, добавлено новое опциональное поле «временно», пагинация эволюционирует, или команды обновляют разные «источники правды» (backend‑код, коллекция Postman и файл OpenAPI).
Справиться с этим честно — значит привести спек в соответствие с реальным поведением. Если API иногда возвращает 409 при конфликте, это должно быть в контракте. Если поле может быть null, укажите это. Если требуется аутентификация, не оставляйте это неясным.
Хороший рабочий процесс даёт вам:
Последний пункт важен, потому что контракт помогает только тогда, когда он соблюдается. Честный спек плюс повторяемые проверки превращает «документацию API» в инструмент, на который команды могут полагаться.
Если вы начнёте с чтения кода или копирования маршрутов, ваш OpenAPI опишет то, что есть сегодня, включая странности, которые вы, возможно, не хотите обещать. Вместо этого опишите, что API должен делать для вызывающей стороны, а затем используйте спек, чтобы проверить, что реализация совпадает.
Прежде чем писать YAML или JSON, соберите небольшой набор фактов для каждого эндпоинта:
Затем опишите поведение через примеры. Примеры заставляют быть конкретными и упрощают создание согласованного контракта.
Для Tasks API пример «счастливого пути» может звучать так: «Создать задачу с title и получить обратно id, title, status и createdAt.» Добавьте распространённые ошибки: «Отсутствие title возвращает 400 с {\"error\":\"title is required\"}» и «Отсутствие авторизации возвращает 401». Если вы уже знаете крайние кейсы, включите их: разрешены ли дублирующиеся заголовки, что происходит, если ID задачи не существует.
Фиксируйте правила простыми предложениями, которые не зависят от деталей кода:
title обязателен и имеет длину 1–120 символов.limit (макс 200).dueDate — дата‑время в формате ISO 8601.Наконец, определите объём v1. Если не уверены, держите v1 маленьким и понятным (create, read, list, update status). Оставьте поиск, массовые обновления и сложные фильтры на потом, чтобы контракт оставался правдопodobным.
Перед тем как просить Claude Code составить спек, запишите заметки о поведении в маленьком, повторяемом формате. Цель — затруднить случайные «догадки» при заполнении пробелов.
Хороший шаблон достаточно короткий, чтобы вы им действительно пользовались, но и достаточно последовательный, чтобы два человека описали один и тот же эндпоинт схожим образом. Фокусируйтесь на том, что делает API, а не на том, как он реализован.
Use one block per endpoint:
METHOD + PATH:
Purpose (1 sentence):
Auth:
Request:
- Query:
- Headers:
- Body example (JSON):
Responses:
- 200 OK example (JSON):
- 4xx example (status + JSON):
Edge cases:
Data types (human terms):
Напишите хотя бы один конкретный запрос и два ответа. Включите статус‑коды и реалистичные JSON‑тела с реальными именами полей. Если поле опционально, покажите пример, где оно отсутствует.
Явно указывайте крайние случаи. Именно они чаще всего приводят к тому, что спецификации становятся неверными: пустые результаты, неверные ID (400 vs 404), дубликаты (409 vs идемпотентное поведение), ошибки валидации и лимиты пагинации.
Также укажите типы данных простыми словами перед тем, как думать о схемах: строки vs числа, date‑time форматы, булевы значения и перечисления (список допустимых значений). Это предотвращает «красивую» схему, не совпадающую с реальными полезными нагрузками.
Claude Code работает лучше, если вы относитесь к нему как к аккуратному писцу. Дайте ему ваши заметки о поведении и строгие правила для формы OpenAPI. Если просто сказать «напиши OpenAPI spec», вы часто получите догадки, несоответствующее именование и пропущенные случаи ошибок.
Вставьте заметки о поведении сначала, затем добавьте короткий блок инструкций. Практический промпт выглядит так:
You are generating an OpenAPI 3.1 YAML spec.
Source of truth: the behavior notes below. Do not invent endpoints or fields.
If anything is unclear, list it under ASSUMPTIONS and leave TODO markers in the spec.
Requirements:
- Include: info, servers (placeholder), tags, paths, components/schemas, components/securitySchemes.
- For each operation: operationId, tags, summary, description, parameters, requestBody (when needed), responses.
- Model errors consistently with a reusable Error schema and reference it in 4xx/5xx responses.
- Keep naming consistent: PascalCase schema names, lowerCamelCase fields, stable operationId pattern.
Behavior notes:
[PASTE YOUR NOTES HERE]
Output only the OpenAPI YAML, then a short ASSUMPTIONS list.
После получения черновика сначала просмотрите ASSUMPTIONS. Там решается, честный ли будет спек. Утвердите правильное, исправьте неправильное и прогоните снова с обновлёнными заметками.
Чтобы держать имена согласованными, укажите соглашения заранее и придерживайтесь их. Например: стабильный шаблон operationId, имена тегов только существительные, единичные имена схем, одна общая схема Error и одно имя схемы аутентификации, используемое везде.
Если вы работаете в workflow‑среде вроде Koder.ai, полезно сохранить YAML как настоящий файл рано и итеративно править в небольших диффах. Так видно, какие изменения пришли из одобренных решений по поведению, а какие — из догадок модели.
Прежде чем сравнивать с продакшеном, убедитесь, что OpenAPI файл внутренне согласован. Это самое быстрое место, где ловятся желаемые формулировки и расплывчатости.
Прочитайте каждый эндпоинт как разработчик клиента. Сконцентрируйтесь на том, что вызывающий обязан отправить и на что он может рассчитывать в ответе.
Практический проход для проверки:
201, а не 200). Выберите 400 vs 422 и придерживайтесь.Ошибки заслуживают особого внимания. Выберите одну общую форму и переиспользуйте её везде. Некоторые команды держат это очень просто ({ error: string }), другие используют объект ({ error: { code, message, details } }). Любой вариант работает, но не смешивайте их по разным эндпоинтам и примерам. Если смешаете, клиентский код быстро наполнится особыми случаями.
Хороший сценарий для проверки: если POST /tasks требует title, схема должна пометить это как required, ответ с ошибкой должен показать фактическую форму ошибки, которую вы возвращаете, и операция должна ясно указывать, требуется ли авторизация.
Когда спек читается как задуманное поведение, воспринимайте работающий API как правду того, что видят клиенты сегодня. Цель не «победить» между спеком и кодом, а выявлять различия рано и принимать ясные решения по каждому элементу.
Для первого прохода реальные примеры запросов/ответов обычно проще всего. Подходят также логи и автоматизированные тесты, если они надёжны.
Следите за распространёнными несовпадениями: эндпоинты есть в одном месте, но отсутствуют в другом; различия в названиях или формах полей; различия в статус‑кодах (200 vs 201, 400 vs 422); недокументированное поведение (пагинация, сортировка, фильтры); и различия в авторизации (спек говорит public, а код требует токен).
Пример: в OpenAPI сказано, что POST /tasks возвращает 201 с {id,title}. Вы вызываете рабочий API и получаете 200 плюс {id,title,createdAt}. Это не «приблизительно то же самое», если вы генерируете SDK из спека.
Прежде чем менять что‑то, решите, как вы будете разрешать конфликты:
Держите изменения маленькими и обзорными: один эндпоинт, один ответ, одно изменение схемы. Так легче ревьюить и ретестить.
Когда у вас есть спек, которому вы доверяете, превратите его в маленькие примеры валидации. Это то, что предотвращает возврат дрейфа.
На сервере валидация означает быстро отклонять запросы, которые не соответствуют контракту, и возвращать понятную ошибку. Это защищает ваши данные и упрощает debugging.
Простой способ выразить серверные примеры валидации — записывать их как кейсы из трёх частей: ввод, ожидаемый вывод и ожидаемая ошибка (код ошибки или паттерн сообщения, не точный текст).
Пример (контракт говорит, что title обязателен и от 1 до 120 символов):
{
"name": "Create task without title returns 400",
"request": {"method": "POST", "path": "/tasks", "body": {"title": ""}},
"expect": {"status": 400, "body": {"error": {"code": "VALIDATION_ERROR"}}}
}
На клиенте валидация — это умение заметить дрейф до того, как пострадают пользователи. Если сервер начнёт возвращать другую форму, или обязательное поле исчезнет, тесты клиента должны это зафиксировать.
Держите клиентские проверки сфокусированными на том, от чего вы действительно зависите, например «у задачи есть id, title, status». Избегайте утверждений обо всех опциональных полях или точном порядке. Вы хотите, чтобы тесты падали на ломания, а не на безвредные дополнения.
Несколько правил, которые делают тесты читабельными:
Если вы работаете в Koder.ai, вы можете хранить эти примеры рядом с OpenAPI файлом и обновлять их в том же ревью, когда поведение меняется.
Представьте небольшой API с тремя эндпоинтами: POST /tasks создаёт задачу, GET /tasks возвращает список, GET /tasks/{id} возвращает одну задачу.
Начните с пары конкретных примеров для одного эндпоинта, как будто объясняете тестировщику.
Для POST /tasks подразумеваемое поведение может быть таким:
{ \"title\": \"Buy milk\" } и получить 201 с новым объектом задачи, включающим id, title и done:false.{} и получить 400 с ошибкой вроде { \"error\": \"title is required\" }.{ \"title\": \"x\" } (слишком короткий) и получить 422 с { \"error\": \"title must be at least 3 characters\" }.Когда Claude Code генерирует OpenAPI, фрагмент для этого эндпоинта должен захватывать схему, статус‑коды и реалистичные примеры:
paths:
/tasks:
post:
summary: Create a task
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateTaskRequest'
examples:
ok:
value: { "title": "Buy milk" }
responses:
'201':
description: Created
content:
application/json:
schema:
$ref: '#/components/schemas/Task'
examples:
created:
value: { "id": "t_123", "title": "Buy milk", "done": false }
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
missingTitle:
value: { "error": "title is required" }
'422':
description: Unprocessable Entity
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
tooShort:
value: { "error": "title must be at least 3 characters" }
Типичное несоответствие может быть тонким: рабочий API возвращает 200 вместо 201, или возвращает { "taskId": 123 } вместо { "id": "t_123" }. Это такие «почти то же самое» различия, которые ломают сгенерированные клиенты.
Исправляйте, выбрав один источник правды. Если задуманное поведение верно — измените реализацию, чтобы вернуть 201 и согласованную форму Task. Если продакшен‑поведение уже зависит пользователями, обновите спек (и заметки о поведении), затем добавьте недостающие проверки и ответы с ошибками, чтобы клиенты не удивлялись.
Контракт перестаёт быть честным, когда он перестаёт описывать правила и начинает описывать то, что API вернул в какой‑то удачный день. Простой тест: сможет ли новая реализация пройти этот спек, не скопировав сегодняшние странности?
Одна ловушка — переобучение на конкретный ответ. Вы захватили один ответ и сделали из него закон. Пример: API сейчас всегда отдаёт dueDate: null, и вы записали в спеке, что поле всегда nullable. Но реальное правило могло бы быть «обязательно, когда status == scheduled». Контракт должен выражать правило, а не текущее состояние данных.
Ошибки — место, где честность часто рушится. Соблазнительно специфицировать только успешные ответы, потому что они выглядят аккуратно. Но клиентам нужны базовые вещи: 401 при отсутствии токена, 403 для запрета доступа, 404 для неизвестного ID и согласованная ошибка валидации (400 или 422).
Другие проблемные паттерны:
taskId в одном месте, id в другом; или priority как строка в одном ответе и как число в другом).string, всё становится опциональным).Хороший контракт — тестируемый. Если вы не можете написать фейлящий тест по спеку, значит он ещё не честен.
Прежде чем отдать OpenAPI другой команде (или вставить в документацию), быстро проверьте: сможет ли кто‑то использовать этот файл без вчитывания в ваши мысли?
Начните с примеров. Спек может быть валидной и при этом бесполезной, если каждый запрос и ответ абстрактны. Для каждой операции включите хотя бы один реалистичный пример запроса и один пример успешного ответа. Для ошибок обычно достаточно одного примера на распространённый случай (auth, валидация).
Затем проверьте согласованность. Если в одном эндпоинте ответ { "error": "..." }, а в другом { "message": "..." }, то у клиентов будет ветвящаяся логика повсюду. Выберите одну форму ошибки и используйте её вместе с предсказуемыми статус‑кодами.
Короткий чеклист:
Практический трюк: выберите один эндпоинт, притворитесь, что вы никогда не видели API, и ответьте на вопрос «Что я отправляю, что я получаю и что ломается?» Если OpenAPI не даст чёткого ответа, он ещё не готов.
Этот рабочий процесс окупается, когда выполняется регулярно, а не только во время релиза. Выберите простое правило и держитесь его: запускайте его при каждом изменении эндпоинта и снова перед публикацией обновлённого спека.
Упростите владение. Тот, кто меняет эндпоинт, обновляет заметки о поведении и черновик спека. Второй человек делает ревью «спек vs реализация», как обычный code review. QA или поддержка часто отлично подходят в роли ревьюеров — они замечают неясные ответы и крайние случаи.
Обращайтесь с правками контракта как с кодом. Если вы используете чат‑драйвенный билд, например Koder.ai, снимайте состояние перед рискованными правками и используйте откаты при необходимости. Koder.ai также позволяет экспортировать исходники, что упрощает хранение спека и реализации рядом в репозитории.
Рутина, которая обычно работает без замедления команд:
Следующее действие: выберите один уже существующий эндпоинт. Напишите 5–10 строк заметок о поведении (входы, выходы, ошибки), сгенерируйте черновой OpenAPI из этих заметок, валидируйте его, затем сравните с работающей реализацией. Исправьте одно несоответствие, протестируйте снова и повторите. После одного эндпоинта привычка обычно закрепляется.
OpenAPI drift — это ситуация, когда реальный API больше не соответствует файлу OpenAPI, который делятся команды. Спек может не учитывать новые поля, статус-коды или правила аутентификации, или описывать «идеальное» поведение, которого сервер на самом деле не придерживается.
Это важно, потому что клиенты (приложения, другие сервисы, сгенерированные SDK, тесты) принимают решения, опираясь на контракт, а не на то, как сервер «обычно» себя ведёт.
Драйв проявляется в случайных и труднопояснимых сбоях на стороне клиента: мобильное приложение ждёт 201, а получает 200, SDK не может десериализовать ответ из‑за переименованного поля, или обработка ошибок ломается из‑за разных форматов ошибок.
Даже если ничего не падает, команды перестают доверять спецификации — и тогда вы теряете раннее предупреждение о проблемах.
Копирование backend-кода даёт вам снимок текущего поведения, включая случайные особенности, которые вы, возможно, не хотите гарантировать в контракте.
Лучше поумолчанию: сначала опишите предполагаемое поведение (входы, выходы, ошибки), а затем проверьте, что реализация соответствует этому описанию. Так вы получаете контракт, который можно контролировать, вместо простого снимка сегодняшних маршрутов.
Для каждого эндпоинта зафиксируйте:
Выберите одну форму ответа об ошибках и используйте её повсеместно.
Простой дефолт — один из двух вариантов:
{ "error": "message" }, или{ "error": { "code": "...", "message": "...", "details": ... } }Затем используйте это везде, включая примеры. Последовательность важнее сложности, потому что клиенты зашивают эту форму в код.
Дайте Claude Code ваши заметки о поведении и строгие правила; скажите явно, чтобы он не выдумывал поля. Практический набор инструкций:
TODO в спецификацию и перечисли под ASSUMPTIONS.»Error) и ссылаться на них.»После генерации первым делом просмотрите раздел ASSUMPTIONS — именно там начинаются ошибки, если допускать догадки.
Прежде чем сравнивать со «всё ещё работающим» API, проверьте сам OpenAPI файл:
201)Это позволяет поймать «желаемое» описание до проверки production-поведения.
Считайте running API тем, что видят пользователи сегодня, и решайте по каждому расхождению:
Держите изменения маленькими (один эндпоинт или один ответ за раз), чтобы быстро протестировать и откатить при необходимости.
На сервере валидация означает быстро отклонять запросы, которые не соответствуют контракту, и возвращать понятную ошибку. Это защищает данные и облегчает обнаружение багов.
На клиенте валидация — это обнаружение дрейфа раньше, чем пользователи заметят проблему. Тесты на клиенте должны фокусироваться только на том, от чего зависит функциональность:
Избегайте проверки каждого опционального поля, чтобы тесты падали только на реальные регрессии, а не на безопасные добавления.
Простая рутина, которая редко тормозит команды:
Если вы используете Koder.ai, держите OpenAPI рядом с кодом, снимайте состояние перед правками и откатывайте при необходимости.
Если вы можете написать конкретный запрос и два ответа, обычно этого достаточно, чтобы набросать правдивую спецификацию.