Есть один верный способ проверить команду на прочность: сделать API и никому не сказать, как работает API.
Не надо OpenAPI. Не надо Swagger. Не надо примеров запросов и ответов. Не надо описывать ошибки. Не надо фиксировать контракты. Настоящий backend-разработчик верит: если поле называется status, фронтенд сам поймёт, что там может быть active, ACTIVE, 1, true, enabled или вообще null.
Документация — это для слабых. Сильные читают network tab.
Поля должны быть неочевидными
Хорошее API никогда не раскрывает свои намерения сразу. Например, поле type может означать тип пользователя, тип документа, тип операции или тип ошибки. В идеале — всё сразу, в зависимости от эндпоинта.
Ещё лучше использовать названия, понятные только автору:
{
"flg": 1,
"usrTp": 3,
"isAvailable": false,
"available": true
}
Пусть фронтенд сам разберётся, почему isAvailable и available противоречат друг другу. Это развивает системное мышление.
Особенно полезно возвращать важные поля не всегда. Сегодня email есть, завтра нет, послезавтра он лежит внутри contacts.primary.email, а в пятницу приходит как массив. Главное — не предупреждать. Сюрпризы делают продукт живым.
Ошибки должны быть разными
Никогда не стандартизируй формат ошибок. Это скучно и предсказуемо.
Один эндпоинт может возвращать так:
{
"error": "User not found"
}
Другой так:
{
"message": "USER_NOT_FOUND",
"code": 404
}
Третий — просто строку:
Something went wrong
А лучший вариант — вернуть 200 OK с телом:
{
"success": false
}
Так фронтенд никогда не расслабляется. Ему приходится проверять HTTP-статус, success, error, message, result, data, meta, фазу Луны и настроение backend-сервиса.
Отдельное удовольствие — ошибки валидации. Например, на одно поле вернуть массив, на другое строку, а на третье вообще ничего:
{
"errors": {
"email": "Invalid email",
"password": ["Too short", "No number"],
"name": null
}
}
Пусть дизайнеры тоже подключаются и придумывают, как это красиво показать.
Контракты надо менять внезапно
Контракт API — это не договор, а творческий процесс.
Сегодня поле называется userId, завтра id, потом uuid, а после рефакторинга — external_user_identifier. Старое название можно удалить сразу. Зачем поддерживать обратную совместимость, если можно поддерживать атмосферу постоянной тревожности?
Особенно хорошо менять типы данных без предупреждения:
{
"price": "1200"
}
А потом:
{
"price": 1200
}
А потом:
{
"price": {
"amount": 1200,
"currency": "RUB"
}
}
Фронтенд всё равно TypeScript использует. Пусть типизирует реальность как-нибудь сам.
OpenAPI и Swagger — лишняя бюрократия
Некоторые команды зачем-то описывают API в OpenAPI: схемы, параметры, ответы, статусы, примеры. Потом по этому можно генерировать клиентов, мокать сервер, валидировать контракты и быстрее договариваться между командами.
Но где в этом азарт?
Гораздо лучше обсуждать каждый эндпоинт в чате:
— А что возвращает
/orders?
— Сейчас скину пример.
— А почему у меня другой ответ?
— А, это если пользователь юрлицо.
— А где это написано?
— Ну теперь ты знаешь.
Так знания распространяются естественным путём — через боль.
Не надо версионировать API
Версионирование — это признание того, что API может использовать кто-то ещё. А мы же знаем: фронтенд всегда обновляется одновременно с backend. Особенно мобильный. Особенно после релиза в App Store.
Поэтому никаких /v1, /v2, deprecated-полей и migration guide. Просто меняй поведение существующего эндпоинта. Если что-то сломается — значит, кто-то был недостаточно внимателен.
Идеальный результат
Если всё сделать правильно, команда получит:
- фронтенд, заваленный
if (data?.result?.payload?.items || data?.items || []); - QA, который заводит баги с формулировкой «иногда не работает»;
- backend, который говорит «у меня локально всё нормально»;
- менеджера, который не понимает, почему простая форма занимает неделю;
- созвоны на тему «а какой всё-таки контракт у этого метода?».
И главное — API станет настоящим квестом. Каждый новый экран будет начинаться не с разработки, а с археологии.
Как делать неправильно правильно
Не описывай поля.
Не фиксируй форматы ошибок.
Не предупреждай об изменениях.
Не используй OpenAPI.
Не добавляй примеры.
Не версионируй контракты.
Не думай о тех, кто будет этим пользоваться.
Ведь API без документации — это не интерфейс. Это загадка.
А фронтенд пусть сам догадается.
А если серьёзно
Контракт API должен быть понятным и предсказуемым: описанные поля, единый формат ошибок, примеры запросов и ответов, предупреждения об изменениях и версионирование там, где без него нельзя сохранить обратную совместимость. OpenAPI не заменит разговор между командами, но избавит их от необходимости каждый раз угадывать одно и то же.