URL-дизайн на собеседовании системного аналитика
SELECT * FROM orders, users без WHERE и без JOIN ... ON. В orders 1000 строк, в users 500 строк. Что вернёт запрос?Содержание:
Проектирование URL — базовый навык системного аналитика, который проверяют почти на каждом собесе по REST API. Интервьюер даёт задачу вроде «спроектируй эндпоинты для сервиса заказов» и смотрит, отличаешь ли ты ресурс от действия, знаешь ли конвенции и умеешь ли объяснить свой выбор. Плохой URL-дизайн — это первый сигнал, что кандидат не работал с реальным API, а только читал про него.
REST-конвенции для URL
Главное правило REST: ресурсы — это существительные, а не глаголы. Действие над ресурсом выражается HTTP-методом, а не словом в пути. Один и тот же путь /orders/42 обслуживает и чтение, и обновление, и удаление — меняется только метод.
ХОРОШО:
GET /orders — список заказов
GET /orders/42 — один заказ
POST /orders — создать заказ
PUT /orders/42 — заменить заказ целиком
PATCH /orders/42 — частично обновить
DELETE /orders/42 — удалить
ПЛОХО (глаголы в пути дублируют метод):
POST /createOrder
GET /getOrderById?id=42
POST /orders/42/deleteВложенные ресурсы показывают отношение «принадлежит». Заказ принадлежит пользователю, товарная позиция — заказу:
GET /orders/42/items — позиции заказа 42
GET /users/99/orders — заказы пользователя 99Вложенность глубже двух-трёх уровней — антипаттерн: путь становится нечитаемым и хрупким. Если нужна связь между удалёнными сущностями, лучше вынести её в query-параметр или отдельный эндпоинт, чем городить /users/99/orders/42/items/7/reviews.
Множественное или единственное число
Стандарт индустрии — множественное число для коллекций: /orders, /users, /products. Это читается естественно и для списка (GET /orders), и для отдельного элемента (GET /orders/42).
Единственное число уместно только для синглтонов — ресурсов, которые существуют в единственном экземпляре в контексте запроса: /me, /profile, /settings. Здесь нет коллекции, поэтому и множественное число смысла не имеет.
На собесе главное — не смешивать стили. Если половина API на /orders, а половина на /order — это сразу читается как несогласованность. Консистентность важнее того, какую именно конвенцию вы выбрали.
Версионирование
Версия нужна, когда меняется контракт API так, что старые клиенты сломаются. Три подхода, и интервьюер ждёт, что вы назовёте плюсы и минусы каждого.
- Версия в пути —
/v1/orders,/v2/orders. Самый распространённый и практичный вариант: версия видна сразу, легко роутить и кешировать, просто дебажить в логах и браузере. Минус — версионируется весь API целиком, а не отдельные ресурсы. - Версия в заголовке —
Accept: application/vnd.api.v2+json. URL остаются «чистыми», можно версионировать точечно, но тестировать и отлаживать труднее: версию не видно в адресной строке, нужен инструмент вроде curl или Postman. - Версия в query-параметре —
/orders?version=2. Считается антипаттерном: query должен фильтровать ресурс, а не менять контракт; такие URL плохо кешируются и путают семантику.
Практичный дефолт для большинства сервисов — версия в пути. Именно этот ответ ждут на собесе, если вы обоснуете выбор простотой отладки и роутинга.
Query-параметры
Query-строка меняет представление одной и той же коллекции: фильтрует, сортирует, разбивает на страницы. Она не должна менять сам ресурс или его версию.
- Фильтрация:
?status=paid&country=RU— отобрать подмножество по атрибутам. - Пагинация:
?limit=20&cursor=abc(cursor-based, устойчива к вставкам) или?page=2(offset-based, проще, но плывёт при изменении данных). - Сортировка:
?sort=created_at&order=desc— по какому полю и в каком направлении. - Sparse fieldsets:
?fields=id,name— вернуть только нужные поля, чтобы уменьшить размер ответа. - Включение связей:
?include=customer,items— подтянуть связанные сущности одним запросом вместо N+1.
Спецификация JSON:API задаёт готовые конвенции для всего этого — на неё удобно ссылаться на собесе, чтобы показать, что вы знаете стандарты, а не изобретаете формат с нуля.
Slug'и
Slug — человекочитаемый идентификатор в URL вместо числового ID. Он лучше для SEO и понятнее пользователю:
/articles/how-to-write-clean-code
/products/iphone-15-proГенерация. Slug получают из заголовка: приводят к нижнему регистру, заменяют пробелы дефисами, транслитерируют кириллицу и выкидывают спецсимволы. «Как писать чистый код» → kak-pisat-chistyy-kod.
Стабильность. Смена slug ломает все внешние ссылки и SEO — поисковик теряет проиндексированный URL. Поэтому есть два подхода:
- Сделать slug неизменяемым — он фиксируется при создании и больше не меняется, даже если заголовок отредактировали.
- Разрешить менять, но настроить редиректы 301 со старого URL на новый, чтобы ссылки и позиции в поиске не потерялись.
На практике часто хранят и числовой ID (для внутренних связей и стабильности), и slug (для URL): /articles/1234-kak-pisat-chistyy-kod. Тогда роутинг работает по ID, а хвост-slug чисто для читаемости и его можно менять безболезненно.
Как это спрашивают на собесе
Типичный формат — открытая задача на проектирование. «Спроектируй REST API для корзины / заказов / отзывов». Интервьюер смотрит на несколько вещей:
- Ресурс vs действие. Первое, что проверяют, — не появятся ли у вас
/getOrdersи/updateUser. Если появились, это минус. - Правильный метод под операцию. Ждут, что вы объясните разницу PUT (заменить целиком, идемпотентно) и PATCH (частичное обновление), почему DELETE не должен возвращать тело.
- Обработка коллекций. Спросят, как отдавать список из миллиона заказов — здесь всплывают пагинация, фильтры и сортировка.
- Версионирование и обратная совместимость. «Как добавить новое обязательное поле, не сломав старых клиентов?» Ответ — через версию или через необязательное поле с дефолтом.
- Обоснование. Важнее не «единственно правильный» ответ, а то, что вы проговариваете trade-off'ы: почему cursor, а не offset; почему версия в пути, а не в заголовке.
Частая ошибка — отвечать заученными правилами без объяснения. Сильный кандидат не просто говорит «ресурсы — существительные», а показывает, что понимает, почему это упрощает жизнь клиенту и кешу.
Связанные темы
- REST API на собесе SA
- HTTP методы и коды для SA
- Pagination cursor vs offset для SA
- OpenAPI и Swagger для SA
- Подготовка к собесу системного аналитика
FAQ
Существительные или глаголы в URL?
Всегда существительные для ресурсов, действие задаётся HTTP-методом. POST /orders создаёт заказ, DELETE /orders/42 удаляет. Глаголы в пути (/createOrder) дублируют метод и считаются антипаттерном.
Как версионировать API?
Три способа: версия в пути (/v1/orders), в заголовке (Accept: ...v2+json) и в query (?version=2). Практичный дефолт — путь: версия видна, легко роутить и дебажить. Query-версионирование считается антипаттерном.
Множественное или единственное число в путях?
Множественное для коллекций (/orders, /users), единственное только для синглтонов (/me, /profile). Главное — не смешивать стили в рамках одного API.
Можно ли менять slug после публикации?
Технически можно, но это ломает внешние ссылки и SEO. Либо делают slug неизменяемым, либо настраивают 301-редирект со старого URL на новый. Часто в URL кладут и стабильный ID, и slug сразу.
Это официальная информация?
Нет. Статья основана на REST best practices, спецификации JSON:API и опыте прохождения собеседований. Конкретные конвенции зависят от команды и стандартов компании.
Тренируйте системный анализ — откройте тренажёр с 1500+ вопросами для собесов.