System design: case studies (E2)
Один каркас — жизненный цикл рекомендательной системы с петлями — и четыре продукта, отвечающие на одни и те же вопросы по-разному: видео-платформа, короткая лента, маркетплейс, новости. Включи режим самопроверки — и сформулируй свои ответы, прежде чем раскрывать готовые.
⚠ Разборы — честный конспект инженерной практики и типовых trade-offs, а не описание внутренних систем реальных компаний. Каждая стадия ссылается на модуль учебника, где она построена вживую.
Каркас: жизненный цикл рекомендательной системы
Один и тот же скелет для любого продукта — с петлями: офлайн не устроил → назад к данным; мониторинг поймал дрейф → снова данные и ретрейн. Ниже четыре продукта отвечают на одни и те же вопросы по-разному.
Петли: 6 → 2 (офлайн-метрики не устроили — чините данные и лейблы, не слои) · 10 → 2 (дрейф/петля обратной связи — новые данные, ретрейн). Цикл не заканчивается деплоем.
Как пользоваться: выбери продукт → включи самопроверку → для каждого шага сначала ответь сам → раскрой решение → сравни этот же шаг между продуктами (таблица ниже).
🎬 Design: Видео-платформа
длинное видео, YouTube-подобная
❓ Вопросы себе перед этим шагом (9)
- Какую продуктовую проблему решаем и как выглядит успех через год (а не через неделю)?
- Кто пользователи и в каких сценариях они встречают рекомендации? Откуда берутся айтемы — платформа двусторонняя (продавцы/авторы)?
- Какие действия возможны и что из них — сигнал ценности, а что шум (клик ≠ удовлетворённость)?
- Какая одна метрика (north-star) отражает долгосрочную ценность — и можно ли её «накрутить» нежелательным поведением?
- Какие guardrails нельзя просадить? Есть ли этические / регуляторные / well-being ограничения?
- Что нужно ЛОГИРОВАТЬ, чтобы north-star и guardrails были измеримы (dwell, возвраты, позиции)? — это техзадание шагу 2.
- Где в продукте живёт выдача (surface) и что там было до рекомендаций — какой baseline опыта?
- Какой масштаб: юзеров, айтемов, RPS? Как быстро оборачивается каталог (годы / дни / часы)?
- Что случится, если рекомендации просто выключить? Какая доля ценности продукта реально на них — от этого зависит бюджет всей системы.
- Цель — долгосрочное удержание через ценность просмотра, а не клики: north-star — «satisfied watch time» (время просмотра, взвешенное сигналами удовлетворённости), потому что сырой CTR оптимизируется кликбейтом.
- Пользователи: зрители с длинной историей вкуса. Айтемы: видео (каталог в сотни миллионов, гигантский long-tail, айтем живёт годами). Действия: показ → клик → доля досмотра, лайк/дизлайк, подписка, «не интересно».
- Guardrails: жалобы/дизлайки, доля кликбейта (клик без досмотра), разнообразие каналов, время до первого качественного просмотра.
❓ Вопросы себе перед этим шагом (6)
- Какие события уже логируются, а какие надо НАЧАТЬ логировать (impressions? позиции? surface? контекст запроса)?
- Можем ли отличить «показали и не заинтересовался» от «не показали»? Есть ли наблюдаемые негативы, или лог positive-only?
- Показ = реально увиден? Что с viewability и позицией в ленте?
- Какие идентификаторы (логин / аноним / кросс-девайс) — и что с PII, приватностью, согласиями?
- Какие смещения зашиты в лог текущей системой (position bias, exposure bias) — и что мы потом с ними сделаем?
- Пишем ли model_version / experiment_id в каждое событие (без этого потом не отладиться)?
- Логируем impressions с позицией и поверхностью (главная / сайдбар / поиск) — position bias тут реален и его придётся вычитать.
- Watch-прогресс пишем тиками (25/50/75/100%), не одним числом в конце — сессии обрываются.
- Контекст запроса: устройство, время суток, вход с уведомления или органика — всё это фичи и срезы для аналитики.
Вживую в учебнике: схема event-лога и position bias — B1
❓ Вопросы себе перед этим шагом (7)
- Что считаем позитивом — клик, dwell, покупка? Не учим ли мы кликбейт этим выбором?
- Откуда негативы: наблюдаемые (скипы), impression-aware или сэмплированные — и какое смещение каждый вариант вносит?
- Как атрибутируем целевое действие к показу (окно атрибуции)? Есть ли delayed feedback?
- Split строго по времени? Нет ли утечки будущего — в том числе через ФИЧИ (агрегаты, посчитанные по всему логу)?
- Какие фичи считаются офлайн, какие в real-time — и считаются ли они ОДИНАКОВО в train и serving?
- Дубли, боты, автоплей, аномалии — как чистим? Нужна ли сессионизация?
- Повторное потребление: seen-айтемы — валидный таргет (музыка, POI) или их надо убирать (фильмы)?
- Лейбл — не «клик», а «satisfied watch»: доля досмотра с порогом, зависящим от длины видео (30 сек шортса ≠ 30 сек двухчасовой лекции), калибруется опросами.
- Негативы — impression-aware: показали и не кликнул / кликнул и бросил. «Не показали» негативом не считаем.
- Split — только временной: глобальная отсечка, никакого random (утечка будущего).
Вживую в учебнике: почему только temporal split — B2
❓ Вопросы себе перед этим шагом (7)
- Какая офлайн-метрика лучше всего аппроксимирует north-star (заданный на шаге 1) — и в каких случаях они разойдутся?
- Что из желаемого протокола данные вообще позволяют? Нет impressions → нет честных негативов; нет propensities → нет IPS.
- Что мерим на retrieval (recall пула — не потеряли ли), а что на ранкере (NDCG/MRR верха)?
- Один релевантный айтем на юзера или много? (тогда recall = hit-rate, а precision@K ≤ 1/K — как читать значения?)
- Чем ловим схлопывание в популярное: coverage, novelty, diversity?
- Что принципиально мерится только онлайн (retention, конверсия) — и не обманываем ли себя офлайн-прокси?
- С какими baseline сравниваем (popularity, прошлая модель) и на одном ли протоколе?
- Офлайн: recall@K по стадии retrieval (не потеряли ли хорошее), NDCG/hit ранкера на watch-лейблах, coverage/novelty как анти-схлопывание.
- Онлайн-only: watch time на пользователя, возвраты (Day-1 / Day-7 retention), доля отписок от рекомендаций — это не посчитать на истории.
- Помним про offline↔online gap: офлайн-выигрыш — фильтр гипотез, решение принимает A/B.
Вживую в учебнике: какая метрика что ловит — /metrics
❓ Вопросы себе перед этим шагом (8)
- Можно ли скорить весь каталог одной моделью, или масштаб требует воронку retrieval → ranking → re-rank?
- Какие candidate sources и зачем несколько — что уникального добавляет каждый (recall-ценность)?
- Какие фичи реально доступны ранкеру в момент запроса (и не подглядывают ли они в будущее)?
- Что делает re-rank: разнообразие, дедуп, бизнес-правила, sponsored — и что из этого ДОЛЖНО отработать до ранкера (eligibility)?
- Как отвечаем холодному юзеру / айтему / при отказе компонента — какова лестница fallback целиком?
- Где нужен exploration и какой у него бюджет? Как новый контент получает первые показы?
- Есть ли контекст запроса (гео, устройство, время) и куда его класть — query-башня / ранкер (item-башня предрассчитана)?
- Build vs buy: что берём готовым (ANN-библиотека, feature store, vector DB), а что пишем сами — и почему?
- Retrieval из нескольких источников: two-tower по истории просмотров, co-watch item-CF, подписки, свежее/трендовое — union + provenance.
- Ranker — multi-task: pWatch, pLike, pDislike, pReport → комбинируются в value-модель (веса — продуктовое решение, не ML).
- Re-rank: разнообразие каналов/тем (MMR-подобное), дедуп почти-дублей, свежие слоты.
- Cold-start: новый юзер — onboarding-интересы + популярное по гео; новое видео — контентные фичи канала + exploration-бюджет.
Вживую в учебнике: воронка вживую — системный дизайн · лестница fallback — B5
❓ Вопросы себе перед этим шагом (6)
- Насколько обучающие данные смещены текущей системой (учимся на том, что сами показали)?
- Каков честный протокол оценки — и кому он льстит (напр., popularity при пороге «понравилось»)?
- Бьём ли простые baseline? Если нет — что чиним ПЕРВЫМ: данные, лейблы, фичи — и лишь потом модель?
- Какие абляции покажут, что реально несёт сигнал (а не «коэффициент большой»)?
- Офлайн растёт, а есть ли риск, что мы просто переобучились на прокси-метрику?
- Воспроизводимость: зафиксированы ли данные, сиды, версии — можно ли повторить число из отчёта?
- Обучение на собственных логах смещено экспозицией: то, что не показывали, не имеет лейблов — контролируем через exploration-трафик и взвешивание.
- Валидация — временная, с бейзлайнами: не бьём popularity/предыдущую модель — не выкатываем.
- Если офлайн не устраивает — чаще всего проблема в лейблах и фичах, а не в архитектуре: возвращаемся к шагам 2–3, а не наращиваем слои.
⚠️ Классическая ошибка: месяц тюнить архитектуру ранкера, когда лейбл «watch» не отделяет автоплей от осознанного просмотра.
❓ Вопросы себе перед этим шагом (6)
- Какой latency-бюджет на запрос и как он делится по стадиям воронки?
- Что предрассчитано офлайн (эмбеддинги, индексы, кандидаты) и с какой частотой обновляется?
- Какие eligibility-фильтры обязаны отработать ДО ранжирования (наличие, регион, возраст, safety)?
- Что происходит при отказе каждого компонента — есть ли graceful degradation и куда падаем?
- Как выкатываем (shadow → канарейка → 100%), есть ли parity-тесты фич — и за сколько минут откатимся?
- Где живёт состояние (сессия, кеши) и что случится при рестарте?
- Item-эмбеддинги предрассчитаны и лежат в ANN-индексе; user-вектор считается на запрос.
- Latency-бюджет делится по стадиям (retrieval единицы мс через ANN, ранкер — основная часть); деградация — сначала урезать кандидатов, потом fallback на популярное.
- Раскатка новой модели — shadow → канарейка → 100%, с parity-тестами фич (train/serve skew).
Вживую в учебнике: почему ANN и что он даёт — two-tower
❓ Вопросы себе перед этим шагом (6)
- Какая целевая метрика эксперимента, какие guardrails остановят выкатку — и зафиксированы ли критерии решения ДО запуска?
- Какая нужна мощность: размер выборки, длительность — с учётом novelty-эффекта и отложенных эффектов (retention)?
- Как делим трафик? Нет ли interference между группами (общий инвентарь, соцграф, петли)?
- Годится ли interleaving как дешёвая проверка ранжирования до полного A/B?
- Что делаем при противоречии «офлайн лучше — онлайн хуже»? (обычно это skew или смещённый офлайн)
- Меняет ли эксперимент сами данные для будущего обучения (петля через логи)?
- A/B: целевая — satisfied watch time / retention; guardrails — жалобы, доля кликбейта, coverage.
- Novelty-эффект: новая модель первое время выигрывает просто новизной выдачи — эксперимент держат недели.
- Interleaving — дешёвый способ сравнить ранкеры на коротком горизонте; долгосрочные эффекты (retention, доверие) он не ловит — их проверяет A/B.
❓ Вопросы себе перед этим шагом (6)
- Что мониторим на каждом слое: данные (свежесть, дрейф, объём) / модель (score-распределение, coverage) / система (p99, ошибки, fallback-rate — «не empty» ещё не значит «здоров») / бизнес?
- Какие РАННИЕ диагностические сигналы поймают проблему до просадки выручки (freshness lag, PSI, p99)?
- На каких срезах может прятаться деградация при стабильном среднем (новые юзеры, хвост, регион)?
- С каким baseline-окном сравниваем (тот же день недели / сезон), чтобы промо и сезонность не давали ложных алертов?
- Как заметить петлю обратной связи: head-share / coverage / Gini ВО ВРЕМЕНИ, а не одним средним?
- Пороги алертов: кто получает, что в ранбуке, не устанем ли от ложных срабатываний?
- Данные: freshness lag фич и ANN-индекса (устаревший индекс = невидимые новые видео).
- Модель: дрейф распределения score, head-share/coverage во времени — ловим петлю «богатые богатеют».
- Система: p99 по стадиям, fallback-rate (сколько юзеров тихо получили популярное).
Вживую в учебнике: симулятор инцидентов — B7
❓ Вопросы себе перед этим шагом (7)
- Как часто ретрейним по плану — и что триггерит внеплановый ретрейн (дрейф-алерт)?
- Как система размечает новый контент — работает ли exploration-конвейер постоянно?
- Что в системе усиливает само себя (петли популярности, промо-петля) и что этому противопоставлено?
- Главные failure modes этого продукта — как каждый из них поймать заранее (монитор/guardrail)?
- Какой техдолг копится (фичи, логи, индексы, старые эксперименты) и когда его чистим?
- Что попадает в постмортем после инцидента и превращается ли он в новый монитор/тест?
- Когда продукт изменится (новый surface, новый тип айтемов) — какие части системы переживут это, а какие придётся переделать?
- Ретрейн по каденсу (сутки/недели) + непрерывное дообучение лёгких частей; тяжёлые башни — реже.
- Петля обратной связи — главный хронический риск: без exploration хвост умирает, метрики какое-то время даже растут.
- Failure modes: кликбейт-спираль (оптимизация кликов), rabbit holes (узкие туннели интересов), каннибализация подписок рекомендациями.
Главное — satisfied watch time и долгосрочная ценность, а не клики. Архитектура: multi-source retrieval → multi-task ранкер (value-модель) → re-rank на разнообразие и свежесть. Главные риски — кликбейт-спираль, rabbit holes и петля популярности; лечатся лейблом качества, diversity и exploration.
🧠 Проверь себя: Почему north-star видео-платформы — не CTR, хотя клик проще всего мерить?
Сравнение: один вопрос — четыре продукта
Выбери стадию каркаса — и посмотри, как продукт меняет ответ. В этих различиях — суть системного проектирования: каркас один, решения продуктовые.
📖 Мини-глоссарий терминов из кейсов (13)
- OOS
- out-of-stock — товар закончился; OOS-доля в выдаче = сколько рекомендованного нельзя купить прямо сейчас
- out-of-stock
- товар закончился; доля таких в выдаче — прямой прокси сломанного инвентарь-пайплайна
- CUPED
- метод снижения дисперсии в A/B за счёт пред-экспериментальных данных пользователя — эксперимент быстрее достигает значимости
- rabbit hole
- «кроличья нора»: система загоняет пользователя во всё более узкий туннель однотипного контента
- dwell
- время, реально проведённое с контентом — отличает «кликнул и закрыл» от «прочитал/посмотрел»
- replay
- офлайн-прогон модели по недавним залогированным событиям («как если бы она работала тогда») — оценка на свежих срезах
- creator-side
- метрики стороны авторов: получают ли новые авторы показы, как быстро набирается первая аудитория
- promo-aware
- с поправкой на промо: сравниваем с сопоставимым периодом (та же распродажа / день недели), а не с «вчера»
- delayed feedback
- отложенный отклик: целевое действие (покупка) случается спустя дни после показа — на момент обучения часть лейблов ещё «не произошла»
- GMV
- gross merchandise value — суммарный оборот товаров через площадку
- novelty-эффект
- временный всплеск метрик от новизны выдачи: «новое» кликают, потому что оно новое; проходит за дни-недели
- interleaving
- смешивание выдач двух ранкеров в один список: чьи айтемы кликают чаще, тот и лучше — чувствительнее A/B, но только про ранжирование
- seller health
- здоровье экосистемы продавцов: разнообразие, выживаемость новых, отсутствие монополизации выдачи
Теория простым языком
Спроектировать рекомендательную систему — значит пройти весь жизненный цикл: от бизнес-задачи до мониторинга — и обосновать, почему для этого продукта ответы именно такие. Выше — один каркас и четыре продукта; здесь — как этим пользоваться.
▸Зачем case studies, если модули уже пройдены
Как применять каркас (метод)
1. Начинай с шага 1, а не с модели — и метрики успеха задавай там же. North-star и guardrails объявляются вместе с задачей, до сбора данных: логирование проектируется под метрику, а не наоборот. Половина решений определяется этим выбором — satisfied watch time против GMV дают разные системы при одинаковых алгоритмах. А шаг 4 — не «выбор метрик», а протокол офлайн-оценки: он стоит после данных, потому что ограничен тем, что лог реально содержит (нет impressions — нет честных негативов, нет propensities — нет IPS).
2. Проговаривай данные до архитектуры. «Какие сигналы у нас есть и каких нет» — вопрос, который отделяет зрелый дизайн: без impressions нет честных негативов, без позиций не вычесть position bias.
3. Называй петли. Офлайн не устроил → чинить лейблы/данные (шаги 2–3), не наращивать слои. Мониторинг поймал дрейф → ретрейн и новые данные. Цикл не заканчивается деплоем.
4. Заканчивай failure modes. Кликбейт-спираль, петля популярности, распроданный топ, старая новость наверху — умение назвать, как система ломается, ценится выше лишнего слоя в ранкере.
Что общее у всех четырёх (и почти любого продукта)
Многостадийная воронка (retrieval → ranking → re-rank), impression-aware лейблы (B1), временная валидация (B2), лестница cold-start (B5), мониторинг по слоям (B7) и петля обратной связи как хронический риск. Различия — в весах: новостям свежесть важнее персонализации, маркетплейсу наличие важнее релевантности, короткой ленте exploration важнее точности.
⚠️ Что может пойти не так
- Начинать проектирование с модели («возьмём трансформер») — сначала цель, данные и метрики, модель предпоследняя.
- Один дизайн на все продукты: если ответ для новостей совпал с ответом для маркетплейса — ты не учёл продукт.
- Забыть онлайн-часть: дизайн без A/B, мониторинга и failure modes — половина системы.
- Выдавать разборы за устройство реальных компаний: это конспект практики, детали везде свои.
- Игнорировать двусторонность платформ: fairness / exposure-diversity guardrails — часть дизайна, и в зависимости от продукта это продавцы (marketplace), авторы (короткая лента), источники и точки зрения (новости) или каналы (видео).
Где это в дорожной карте
Это E2 — case studies: замковый камень поверх модулей. Дальше — E3 capstone: собрать один такой пайплайн руками на реальном implicit-логе (D2a/D2b уже дают данные и ранкер), и Фаза 14 — честная онлайн-оценка (A/B, interleaving, IPS/DR).