Подготовка к найму в команду продуктов «Корзина», «Покупки», «Вишлисты» и «Семьи» — SQL, эксперименты, e-commerce метрики и продуктовые кейсы
Фишка: Двусторонняя маркетплейс-модель (покупатели + продавцы) и работа над высоконагруженными продуктами с миллионами пользователей. Для этой вакансии — фокус на корзине, покупках, вишлистах и семейных аккаунтах с сетевыми эффектами.
| Этап | Длительность | Что проверяют |
|---|---|---|
| HR-скрининг | 15–20 мин | Опыт, мотивация, зарплатные ожидания, формат работы. Уточняют команду (Корзина/Покупки/Вишлисты/Семьи) и состав следующих этапов. |
| Техническое: SQL | 60–90 мин | Живое кодирование на общем экране: JOIN, CTE, оконные функции, агрегации. Задачи в контексте заказов, товаров, пользователей, продавцов. Важно проговаривать ход мыслей и обсуждать оптимизацию. |
| Техническое: метрики, статистика, эксперименты | 45–60 мин | E-commerce метрики (GMV, воронка, средний чек), дизайн и валидация A/B-тестов, ratio-метрики, статистические критерии. Возможны вопросы по Python (pandas) и ClickHouse/Vertica. |
| Кейс с нанимающим менеджером | 45–60 мин | Разбор бизнес-задачи: падение конверсии, система метрик для нового продукта, оценка гипотез. Обсуждение опыта и совместимости с командой. |
Обязательный минимум
Плюсом будет
Была ли необходимость оптимизировать запросы?
Опишите конкретный кейс: EXPLAIN, индексы, переписывание JOIN, денормализация, партиционирование. Для Ozon важны запросы к большим таблицам заказов и событий.
Как склеить user_id (int) и session_id (text) в один ключ в SQL?
Приведите int к text: user_id::text || '-' || session_id (PostgreSQL) или CAST(user_id AS VARCHAR). Нельзя конкатенировать int напрямую без приведения типа.
Для каждого товара найдите ранг по продажам в его категории
ROW_NUMBER() OVER (PARTITION BY category_id ORDER BY sales DESC). Обсудите разницу RANK vs DENSE_RANK при одинаковых значениях.
Рассчитайте конверсию из просмотра карточки в добавление в корзину по дням
CTE с событиями → COUNT(DISTINCT user_id) на каждом шаге воронки → деление. Учтите LEFT JOIN, чтобы не потерять дни без конверсий.
В чём разница между ClickHouse и MySQL?
ClickHouse — колоночная OLAP-СУБД для аналитики на больших объёмах, быстрые агрегации. MySQL — строковая OLTP для транзакций. В Ozon ClickHouse/Vertica — типичный стек для аналитики.
Совет: На живом SQL-собесе проговаривайте решение вслух: сначала структура (CTE), потом JOIN-логика, потом оптимизация.
Ловушка: Частая ошибка — INNER JOIN вместо LEFT JOIN, когда нужно включить товары/пользователей без событий.
Фишка: Задачи Ozon часто привязаны к e-commerce: заказы, товары, продавцы, воронка корзины.
Какие A/B-тесты проводил?
Структура ответа: гипотеза → метрики (приёмочная + информационные) → дизайн (сплит, размер выборки, длительность) → анализ → решение. Упомяните guardrail-метрики.
Как будешь находить статистическую значимость конверсии между группами A и B?
Z-тест/χ² для пропорций. Проверьте размер выборки, MDE, α. Для ratio-метрик — дельта-метод или бутстрап, не простой t-тест.
Как будешь валидировать результат A/B-теста?
SRM (Sample Ratio Mismatch), AA-тест, проверка однородности групп, стабильность эффекта по сегментам, guardrail-метрики, доверительные интервалы.
В чём разница между приёмочной и информационной метрикой?
Приёмочная (primary) — по ней принимается решение о запуске. Информационные — объясняют механику, не для прямого вывода.
В чём разница между ratio-метрикой и не ratio-метрикой?
Ratio = числитель/знаменатор (средний чек = GMV/заказы). Дисперсия ratio ≠ дисперсии среднего. Нужны специальные методы оценки (дельта-метод, линеаризация).
Зависит ли числитель от знаменателя в ratio-метрике?
Да, для ratio-метрик наблюдения не независимы. Стандартный t-тест по пользовательским средним некорректен — используйте линеаризацию или бутстрап.
Когда использовал Mann-Whitney вместо t-теста?
При ненормальном распределении, выбросах, малых выборках. Mann-Whitney сравнивает ранги, не средние.
Всегда ли вырастет конверсия, если она выросла в A/B-тесте?
Нет. Возможны novelty effect, сезонность, перетекание между группами, Simpson's paradox. Нужна валидация на holdout и мониторинг после раскатки.
Как реализуется дизайн теста?
Гипотеза → выбор unit of randomization (user/session) → расчёт sample size (MDE, power 80%, α=0.05) → длительность (учёт недельной сезонности) → метрики и guardrails.
Будешь делать split по чатам или по пользователям?
По пользователям, если эффект на уровне пользователя. Split по чатам — только если unit of analysis = чат. Для семейных продуктов — обсудите кластерный сплит.
Ловушка: Нельзя применять обычный t-тест к ratio-метрикам (средний чек, CTR) без коррекции.
Фишка: Для вакансии «Семьи» могут спросить про сетевые эффекты: один пользователь влияет на других, нужен кластерный сплит.
Совет: Упоминайте продвинутые методы из вакансии: стратификация, CUPED, поправка на множественное тестирование.
В чём разница между ARPPU и GMV?
GMV — общий объём заказов (gross merchandise value). ARPPU — средняя выручка на платящего пользователя (GMV / paying users). ARPPU уже ratio-метрика.
Что такое CR1?
Конверсия первого уровня воронки — обычно из визита/сессии в целевое действие (просмотр карточки, добавление в корзину). Уточняйте определение в контексте продукта.
Для каждой ли метрики характерна сезонность?
Нет. DAU/сессии — да. Конверсия может быть стабильнее. Учитывайте сезонность при выборе периода A/B-теста.
В какой период проводится тест для DAU?
Минимум 1–2 полных недельных цикла, чтобы сгладить дневную и недельную сезонность. Для DAU — не менее 14 дней.
В чём разница между web-аналитикой и мобильной аналитикой?
Разные трекинг-события, сессии (web — по таймауту, mobile — по foreground/background), атрибуция установок, разные воронки.
По каким метрикам оценить успех нового раздела маркетплейса?
North Star + воронка (просмотр → карточка → корзина → заказ), GMV раздела, доля в общем GMV, retention, unit-экономика.
Фишка: Вакансия фокусируется на Корзине, Покупках, Вишлистах и Семьях — готовьте метрики для этих продуктов: add-to-cart rate, cart abandonment, wishlist conversion, family account adoption.
Совет: Знайте воронку: просмотр → карточка → корзина → оформление → оплата → доставка. Умеете локализовать падение по шагам.
Какие задачи решал на Python?
pandas для обработки данных, numpy для статистики, scipy/statsmodels для A/B, matplotlib/plotly для визуализации. Приведите конкретный пайплайн: extract → transform → analyze.
Работал ли с ClickHouse?
Опишите типовые запросы: агрегации по партициям, ARRAY JOIN, работа с MergeTree. Если нет опыта — покажите понимание отличий от PostgreSQL.
Опыт работы с AirFlow и Git?
AirFlow — DAG-и для ETL/ELT, витрины данных, расписание пайплайнов. Git — версионирование SQL/Python, code review аналитических скриптов.
Совет: Из вакансии: проектирование витрин для дашбордов — опишите star-schema, grain таблицы, идемпотентность пайплайнов.
Расскажи о себе
2–3 минуты: аналитический бэкграунд → ключевые достижения с цифрами → почему продуктовая аналитика → интерес к Ozon.
Расскажи про опыт работы
1–2 проекта по STAR: задача, ваш вклад, метрики результата. Акцент на A/B, SQL, влияние на продукт.
Почему ищешь работу / Почему Ozon?
Конкретика: масштаб маркетплейса, data-driven культура, продукты (корзина, семьи), быстрые циклы экспериментов.
Были ли ситуации, когда твоё мнение было верным в спорной ситуации?
STAR: разногласие с PM/разработкой → аргументы данными → результат. Покажите умение отстаивать позицию конструктивно.
Совет: На HR уточните: какая команда, формат технического этапа, стек (ClickHouse/Vertica), будет ли тестовое задание.
Ранг товара по продажам в категории
Есть таблицы products (product_id, category_id, name) и orders (order_id, product_id, user_id, order_date, amount). Для каждого товара вычислите его ранг по сумме продаж (amount) внутри категории. Выведите product_id, category_id, total_sales, rank.
WITH sales AS ( SELECT p.product_id, p.category_id, SUM(o.amount) AS total_sales FROM products p LEFT JOIN orders o ON p.product_id = o.product_id GROUP BY p.product_id, p.category_id ) SELECT product_id, category_id, total_sales, RANK() OVER (PARTITION BY category_id ORDER BY total_sales DESC) AS rank FROM sales;
Сложность: O(n log n) на партицию из-за сортировки в оконной функции
Конверсия воронки корзины по дням
Таблица events (user_id, event_name, event_date), где event_name ∈ ('view_item', 'add_to_cart', 'purchase'). Рассчитайте дневную конверсию из view_item в add_to_cart и из add_to_cart в purchase.
WITH daily AS (
SELECT event_date,
COUNT(DISTINCT CASE WHEN event_name = 'view_item' THEN user_id END) AS viewers,
COUNT(DISTINCT CASE WHEN event_name = 'add_to_cart' THEN user_id END) AS cart_users,
COUNT(DISTINCT CASE WHEN event_name = 'purchase' THEN user_id END) AS buyers
FROM events
GROUP BY event_date
)
SELECT event_date,
cart_users::float / NULLIF(viewers, 0) AS view_to_cart_cr,
buyers::float / NULLIF(cart_users, 0) AS cart_to_purchase_cr
FROM daily
ORDER BY event_date;Сложность: O(n) — один проход с агрегацией
Retention покупателей по когортам
Таблица orders (user_id, order_date). Для когорты пользователей, совершивших первый заказ в январе 2025, рассчитайте retention в 1-й, 2-й и 3-й месяц (доля вернувшихся).
WITH first_order AS (
SELECT user_id, MIN(order_date) AS first_date
FROM orders
GROUP BY user_id
),
cohort AS (
SELECT user_id, first_date
FROM first_order
WHERE first_date >= '2025-01-01' AND first_date < '2025-02-01'
),
activity AS (
SELECT c.user_id,
DATE_TRUNC('month', o.order_date) AS activity_month,
DATE_TRUNC('month', c.first_date) AS cohort_month
FROM cohort c
JOIN orders o ON c.user_id = o.user_id
)
SELECT
EXTRACT(MONTH FROM AGE(activity_month, cohort_month)) AS month_number,
COUNT(DISTINCT user_id)::float / (SELECT COUNT(*) FROM cohort) AS retention_rate
FROM activity
WHERE activity_month > cohort_month
GROUP BY 1
ORDER BY 1;Сложность: O(n log n) из-за GROUP BY и JOIN
Расчёт статистической значимости конверсии (Python)
Даны два варианта A/B-теста: группа A — 10000 пользователей, 1200 конверсий; группа B — 10000 пользователей, 1350 конверсий. Есть ли статистически значимая разница при α=0.05?
from statsmodels.stats.proportion import proportions_ztest n_a, conv_a = 10000, 1200 n_b, conv_b = 10000, 1350 count = [conv_a, conv_b] nobs = [n_a, n_b] z_stat, p_value = proportions_ztest(count, nobs) # p_value ≈ 0.003 → разница значима при α=0.05 # Относительный лифт: (1350/10000 - 1200/10000) / (1200/10000) ≈ 12.5%
Сложность: O(1)
Каркас ответа
3 дня
7 дней
14 дней
| Блок | Готов, если... |
|---|---|
| SQL | можешь за 15 мин написать запрос с CTE, JOIN 3+ таблиц и оконной функцией, объяснив выбор JOIN |
| A/B-тесты | можешь описать полный цикл теста и объяснить, почему t-тест не подходит для ratio-метрик |
| E-commerce метрики | можешь объяснить GMV, ARPPU, воронку корзины и как локализовать падение конверсии |
| Продуктовые кейсы | можешь за 5 минут структурировать ответ на «конверсия упала на 12%» по фреймворку |
| Python | можешь на pandas посчитать конверсию и z-тест для двух групп |
| Поведенческое | есть 3 истории по STAR с измеримыми результатами |
В день собеседования