Суть Читай реальные HTTP-запросы, дизайны URL и формы ответов, предскажи сбой и выбери фикс с наибольшим рычагом, который senior сделает первым.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min
Дефекты дизайна API видны в wire-формате — строке запроса, форме URL, теле ответа. Прочитай каждый сниппет, найди ловушку и выбери фикс, который senior-инженер сделает первым.
Цель
Отработай цикл, который ты запускаешь в каждом ревью API: прочитай запрос и форму ресурса, предскажи, что сломается в production, и потянись к структурному фиксу, прежде чем латать симптомы.
Так API отменяет заказ. В чём дефект и какой фикс?
Heads-up PUT vs PATCH — не проблема: замена всего заказа имеет тот же изъян. Дефект в том, что переход состояния неохраняем — клиент пишет status напрямую вместо того, чтобы сервер навязывал правила.
Heads-up Media-тип — деталь; структурный дефект в том, что любой клиент может задать status любым значением. Фикс — охраняемый action-ресурс, а не другой формат патча.
Heads-up Выглядит RESTful, но записываемое поле status отдаёт клиенту стейт-машину. Настоящая отмена — охраняемый переход, которым должен владеть сервер, а не поле, которое флипает клиент.
Сниппет 2 — дизайн URL ресурсов
GET /getUserOrders?userId=7POST /createOrderForUser?userId=7GET /customers/7/projects/3/orders/42/items/9/discounts
Викторина
Completed
В этих трёх строках два разных запаха моделирования. Назови их и редизайн.
Heads-up Оба дефекта. getUserOrders и createOrderForUser кладут действие в URL — это RPC, теряющий uniform interface, а не приемлемый алиас рядом с REST-ресурсами.
Heads-up Query-параметры для фильтрации и связей — корректный REST. Реальные запахи — глаголы в строках 1–2 и шестиуровневая вложенность в строке 3, а не использование query-строк.
Heads-up Регистр — косметика. Две строки кодируют глаголы в пути, одна вложена на шесть уровней — структурные проблемы, которые консистентный регистр не решает.
Клиент шлёт этот запрос, TCP-соединение рвётся до прихода ответа, и клиент ретраит идентичный запрос с тем же Idempotency-Key. Что должен сделать сервер?
Heads-up Это ровно то двойное списание, которое этот заголовок и предотвращает. Idempotency-Key говорит серверу обнаружить повтор и вернуть первый результат, а не создавать второй платёж.
Heads-up Повтор с тем же ключом и телом — не конфликт; контракт в том, чтобы вернуть исходный успешный ответ, делая ретрай прозрачным для клиента. 409 заставил бы клиента обрабатывать не-ошибку как ошибку.
Heads-up PUT и DELETE идемпотентны по своей семантике; idempotency key существует именно потому, что POST — нет, позволяя безопасно ретраить создающий запрос.
Это ресурс user, сериализованный прямо из строки ORM. В чём ошибка и структурный фикс?
Heads-up Убрать credential необходимо, но недостаточно. is_deleted и internal_risk_score — внутренние, а сериализация строк напрямую всё равно приваривает контракт к именам колонок — структурный фикс это явный mapping-слой.
Heads-up Число vs строка для id — мелкий выбор конвенции. Реальный урон — слив credential и внутренних полей и связывание wire-контракта со схемой БД.
Heads-up Хэш пароля не должен быть ни в каком representation независимо от роли, и связь со схемой остаётся. Фикс — отображать в осознанную wire-форму, а не гейтить сырые колонки по роли.
Итог
Любой дефект API читается с провода: записываемое поле status — неохраняемый переход (используй охраняемый action-ресурс); глаголы в пути и шестиуровневая вложенность — RPC и chattiness (noun-ресурсы, мелкие пути, ID в корне); ретраенный POST нуждается в idempotency key для безопасного дедупа; а строка, сериализованная прямо в JSON, сливает credential и внутренние поля и приваривает контракт к схеме (отображай через DTO). Читай запрос, найди структурный запах, чини форму — не симптом.