Backend

Архитектура API с нуля: наш подход

К Кирилл Зайченко12 июня 2026 г. 4 мин
А

API — это язык, на котором две системы договариваются. И как любой язык, он ценен предсказуемостью: если правила меняются на ходу, разговаривать невозможно.

Мы всегда начинаем с контракта, ещё до кода. Описываем ресурсы, методы, форматы, коды ошибок. Это занимает день-два и почти всегда вскрывает вопросы, которые иначе всплыли бы через месяц: что происходит, если объект удалён; можно ли создать две записи с одинаковым ключом; что возвращать, когда список пустой.

Побочная выгода: как только контракт согласован, фронтенд и бэкенд работают параллельно. Фронтенд поднимает заглушку по контракту и не ждёт готового сервера. На проектах со сжатыми сроками это часто решающая экономия.

Дальше — правила, которые мы соблюдаем почти без исключений.

Ломающие изменения — только через версию. Клиент не должен однажды утром обнаружить, что интеграция сломалась, потому что мы переименовали поле. Добавлять поля можно, удалять и переименовывать — нет. Если очень надо, появляется /v2, а /v1 живёт объявленный срок.

Формат ошибок фиксируется заранее и одинаков для всего API. Мы отдаём машиночитаемый код, человекочитаемое сообщение и, где уместно, указание на конкретное поле. Разница между «400 Bad Request» и «400 с кодом VALIDATION_FAILED и указанием, что поле phone не соответствует формату» — это разница между часом переписки с интегратором и нулём.

Идемпотентность для всего, что создаёт или списывает. Клиент отправил запрос, соединение оборвалось, клиент повторил — и создались два заказа. Лечится ключом идемпотентности: клиент передаёт уникальный идентификатор операции, сервер запоминает результат и на повтор отдаёт то же самое вместо новой записи. Это стоит недорого и снимает целый класс инцидентов.

Пагинация с самого начала, даже когда записей десять. Через год их будет сто тысяч, а эндпоинт без пагинации к этому моменту уже будет использоваться в трёх местах. Мы предпочитаем курсор смещению: он не сбивается, когда данные добавляются во время листания.

Даты — всегда в UTC и в ISO 8601. Это скучно и это спасает. Локальное время в API однажды обязательно приведёт к тому, что что-то произойдёт дважды в ночь перевода часов или не произойдёт вовсе.

Отдельная тема — документация. Наше единственное жёсткое требование: она должна лежать рядом с кодом и генерироваться из него. Документация, которая живёт в отдельном файле или, хуже, в вики, устаревает за месяц. После этого она хуже, чем её отсутствие: ей верят, а она врёт.

Чего мы стараемся избегать. Не делаем эндпоинты под конкретный экран интерфейса — через полгода экранов станет пять, и API превратится в свалку узкоспециальных методов. Не прячем ошибки за кодом 200 с полем success: false — HTTP уже умеет сообщать об ошибке, не нужно изобретать второй механизм поверх. Не возвращаем разную структуру в зависимости от параметров: клиенту придётся писать разбор на каждый случай.

И последнее, что стоит проговорить: хорошее API — это в первую очередь про то, чтобы им было тяжело воспользоваться неправильно. Если интегратор регулярно ошибается в одном и том же месте, дело не в интеграторе.

Понравилась статья?

У нас много таких историй — и ещё больше идей для вашего продукта.

Обсудить проект