Суть Читай реальные API-контракты, ответы, запрос пагинации и rate-limit-заголовки, затем выбирай сеньорский ход — фикс с наибольшим рычагом или верную интерпретацию.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min
Проблемы API диагностируются в контракте и на проводе: таблица маршрутов, ответ, SQL-запрос страницы, набор заголовков. Прочитай каждый и выбери ход, который сеньор сделает первым.
Цель
Потренируй review-цикл, который ты гоняешь на каждом публичном API: прочитай контракт или ответ, предскажи, где он сломается под нагрузкой или retry, и тянись к структурному фиксу до того, как тянуться к обходу.
Сниппет 1 — таблица маршрутов
POST /chargeCardPOST /refundCharge?id=123GET /getUserOrders?user=42POST /orders/{id}/cancel?action=cancel
Викторина
Completed
В этой таблице маршрутов смешано несколько ошибок моделирования. Какая единственная коррекция даёт наибольший рычаг и почему она важнее всего?
Heads-up Исправить ?id=123 на сегмент пути — половина работы, но POST /chargeCard и ?action=cancel всё ещё глаголы. Ядро дефекта — глагольные ресурсы, что и отравляет status-коды.
Heads-up Версионирование ортогонально; /v1/chargeCard — всё ещё глагол с той же retry-проблемой. Версионирование организует эволюцию, а не чинит плохое моделирование.
Heads-up Создание charge или cancellation меняет состояние — GET обязан быть безопасным и идемпотентным. Делать записи GET-абельными — катастрофа корректности и кэширования.
Сниппет 2 — ответ на создание платежа
POST /paymentsIdempotency-Key: 8f1c-...-a9--> HTTP/1.1 200 OK { "status": "ok", "error": null }
Викторина
Completed
Клиент прислал Idempotency-Key, а сервер вернул 200 без id ресурса. Дальше следует retry по таймауту. Что не так и что сервер должен возвращать?
Heads-up Для создания честный код — 201 + Location. Голый 200 без id не отличит свежее создание от идемпотентного повтора, и эта неоднозначность и ведёт к двойным списаниям.
Heads-up 204 значит «успех, без тела» — но созданный платёж обязан вернуть id/Location, чтобы клиент мог сослаться и подтвердить идемпотентный повтор. 204 выбрасывает информацию, нужную retry.
Heads-up 409 — про конфликтующее состояние (например, повтор, столкнувшийся с другим in-flight результатом), а не про успешное первое создание. Первая запись — 201; безопасный повтор — 200 с исходным результатом.
Сниппет 3 — запрос глубокой страницы
-- страница 5001 живой растущей лентыSELECT id, created_at, titleFROM postsORDER BY created_at DESCOFFSET 100000 LIMIT 20;
Викторина
Completed
Этот запрос обслуживает живую растущую ленту, и глубокие страницы тормозят и иногда теряют или дублируют посты. Какова cursor-перепись и что она даёт?
Heads-up Индекс ускоряет ORDER BY, но OFFSET 100000 всё равно сканирует и отбрасывает 100k строк на каждой глубокой странице. Discard убирает keyset, а не индекс.
Heads-up created_at не уникален — связки на одном timestamp пропускаются или дублируются на границах страниц. Keyset-курсору нужен уникальный tiebreaker (id), отсюда композит (created_at, id).
Heads-up Лента отсортирована по created_at DESC; пагинация по одному id вернёт неверный порядок. Курсор обязан совпадать с ключом сортировки, а id — только tiebreaker.
Сниппет 4 — rate-limit-заголовки
HTTP/1.1 429 Too Many RequestsRetry-After: 8X-RateLimit-Limit: 1000X-RateLimit-Remaining: 0X-RateLimit-Reset: 1716998400
Викторина
Completed
Клиент получил этот ответ. Каково корректное поведение клиента и что означает каждый сигнал?
Heads-up Remaining = 0 и Retry-After = 8 — мгновенный retry гарантированно снова задросселирован и усиливает нагрузку. Весь смысл Retry-After в том, чтобы клиент выждал указанное окно.
Heads-up 429 — сигнал backoff, а не жёсткий сбой: запрос валиден, просто сверх квоты. Клиент должен выждать и ретраить, показывая ошибку пользователю, только если она держится после reset.
Heads-up Retry-After и поля X-RateLimit-* — это и есть документированный сигнал квоты; их игнор превращает кооперативный дроссель в гадание и может держать клиента заблокированным дольше.
Итог
Любой review API читает одни и те же артефакты: таблица маршрутов говорит, существительные ли ресурсы (и значит, могут ли status-коды быть честными); ответ на создание должен быть 201 + Location на первой записи и 200 на идемпотентном повторе, а не голый 200 без id; запрос глубокой страницы должен быть keyset-курсором по уникальному композитному ключу сортировки, а не OFFSET; а 429 с Retry-After + X-RateLimit-* — кооперативный backoff-контракт, который клиент обязан чтить. Прочитай контракт, предскажи сбой под retry или нагрузкой, затем применяй структурный фикс.