Модуль 12.3. Техническое собеседование: SQL, API, UML/BPMN
Разбор live-интервью для junior/middle/senior. Пошаговые сценарии, примеры заданий, эталонные подходы, рубрика оценивания. Бонус: как упаковать практику (Яндекс Практикум/Stepik/Otus/SkillFactory) в портфолио.
Техническое собеседование на системного аналитика (SA) обычно проверяет три оси:
Данные (SQL/ER) → Интеграции (API/контракты) → Поведение (UML/BPMN).
Ваша задача — быстро структурировать требования, сделать разумные допущения, показать внимание к краям (идемпотентность, ошибки, таймауты, NULL/дубликаты), и донести решения чётко и измеримо.
Формат live-интервью и таймбокс
Junior (45–60 мин)
- 10 мин — ввод/контекст, мини-ER.
- 15 мин — SQL (2 задачи, JOIN + агрегация).
- 15 мин — API (1 эндпоинт + коды ошибок).
-
10 мин — BPMN или Sequence (1 сценарий).
Оценка: точность терминов, аккуратность, способность думать вслух.
Middle (60–75 мин)
- 10 мин — уточняющие вопросы/границы.
- 20 мин — SQL (окна/дедуп/SCD намёк).
- 20 мин — API (идемпотентность, пагинация, события).
-
15 мин — BPMN с таймаутом/ошибкой + State.
Оценка: trade-off’ы, контракты, обработка ошибок, RTM-мышление.
Senior (75–90 мин)
- 10 мин — постановка/риски/CCB.
- 20 мин — SQL (CDC/дедуп, лаги, MERGE-идея).
- 25 мин — API + событийная модель + версия/депрекейшн.
-
20 мин — BPMN/Sequence с compensation + NFR/наблюдаемость.
Оценка: архитектурное мышление, эволюция, SLO/SLA, компромиссы.
SQL на собеседовании: задачи, подходы, подводные камни
Мини-датасет (используем в примерах)
customers(id, email, created_at) orders(id, customer_id, status, total_amount, currency, created_at) order_items(id, order_id, sku, qty, unit_price) payments(id, order_id, status, amount, currency, psp_ref, event_time) refunds(id, payment_id, amount, event_time)
Инварианты: деньги — DECIMAL(18,2); status ∈ {PLACED, PAID, SHIPPED, DELIVERED, CANCELLED}.
Типовые вопросы и эталонные подходы
(J) SQL-1. Заказы и суммы по клиентам за последние 30 дней
Подсчитать TOP-5 клиентов по сумме оплаченных заказов.
Подход: фильтр по payments.status='CAPTURED', дедуп по payment_id, сумма по customer.
SELECT o.customer_id,
SUM(p.amount) AS paid_amount
FROM payments p
JOIN orders o ON o.id = p.order_id
WHERE p.status = 'CAPTURED'
AND p.event_time >= now() - INTERVAL '30 day'
GROUP BY o.customer_id
ORDER BY paid_amount DESC
LIMIT 5;
Подводные камни: частичный capture, валюта — оговорить конвертацию/отсутствие.
(M) SQL-2. Дедуп по последнему событию (окна)
Оставить последнюю запись по payment_id (устранить дубль событий).
SELECT *
FROM (
SELECT p.*,
ROW_NUMBER() OVER (PARTITION BY p.id ORDER BY p.event_time DESC) AS rn
FROM payments p
) s
WHERE rn = 1;
(M) SQL-3. Конверсия из заказов в оплату (день, p95 latency намёк)
Посчитать дневную конверсию: paid_orders / placed_orders.
WITH placed AS (
SELECT date_trunc('day', created_at) d, COUNT(*) c
FROM orders WHERE status IN ('PLACED','PAID','SHIPPED','DELIVERED')
GROUP BY 1
),
paid AS (
SELECT date_trunc('day', o.created_at) d, COUNT(DISTINCT o.id) c
FROM payments p
JOIN orders o ON o.id = p.order_id
WHERE p.status='CAPTURED'
GROUP BY 1
)
SELECT p.d,
p.c AS placed,
COALESCE(pa.c,0) AS paid,
COALESCE(pa.c,0)::decimal / NULLIF(p.c,0) AS conversion
FROM placed p
LEFT JOIN paid pa ON pa.d = p.d
ORDER BY p.d;
Дальше обсудите latency (в BI — поздние события → watermark/лаг).
(S) SQL-4. MERGE идея (Silver→Gold) с дедупом по окнам
Описать, как идемпотентно грузить fact_payments из payments_raw.
Ожидаемый ответ: ROW_NUMBER() + MERGE ... WHEN MATCHED ... WHEN NOT MATCHED ..., ключ payment_id. Обсудить tombstones delete.
(M) SQL-5. ABC по SKU — доля выручки и флаг A/B/C.
Ответ: окно SUM(amount) OVER (ORDER BY amount DESC) / total; границы 80/95%.
Типичные ошибки:
- FLOAT для денег; отсутствие NULL-safe деления; забыли фильтр статуса; JOIN без кардинальности → дубли; неявная конвертация валют; отсутствие допущений (assumptions).
API/контракты: что проверяют и как отвечать
Быстрый словарь, который ждут
- Идемпотентность (Idempotency-Key + реестр/уникальный индекс).
- Пагинация курсором, стабильная сортировка, лимиты.
- Ошибки: machine-readable (code/message/details), 201/202/409/422/429.
- Безопасность: OAuth2/JWT, scopes, минимизация PII, rate limit.
- События: schema + semver, минимальный payload, correlationId.
Типовое задание (M/S): «Invoice→Payment с асинхронным PSP»
Спросите перед решением: нужны ли partial capture/refund? какие валюты? SLA PSP?
Мини-контракт (фрагменты OpenAPI):
openapi: 3.0.3
info: {title: Billing API, version: 1.0.0}
paths:
/v1/invoices:
post:
summary: Create invoice
parameters:
- in: header
name: Idempotency-Key
required: true
schema: {type: string, maxLength: 128}
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [orderId, amount, currency]
properties:
orderId: {type: string, format: uuid}
amount: {type: string, pattern: "^[0-9]+(\\.[0-9]{2})$"}
currency:{type: string, enum: [RUB, USD, EUR]}
responses:
"201": {description: Created}
"409": {description: Duplicate, return previous result}
"422": {description: Validation}
/v1/payments:
post:
summary: Create payment for invoice
parameters:
- in: header
name: Idempotency-Key
required: true
schema: {type: string}
responses:
"201": {description: Authorized or Captured}
"202": {description: Accepted (async PSP)}
"422": {description: Validation}
Событие (JSON Schema, выдержка):
{
"title": "payment.captured.v1",
"type": "object",
"required": ["eventId","occurredAt","paymentId","invoiceId","amount","currency"],
"properties": {
"eventId": {"type":"string","format":"uuid"},
"occurredAt": {"type":"string","format":"date-time"},
"paymentId": {"type":"string","format":"uuid"},
"invoiceId": {"type":"string","format":"uuid"},
"amount": {"type":"string"},
"currency": {"type":"string"},
"correlationId": {"type":"string"}
}
}Что ещё проговорить: 202 + job/status, retry+backoff, дедуп вебхуков, версионирование (v1 → additive only), deprecation policy.
Быстрые задания на сравнение
- REST vs GraphQL vs gRPC (когда что; N+1 и кэш, бинарные протоколы).
- Offset vs Cursor (изменяемые наборы → cursor).
- PUT vs PATCH (полная замена vs частичное, ETag/If-Match).
- Идемпотентность create (ключ + реестр → тот же ответ).
Красные флаги: 200 вместо 202; свободный JSON события; PII в событиях; отсутствие default ошибок; «клиент пускай не жмёт дважды».
UML/BPMN: что рисовать «на доске»
Sequence (M/S): «Checkout→PSP→Callback»
Ожидают lifelines, alt/opt-ветки, таймаут/ошибка, корреляция, идемпотентность.
Мини-скелет (текстом):
- Client → OrdersAPI: POST /orders (Idempotency-Key).
- OrdersAPI → PaymentsAPI: POST /payments.
- alt: (Message) PSP→Webhook: payment.captured; (Timer) 25s → return 202.
- PaymentsAPI → EventBus: payment.captured.v1.
- OrdersAPI ← EventBus: обновить статус заказа.
BPMN (J/M): Event-based gateway + Boundary timer
- Пулы: Customer, Checkout, Payments, Warehouse, Carrier.
- На «Авторизовать платеж» — Boundary Timer (таймаут PSP) → 202 + очередь.
- Event-based gateway: «сообщение callback» ИЛИ «таймер».
- На «Доставить» — Timer breach SLA → эскалация/компенсация.
State (M): жизненный цикл Payment/Order
- Payment: NEW → AUTHORIZING → AUTHORIZED → CAPTURED → REFUNDED/FAILED
- Инвариант: refunded_amount ≤ captured_amount.
Анти-паттерны: XOR вместо Event-based; Sequence между пулами; нет End; нет таймеров/ошибок.
Сценарии live-интервью (пошагово)
Скрипт для Middle (пример на 60–70 мин)
- Разминка (5 мин): «Опишите разницу BA/SA в одном абзаце».
-
SQL (20 мин):
- A1: TOP-5 клиентов по оплаченной сумме за 30 дней.
-
A2: Дедуп событий платежей (последнее по id).
Оценка: корректность JOIN, окна, допущения.
- API (20 мин):
- Спроектируйте POST /payments + событие payment.captured.v1.
- Что вернём при таймауте PSP? (ожидаем 202 + retry/job).
- BPMN (15 мин): Collaboration «Заказ-Оплата-Доставка» с Event-gateway и SLA таймером.
- Q&A/NFR (10 мин): p95/p99/окно, алерты, деградация.
Рубрика оценивания (суммарно 100)
- SQL (30): корректность (15), окна/дедуп (10), допущения (5).
- API (35): идемпотентность (10), ошибки/коды (8), 202/асинхрон (8), пагинация/лимиты (5), безопасность (4).
- UML/BPMN (25): события/таймеры (10), message vs sequence (5), читаемость/именование (5), конечность/дефолт (5).
- Коммуникация/структура (10): вопросы/резюме/риски.
Риски на интервью и как их гасить
|
Риск |
Как проявляется |
Что делать (тезисы ответа) |
|---|---|---|
|
«Ушёл в детали» |
Не успеваете |
«Предлагаю допущения A/B, приму A. Остальные — в backlog» |
|
Нет допущений |
Застряли |
Скажите 2–3 Assumptions и продолжайте |
|
Спор про вкусы |
Холивар |
Дайте критерии выбора (нагрузка/совместимость/время), выберите опцию |
|
«Вода» в NFR |
«Быстро/надёжно» |
Формула SLO: метрика + окно + метод проверки |
|
Путаете message/sequence |
Плохая интеграция |
Напомните: между пулами — только Message Flow |
Вопрос–Ответ (частые на техсобесах)
В: Что такое идемпотентность create и как её реализовать?
О: Ключ Idempotency-Key + реестр (уникальный индекс) → повтор возвращает предыдущий результат; TTL/границы описать в контракте.
В: Когда 202 уместен?
О: Долгие/неопределённые операции (PSP, экспорт, ML инференс). Возвращаем job/status, публикуем событие «pending».
В: Чем cursor лучше offset?
О: В изменяемых наборах cursor устойчив к вставкам/удалениям, нет «дыр/дублей», масштабируется.
В: Зачем Event-based gateway в BPMN?
О: Когда выбор ветки зависит от внешнего события/времени, а не значения данных.
В: Как считаете дедуп событий в данных?
О: Окно ROW_NUMBER() OVER (PARTITION BY key ORDER BY op_ts DESC)=1 + идемпотентный MERGE.
Бонус: как превратить практику из курсов в портфолио SA
(без обзора курсов; только навигация по практике)
Упаковка «Docs-as-Code» (универсально для Практикум/Stepik/Otus/SkillFactory)
portfolio/ readme.md # 1 страница: домен, цели, метрики vision/vision.md srs/srs.md # + changelog.md processes/bpmn_*.bpmn rules/dmn_*.dmn contracts/openapi.yaml # + events schemas data/er.puml data/dictionary.md quality/nfr_catalog.md quality/observability.md tests/ac_bdd/*.feature rtm/rtm.csv demo/sequence_*.puml
Принцип: каждый учебный проект — релизный пакет (минимум: Vision→SRS→BPMN/DMN→ER→OpenAPI→NFR→RTM→AC/BDD).
Что показывать на интервью (3–5 мин питч)
- Проблема/метрика («подняли on-time с 86% до 93% в модели» — пусть симулированно, но с цифрой).
- Контракт/диаграмма (1 слайд BP/Seq, 1 слайд OpenAPI с 202/idempotency).
- Данные (ER + DQ-правила).
- NFR/наблюдаемость (SLO + как проверяли).
- Чему научились (1 слайд с анти-паттерном и исправлением).
Карта навыков → артефакты
- SQL/данные: SQL-файл с запросами (JOIN/окна/MERGE), ER и словарь данных.
- API: OpenAPI + события (JSON Schema) + примеры ошибок.
- BPMN/UML: Collaboration с Event-gateway/Boundary/Compensation, Sequence с alt/opt.
- Качество: NFR/SLI/SLO + план нагрузки, RTM CSV.
Памятка кандидату (перед входом в Zoom)
- Выпишите 5 шаблонов: Assumptions, SLO формула, Idempotency ключ, Cursor пагинация, ROW_NUMBER дедуп.
- Держите «рыбу» BPMN (пулы/события/таймеры/compensation).
- Говорите числами: p95/p99, окна, лимиты.
- Каждую часть завершайте резюме решения (1–2 фразы).
- Если не знаете — проговорите критерии выбора и нарисуйте план валидации.
Памятка интервьюеру (как честно оценивать)
- Отделяйте термины от навыков решения проблем.
- Проверьте 3 вещи: идемпотентность/асинхрон, дедуп/окна, события/таймеры.
- Просите assumptions; без них «идеального» решения не бывает.
- Оценивайте коммуникацию: резюме, риск-регистр, план верификации.
Шпаргалка формулировок
- SLO: «В 08:00–23:00 p95 POST /payments ≤ 900 мс. Проверяем нагрузочным тестом + SLI из прод-подобного стенда».
- Идемпотентность: «Создающие операции с Idempotency-Key; повтор → тот же ответ. Реестр ключей + уникальный индекс».
- Пагинация: «Cursor: base64(lastId, createdAt), стаб. сортировка по (createdAt, id)».
- Дедуп: «ROW_NUMBER() OVER (PARTITION BY payment_id ORDER BY op_ts DESC)=1».
- BPMN таймаут: «Event-based gateway: Message vs Timer 25 с; на задаче Boundary Timer → 202 + очередь».



