На собеседовании системного аналитика могут попросить описать новую функцию: выяснить требования, спроектировать данные и API, показать обмен между системами. Полезно пройти задачу последовательно: уточнить результат для пользователя, согласовать правила, описать состояния, затем запросы, ответы и поведение при ошибках.
Разберём этот путь на одном примере: интернет-магазин передаёт заявку на доставку курьерской службе. Покупатель выбирает пункт выдачи и должен понять, принял ли курьер заявку. В конце получатся требования, модель данных, примеры запросов и набор проверок, которые можно объяснить на интервью.
Все номера, правила, сроки и ответы ниже — условия учебного кейса, а не описание Hintsage или реального перевозчика. Формат конкретного собеседования зависит от компании: отдельно могут спросить SQL, способы описания процессов и другие темы. Здесь тренируем связное решение задачи с интеграцией.
Шаг 1. Уточните, что должно получиться
Представим формулировку интервьюера: «Добавьте оформление доставки через партнёра». Она пока не объясняет, кто пользуется функцией, какие данные уже есть и что считать успехом. Если сразу писать запрос к курьеру, эти решения останутся неявными.
Сначала зададим вопросы, от которых зависит решение:
- Для какого заказа оформляют доставку? В нашем примере заказ уже существует, принадлежит покупателю и подготовлен к отправке. Покупатель вошёл в кабинет.
- Куда везём? Только в выбранный пункт выдачи из согласованного справочника. Курьер и склад отправления заранее определены. Стоимость, состав посылки и сведения получателя уже есть в заказе.
- Что означает «готово»? Нужно получить подтверждение регистрации у курьера и его номер заявки. Доставка посылки покупателю — более поздний процесс.
- Можно ли менять решение? В этой версии на заказ разрешена одна заявка; изменение пункта, отмена и повторное оформление после отказа не входят в задание.
- Может ли курьер отвечать долго? Да. Сохраняем заявку в магазине и передаём её в фоне, а покупателю показываем состояние.
Последний вопрос зададим команде курьера: умеет ли её система узнавать повтор одной заявки по нашему постоянному номеру? Для разбора считаем, что такая договорённость есть: в пределах нашего магазина один client_reference соответствует одной заявке на весь срок её учёта. Без этого условия безопасный повтор после потери ответа придётся отдельно согласовать.
Шаг 2. Превратите пожелание в требования
Короткое описание цели может звучать так: «Покупатель хочет отправить подготовленный заказ в выбранный пункт выдачи и увидеть подтверждение курьера». Оно объясняет пользу, но команде ещё нужны точные правила работы.
Зафиксируем требования для учебной версии:
- Создание. Владелец готового к отправке заказа может создать одну заявку с пунктом из разрешённого справочника.
- Повтор. Одинаковый запрос для того же заказа возвращает прежнюю заявку. Новый пункт при существующей заявке вызывает конфликт, а не незаметное изменение доставки.
- Сохранение. Магазин подтверждает создание только после записи заявки и задания на её передачу в постоянное хранилище.
- Подтверждение. Состояние «принята курьером» появляется после его явного ответа с номером. Потеря ответа сама по себе не означает отказа.
- Доступ. Покупатель может читать только заявки собственных заказов. Подтверждённые данные заказа сервер берёт из своей базы.
- Затянувшаяся передача. На автоматические попытки отводим десять минут с момента создания. Затем результат уточняет поддержка; покупателю не предлагают создать ещё одну заявку.
Десять минут — выбранное условие упражнения, а не стандарт интеграций. На интервью его следует согласовать с заказчиком: что делать, если курьер недоступен дольше, кто получает уведомление и как быстро должен отреагировать.
Отдельно уточните требования к качеству: допустимое время ответа магазина при заданной нагрузке, срок хранения заявки и доступность для поддержки. Слова «быстро» или «надёжно» не задают способ проверки. Не обещайте конкретную скорость до выбора условий измерения и испытаний.
Шаг 3. Опишите состояния и модель данных
Нужно различать две вещи: магазин сохранил заявку и курьер её принял. Между ними может пройти время. Состояние заявки помогает объяснить это покупателю и не потерять неопределённый результат при сбое.
| Состояние | Когда устанавливаем | Что показываем |
|---|---|---|
| pending | Заявка сохранена, подтверждённого итога от курьера ещё нет | Передаём заявку курьерской службе |
| confirmed | Курьер подтвердил приём и вернул номер заявки | Заявка принята курьерской службой |
| rejected | Курьер явно отказал по согласованной бизнес-причине и не создал заявку | Заявка не принята; показываем понятную причину |
| needs_attention | Истёк срок автоматических попыток или ответ не соответствует контракту | Результат уточняется; передаём вопрос поддержке |
Заявка начинает путь с pending. Из него переходит в confirmed, rejected или needs_attention. При уточнении из needs_attention можно получить подтверждение или отказ. Подтверждённое принятие не заменяем отказом из-за запоздавшей ошибки другой попытки.
confirmed означает регистрацию у курьера, а не фактическую доставку. rejected используем только для согласованного бизнес-отказа, при котором курьер не создал заявку. Неожиданный формат ответа или ошибка настройки требуют уточнения.
Какие записи понадобятся
Выберем реляционную базу, например PostgreSQL. Это решение учебного примера. Для разговора достаточно показать сущности, связи и ограничения; полную схему базы магазина проектировать заново не нужно.
| Сущность | Основные сведения | Правило |
|---|---|---|
| Заказ: Order | id, владелец, готовность к отправке, состав посылки и сведения получателя | Уже существует; данные подтверждает магазин |
| Заявка: DeliveryRequest | id, order_id, pickup_point_id, status, carrier_request_id, created_at | order_id обязателен, связан с заказом и уникален |
| Снимок отправляемых данных | Пункт, сведения получателя и посылки на момент создания заявки | Все повторы передают один и тот же сохранённый набор |
| Задание передачи: OutboxTask | delivery_request_id, данные отправки, число попыток, время следующей попытки и срок закрепления за работником | Одна начальная задача на заявку; хранится в той же базе |
Здесь id заявки — её номер в магазине, а carrier_request_id — номер у курьера. Например, одной записи D-7001 после подтверждения соответствует CR-88021. Пока номер курьера неизвестен, храним null — отсутствие значения.
Уникальность order_id закрепляем ограничением базы. Проверка «сначала посмотрим, что заявки нет» не защищает от двух одновременных запросов. При конфликте вставки сервер читает существующую запись и сравнивает её пункт с запрошенным. Записи не удаляются так, чтобы снова разрешить создание для того же заказа: правило одной заявки действует весь срок учёта.
Шаг 4. Согласуйте контракт API
API — интерфейс обмена между программами. Контракт описывает, какой запрос отправляет одна сторона, как его проверяет другая и что означает ответ. Для HTTP важно согласовать метод, путь, поля, доступ и ошибки. Назвать один адрес метода недостаточно.
Создание заявки в магазине
Пусть покупатель выбирает пункт P-17 для своего заказа O-314. Клиент передаёт только их номера. Сервер определяет покупателя по действующему входу; поле с чужим номером пользователя в теле не принимается.
В примерах shop.example.invalid — вымышленный адрес, DEMO_USER_TOKEN — учебная подпись вместо настоящего токена. Запросы по этому адресу выполнять не нужно. JSON — текстовый формат, в котором записаны поля и их значения.
POST /delivery-requests HTTP/1.1
Host: shop.example.invalid
Authorization: Bearer DEMO_USER_TOKEN
Content-Type: application/json
{
"order_id": "O-314",
"pickup_point_id": "P-17"
}Оба поля обязательны и должны быть непустыми строками. Неизвестные поля отклоняем. После проверки входа и формата сервер проверяет право на заказ. Если заявка уже есть, сравнивает пункт и возвращает результат повтора. Только для новой заявки проверяет готовность заказа, сохраняет снимок данных и задание передачи.
При первом успешном создании ответ выглядит так:
HTTP/1.1 201 Created
Content-Type: application/json
Location: /delivery-requests/D-7001
Cache-Control: no-store
{
"id": "D-7001",
"order_id": "O-314",
"pickup_point_id": "P-17",
"status": "pending",
"carrier_request_id": null
}201 Created подтверждает создание ресурса в магазине. Заголовок Location указывает, где читать заявку. Состояние pending показывает, что результат курьера ещё неизвестен. Если бы контракт означал только принятие задания на выполнение без создания такой заявки, смысл ответа был бы другим; здесь фиксируем именно создание записи.
Чтение состояния
Клиент может периодически запрашивать сохранённую заявку, пока пользователь смотрит страницу. В этой версии выбираем такой способ получения состояния. Интервал опроса следует согласовать с нагрузкой; закрытие страницы не должно останавливать передачу курьеру.
GET /delivery-requests/D-7001 HTTP/1.1
Host: shop.example.invalid
Authorization: Bearer DEMO_USER_TOKENПосле подтверждения курьера сервер возвращает ту же заявку с новым состоянием и его номером:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"id": "D-7001",
"order_id": "O-314",
"pickup_point_id": "P-17",
"status": "confirmed",
"carrier_request_id": "CR-88021"
}Заголовок Cache-Control: no-store запрещает сохранять этот ответ в HTTP-кэшах. Проверку владельца сервер выполняет при каждом чтении; одного знания адреса заявки недостаточно для доступа.
| Ситуация | Ответ | Что происходит с заявкой |
|---|---|---|
| Корректное первое создание | 201 Created | Одна новая заявка и одно задание на передачу |
| Повтор с теми же order_id и pickup_point_id | 200 OK | Возвращается существующая заявка в текущем состоянии |
| Для этого заказа уже выбран другой пункт | 409 Conflict | Прежняя заявка сохраняется без изменения |
| Неверный JSON, поля или неизвестный пункт | 400 Bad Request | Новая заявка не создаётся |
| Нет действующего входа | 401 Unauthorized | Возвращается WWW-Authenticate: Bearer, данные не раскрываются |
| Заказ или заявка не существует либо принадлежит другому | 404 Not Found | Чужие данные не раскрываются |
| Новая заявка запрошена для заказа, не готового к отправке | 409 Conflict | Новая заявка не создаётся |
| Магазин не может подтвердить сохранение в своей базе | 503 Service Unavailable | Клиенту не сообщают об успешном сохранении |
Повтор с теми же данными может вернуть уже confirmed, если передача успела закончиться. Готовность заказа для нового создания при этом не проверяется заново вместо возврата существующей заявки. Если ответ магазина потерялся после записи, клиент повторяет прежний запрос: сервер находит запись и возвращает её.
Коды выше — часть выбранного контракта. Для ошибок договоримся о теле вида {"error":{"code":"pickup_point_conflict"}}; конкретный код поясняет причину. Передавать клиенту внутреннюю ошибку базы или данные чужого заказа не нужно. Контракт можно оформить в OpenAPI — машиночитаемом описании запросов, ответов и схем полей.
Шаг 5. Покажите обмен с курьерской службой
Передача выполняется фоновым работником — процессом, который читает сохранённые задания и отправляет запросы курьеру. Покупатель ждёт только сохранения в магазине. Взаимодействие с курьером при этом остаётся обычным запросом и ответом, но происходит отдельно от запроса браузера.
Главный риск: сохранить заявку и остановиться до создания задания на отправку. Поэтому заявку и OutboxTask записываем в одной транзакции: либо сохраняются обе записи, либо ни одна. Такой приём называют transactional outbox — сохранение исходящего задания вместе с изменением данных. Отдельный брокер сообщений для выбранного примера не требуется.
Сначала магазин сохраняет заявку
- Покупатель: отправляет номер заказа и пункт выдачи.
- API магазина: проверяет вход, владельца, повтор и правила нового создания.
- База: одной транзакцией сохраняет заявку, снимок данных и задание передачи.
- API магазина: отвечает 201 и показывает состояние pending.
Затем работник получает итог курьера
- Работник: на ограниченное время закрепляет за собой сохранённое задание.
- Запрос курьеру: передаёт постоянный client_reference и прежний снимок данных.
- Курьер: создаёт заявку либо узнаёт повтор и возвращает ранее принятое решение.
- База магазина: сохраняет подтверждённый итог и завершает задание одной транзакцией.
Сетевой запрос выполняем после закрепления задания, вне открытой транзакции базы. Если работник остановится, срок закрепления истечёт и задание сможет продолжить другой процесс. Он использует тот же номер и снимок. Постоянно удерживать блокировку базы во время ожидания курьера не нужно.
Что входит в договорённость с курьером
Для каждого отправления используем client_reference = D-7001 — номер заявки магазина. Вместе с ним передаём выбранный пункт, подготовленные сведения о получателе и посылке из снимка. Способ доступа к API курьера относится к учётной записи магазина; клиентский токен покупателя ему не пересылается.
Согласовать нужно не только названия полей, но и поведение:
- В пределах учётной записи магазина курьер атомарно связывает один
client_referenceс одной заявкой. Параллельные повторы не создают две записи. - Повтор с теми же данными возвращает тот же номер и решение. Изменённые данные с тем же номером отклоняются.
- Курьер явно различает принятие, бизнес-отказ без создания и техническую ошибку, после которой итог может быть неизвестен.
- Постоянная связь номера и заявки сохраняется на согласованный срок учёта; её нельзя молча забывать раньше допустимого повтора.
- Поддержка может уточнить результат по
client_reference, включая случай потери ответа.
Это условия вымышленного партнёрского API, а не свойство любого POST-запроса. Сам метод POST не гарантирует защиту от повторного действия. Возможность повторить одну операцию без создания второй называют идемпотентностью. Она должна обеспечиваться и в магазине, и у курьера.
Что делать, если ответ не пришёл
Представим, что курьер создал заявку, но соединение оборвалось до ответа. Магазин не знает, была ли операция выполнена. Он сохраняет pending и повторяет передачу с тем же client_reference и тем же снимком. Новый номер превратил бы повтор в отдельную заявку.
Паузы между попытками и время ожидания одного ответа согласуем с партнёром; повторять запрос бесконечно и без задержек не нужно. После десяти минут новые автоматические отправки прекращаются, незавершённая заявка получает needs_attention, а поддержка — уведомление с номером для проверки. Уже начатая попытка может завершиться позже: подтверждённый итог сохраняем и после перехода к уточнению.
Неожиданный ответ или нарушение схемы тоже требуют уточнения, а не автоматического объявления отказа. В журнале попыток нужны номер заявки, время, результат обмена и причина ошибки. Для контроля полезны возраст старейшей ожидающей заявки и число записей, требующих внимания. Наличие сохранённого задания само по себе не обещает успешную доставку при любой недоступности партнёра.
Шаг 6. Проверьте требования на сценариях
Критерий приёмки описывает наблюдаемый результат при заданных условиях. Он помогает заказчику, разработчику и тестировщику одинаково понять готовность функции. Возьмём тот же заказ и проверим не только успешное оформление, но и места, где легко получить повтор или потерять результат.
| Исходная ситуация | Действие или событие | Ожидаемый результат |
|---|---|---|
| Заказ готов, заявки ещё нет | Владелец выбирает P-17 и отправляет запрос | 201; одна заявка pending и одно сохранённое задание |
| Два одинаковых запроса пришли одновременно | Оба пытаются создать заявку для O-314 | Одна заявка; первый успешный ответ 201, повтор после разрешения конфликта — 200 с тем же id |
| Заявка уже существует для P-17 | Покупатель отправляет запрос для P-18 | 409; пункт прежней заявки не меняется |
| Ответ курьера потерян после принятия заявки | Работник повторяет отправку с тем же client_reference и теми же данными | Курьер возвращает прежний номер; второй заявки у него нет |
| Процесс остановился после ответа курьера, до записи итога | Истекает закрепление задания, другой работник продолжает его | Используется прежний client_reference; подтверждённый итог сохраняется |
| Десять минут нет подтверждённого итога | Исчерпан согласованный срок автоматической передачи | needs_attention; новых автоматических отправок нет, поддержка получает уведомление |
| Заявку запрашивает другой покупатель | Чтение D-7001 с чужим входом | 404 без сведений о заказе и курьере |
Для одновременных запросов важна не только надпись на экране: проверьте число записей заявки и задания, затем число заявок у курьера. Если запросы пришли с разными пунктами, сохраняется один вариант, а другой получает конфликт. Если потеря ответа произошла между принятием у курьера и записью итога, повтор должен восстановить сведения о прежней операции.
SQL можно использовать для проверки связей и числа записей; отдельный практикум по SQL-задачам на собеседовании поможет повторить соединения и подсчёты. Полезно также объяснить, какие проверки выполняет сам API, какие ограничения обеспечивает база, а какие гарантии нужны от внешнего партнёра.
Как объяснить решение на собеседовании
Рассказывайте, какую договорённость фиксирует каждый элемент. Например: «Уникальный номер заказа в заявках закрепляет правило одной доставки. Состояние pending отделяет сохранение от приёма курьером. Постоянный client_reference нужен, чтобы повтор после потери ответа не стал новой операцией».
Короткая версия решения может звучать так:
«Сначала уточню готовность заказа, владельца, пункт и смысл подтверждения. Для учебной версии сохраню одну заявку на заказ вместе с заданием передачи. API магазина вернёт её номер и pending. Фоновый работник отправит снимок данных курьеру с постоянным номером нашей заявки. Повторы должны узнаваться с обеих сторон. После явного итога сохраним принятие или отказ; при неизвестном результате не будем предлагать новое оформление. Затем проверим одновременные запросы, потерю ответа и доступ к чужому заказу».
Если интервьюер меняет условие, сначала определите затронутое правило. Разрешили разделить заказ на две доставки — прежняя уникальность order_id уже не подходит, нужно согласовать состав и номера отдельных отправлений. Потребовали менять пункт после подтверждения — понадобится договорённость об изменении у курьера. Это новые условия, которых нет в нашем исходном контракте.
Как потренироваться самостоятельно
На первый подход выделите около получаса. Это ориентир для упражнения, а не обязательная длительность интервью. Сначала прочитайте только исходное пожелание о доставке и попробуйте пройти задачу без готового разбора.
- Выпишите уточнения. Кто пользователь, какие данные уже есть, что считается успехом и какие изменения разрешены?
- Согласуйте правила. Для каждого придумайте проверку, по которой команда поймёт, что оно выполнено.
- Нарисуйте записи и обмен. Где находятся постоянные данные, как связаны номера и что останется после остановки процесса?
- Опишите один запрос и два ответа. Сначала создание с ожиданием, затем чтение с подтверждением. Объясните каждый статус и поле.
- Добавьте сбой. Ответ потерян после создания у курьера. Покажите, какой номер сохранится и как продолжится работа. Затем измените одно бизнес-правило и пересмотрите решение.
Что проверить перед следующим интервью
В вашем решении должны связываться цель пользователя, правила, данные и обмен. Проверьте, что из каждого состояния понятен следующий шаг, ответ API соответствует сохранённым данным, а повтор запроса сохраняет ту же операцию. Отдельно назовите, что обеспечивает магазин и чего вы ожидаете от партнёра.
Начните с вопросов, зафиксируйте выбранные условия и доведите один сценарий до понятного результата. Если можете объяснить, что случится при повторе, потере ответа и изменении требования, решение уже даёт основу для содержательного разговора о работе системного аналитика.
