Для каждой задачи определите: какое условие каузальности нарушено, почему это проблема, и что нужно сделать.
Команда запускает новый онбординг. Все пользователи, зарегистрированные до 1 марта — контроль (старый онбординг). Все зарегистрированные после 1 марта — тест (новый онбординг). Через месяц сравнивают retention.
Нарушено: рандомизация. Это не A/B-тест, а before/after сравнение. Группы различаются по времени регистрации — любой внешний фактор (маркетинговая кампания, сезонность, изменение трафика) confounds результат.
Что делать: рандомизировать по user_id — каждый новый пользователь с равной вероятностью получает старый или новый онбординг, независимо от даты.
E-commerce маркетплейс тестирует новый алгоритм ранжирования товаров. Рандомизация по покупателям: 50% видят новый алгоритм, 50% — старый. Метрика — GMV per buyer. Через 2 недели тест показывает +8% GMV.
Но: новый алгоритм показывает товары определённых продавцов чаще. Эти продавцы получают больше продаж, а продавцы, чьи товары ушли ниже — меньше.
Нарушено: SUTVA (отсутствие интерференции). Treatment для одних покупателей влияет на товарное предложение для других. Продавцы — общий ресурс: если один покупатель видит товар выше, другой может не увидеть его вовсе.
Следствие: +8% GMV в тесте частично достигнуто за счёт контроля. Реальный ATE при 100% rollout будет значительно ниже — возможно, близок к нулю (zero-sum redistribution).
Что делать: (1) Рандомизация по кластерам (города, регионы). (2) Мониторить метрики продавцов. (3) При rollout сравнить фактический GMV с прогнозом из A/B.
Команда тестирует новый формат рекламного баннера. Рандомизация по пользователям. Метрика: CTR = клики / показы (глобальное отношение). Результат: CTR вырос на +12%.
Но: новый формат занимает больше экранного пространства, из-за чего алгоритм показывает меньше баннеров на пользователя (было 8, стало 5).
Проблема: treatment-affected exposure. Treatment изменил знаменатель метрики (число показов). CTR вырос не потому, что баннер лучше, а потому, что число показов сократилось.
Что делать: (1) Сравнить абсолютное число кликов per user (не ratio). (2) Сравнить число показов между группами. (3) Если показы различаются — CTR невалиден как primary metric.
Эксперимент: новый онбординг увеличивает конверсию в первую покупку. Метрика для оценки эффекта — ARPPU (средняя выручка на покупателя, только среди пользователей, совершивших покупку).
Результат: ARPPU в тест-группе на 15% ниже, чем в контроле. Вывод аналитика: «новый онбординг ухудшает выручку с покупателя».
Нарушено: post-treatment conditioning. ARPPU считается только среди покупателей — это подмножество, определённое post-treatment поведением. Treatment увеличил конверсию → в тесте больше «новых» покупателей с мелкими чеками → средний чек падает.
Следствие: ARPPU ↓ не означает, что treatment плох. Total revenue мог вырасти.
Что делать: использовать ITT-метрику (revenue per user, включая нули) как primary. ARPPU — вспомогательная метрика для диагностики.
Компания тестирует повышение цен в приложении доставки еды. Рандомизация по городам: 10 городов — тест, 10 — контроль. Метрика: средний чек.
Аналитик проводит t-test на уровне пользователей (500K в тесте, 480K в контроле). Получает p = 0.0003. Вывод: «повышение цен значимо увеличило средний чек».
Проблема: неправильная единица анализа. Рандомизация — по городам (кластерам), а анализ — по пользователям. Эффективный N ≈ 20 (число городов), а не 980K.
Следствие: t-test с N = 980K драматически занижает p-value. Реальная значимость: p ≈ 0.15–0.35.
Что делать: cluster-robust SE с кластером = город. Или randomization inference (permutation по 20 городам).
Эксперимент: новый UI checkout-страницы. Рандомизация по user_id. Семейный аккаунт: несколько членов семьи используют один аккаунт.
Часть пользователей контрольной группы обсуждают «странную новую страницу оплаты» в семейном чате (screenshot от пользователя из теста).
Нарушено: (1) Консистентность — если shared device, то один user_id может означать разных людей. (2) SUTVA — информация о treatment «протекает» в контроль через социальные связи.
Следствие: контроль загрязнён. ATE занижен (diluted).
Что делать: (1) Рандомизация по устройству или cookie. (2) Анализировать кластеры пользователей с общими device_id. (3) При значительном leakage — кластерная рандомизация по household.
Социальная сеть тестирует кнопку «Поделиться» в новом формате. Рандомизация по пользователям. Метрика: число shares per user. Тест-группа: +25% shares.
Но: когда пользователь тест-группы делится контентом, его друзья (возможно из контроля) видят этот контент и тоже начинают делиться чаще.
Нарушено: SUTVA. Социальный граф создаёт интерференцию: treatment одного пользователя влияет на поведение его друзей.
Следствие: +25% — переоценка каузального эффекта. Часть роста вызвана spillover.
Что делать: (1) Graph cluster randomization — рандомизировать связанные компоненты. (2) Замерить степень загрязнения контроля: среди пользователей контроля с >50% друзей в тесте shares выше?
Тест: новый алгоритм распределения заказов по курьерам. Рандомизация по заказам. Метрика: время доставки. Из-за бага 20% заказов в тест-группе обрабатывались старым алгоритмом.
Результат: среднее время доставки в тесте на 3% лучше, чем в контроле.
Нарушено: консистентность. 20% тестовой группы фактически получили контрольный treatment. Наблюдаемый Y для них — Y(0), а не Y(1).
Следствие: attenuation bias — реальный эффект больше 3%. ITT = 3%, LATE = 3% / 0.8 = 3.75%.
Что делать: (1) Исправить баг и перезапустить. (2) Оценить LATE через IV. (3) Раскрыть процент non-compliance в отчёте.
Эксперимент: новый дизайн страницы каталога. Рандомизация по сессиям: каждая сессия случайно попадает в тест или контроль. Один пользователь в разных сессиях может видеть разные версии.
Метрика: конверсия (покупка / пользователь).
Проблема: единица рандомизации (сессия) ≠ единица анализа (пользователь). Один пользователь может быть и в тесте, и в контроле — нарушение assignment consistency.
Следствие: (1) Attenuation bias. (2) Непоследовательный пользовательский опыт (novelty/confusion effect). (3) Нельзя атрибутировать покупку конкретной версии.
Что делать: рандомизировать по user_id (sticky assignment). Если нужна session-level рандомизация — метрика тоже session-level.
B2B SaaS: тестируют новый ценовой план. Рандомизация по компаниям (100 + 100). Метрика: revenue per company. Результат: +22% revenue в тесте.
Но: 3 крупных enterprise-клиента, попавших в тест, сгенерировали 60% revenue тестовой группы.
Проблема: (1) Малая выборка (200 компаний). (2) Экстремальный heavy tail — 3 компании определяют результат. (3) Баланс по размеру случаен и ненадёжен.
Следствие: +22% может быть целиком объяснён тем, что крупные компании случайно попали в тест. Повторная рандомизация может дать −15%.
Что делать: (1) Block randomization по размеру. (2) Winsorization / trimmed mean. (3) Permutation test. (4) Проверить результат без 3 крупнейших компаний.
Эксперимент: новая модель рекомендаций. Рандомизация по пользователям. Аналитик анализирует конверсию только среди пользователей, просмотревших ≥5 товаров (аргумент: «неактивные зашумляют результат»).
Treatment влияет на то, сколько товаров пользователь просматривает (рекомендации стали релевантнее → больше просмотров).
Нарушено: post-treatment conditioning. «Просмотр ≥5 товаров» — post-treatment переменная, зависящая от treatment. Фильтрация создаёт разный состав подвыборок.
Следствие: в тесте подвыборка включает «новых активных» пользователей, в контроле — только изначально активных. Сравнение смещено.
Что делать: (1) ITT — анализировать всех. (2) Сегментировать по pre-treatment активности. (3) Просмотры → secondary metric, не фильтр.
Две команды одновременно запускают эксперименты на одних и тех же пользователях. Команда A тестирует новый checkout. Команда B — новую цену. Рандомизация независимая.
Команда A видит +5% CR. Но в подгруппе, где одновременно активен тест B — CR +12%. Без теста B — CR +1%.
Проблема: interaction effect. Два treatment взаимодействуют: новый checkout хорошо работает с новой ценой, но плохо — со старой. Средний эффект (+5%) маскирует гетерогенность.
Следствие: если запустить только checkout без новой цены — реальный эффект +1%, а не +5%.
Что делать: (1) Factorial design (2×2) — анализировать interaction term. (2) Принимать решение с учётом второго эксперимента. (3) При сильном interaction — запускать последовательно.
Эксперимент: новая фича повышает retention. Аналитик измеряет Day 30 retention. Но: в тест-группе 5% пользователей деинсталлировали приложение на Day 3 (из-за багов в новой фиче). Эти пользователи не учитываются в Day 30 retention.
Результат: Day 30 retention в тесте = 42%, в контроле = 38%. Вывод: «фича улучшает retention».
Нарушено: survivor bias (форма post-treatment conditioning). Пользователи, ушедшие из-за бага, исключены из анализа. Оставшиеся — «выжившие», изначально более лояльные.
Следствие: 42% — retention среди выживших. С учётом ушедших: 42% × 0.95 = 39.9% — практически как в контроле.
Что делать: (1) ITT: считать retention по всем, включая деинсталлировавших (retention = 0). (2) Мониторить uninstall rate как guardrail. (3) Если uninstall rate выше — red flag.