Почему A/B-тесту нельзя доверять: SRM, подглядывание и другие ошибки
Практический чеклист качества A/B-теста: как заметить перекос трафика, подглядывание в результаты, множественные проверки и поломку трекинга.
Содержание статьи
Иногда A/B-тест заканчивается не выводом, а красивым скриншотом: «победили с +8%». Но результат мог появиться из-за перекоса распределения, досрочной остановки, нескольких параллельных метрик или сломанного события. Перед тем как поздравлять команду с победой, проверьте, не нарушились ли базовые условия эксперимента.
Коротко
Доверие к тесту складывается из трёх вещей: группы получили эксперимент случайно, данные собираются одинаково, а правило остановки было задано заранее. Если хотя бы одно из этих условий нарушено, p-value и uplift создают видимость точности, но не спасают дизайн.
- Проверьте SRM: фактическое распределение пользователей между группами должно соответствовать плану.
- Не останавливай тест в тот день, когда случайно появился красивый результат.
- Зафиксируйте primary metric, guardrails и срок до старта эксперимента.
- После запуска проверьте трекинг и полноту событий до чтения результата.
SRM: группы распределились не так, как планировали
Sample Ratio Mismatch — это ситуация, когда в группах оказалось существенно не то соотношение пользователей, которое задавали в эксперименте. При плане 50/50 ожидаем примерно одинаковое число назначений. Если в control попало 58%, а в test 42%, сначала расследуем рандомизацию, фильтры и доставку варианта, а не обсуждаем uplift.
SRM не доказывает, что результат неправильный, но показывает: механизм эксперимента мог повлиять на то, кто вообще попал в каждую группу. Причина часто находится в SDK, кэше, авторизации, исключении старых пользователей или фильтре уже после назначения варианта.
Проверка занимает меньше минуты и делается до всех остальных расчётов. Порог здесь строже привычного — 0,0005: SRM смотрят у каждого теста, и при 0,05 каждый двадцатый здоровый тест поднимал бы ложную тревогу.
| Симптом | Куда смотреть |
|---|---|
| Перекос только на одном устройстве | SDK, версия приложения, кэш и повторная инициализация эксперимента |
| Перекос появляется после логина | склейка anonymous_id и user_id, правила исключения и повторное назначение |
| Одна группа теряет пользователей | краш, ошибка загрузки варианта, фильтр доступа или недоставленный feature flag |
| Распределение меняется по дням | сезонность трафика, лимит аудитории и логика ежедневного сэмплирования |
Подглядывание меняет правило игры
Если каждый день смотреть на p-value и останавливать тест при первом значении ниже 0,05, вероятность случайно объявить победителя становится выше заявленных 5%. Это не значит, что нельзя открывать дашборд вообще. Нельзя менять правило решения после того, как увидели промежуточный результат.
Безопасная практика: заранее записать минимальный срок или размер выборки, частоту проверок, primary metric и условие остановки. Если нужен последовательный дизайн, используйте его осознанно и с подходящим методом, а не называйте обычный тест последовательным задним числом.
Схема иллюстративная: чем чаще команда останавливает обычный тест по промежуточному p-value, тем выше риск ложного сигнала.
Множественные проверки и незапланированные срезы
Тест можно «найти» почти в любом результате, если одновременно проверять десять метрик, пять сегментов и несколько периодов. Один случайный p-value ниже 0,05 среди большого набора проверок не выглядит таким же убедительным, как заранее выбранная основная метрика.
Сегменты нужны для понимания механизма и поиска риска, но не для перебора до первого красивого числа. Отделяйте подтверждающий анализ от исследовательского: первый влияет на решение по тесту, второй формирует следующую гипотезу.
- Назовите primary metric до запуска и не меняйте её на вторую по ходу теста.
- Список guardrail-метрик держите коротким: только реальные риски релиза.
- Сегментные находки помечай как exploratory, если они не были запланированы.
- Для нескольких сравнений используйте поправку или отдельный дизайн, а не обычный порог механически.
Проверка событий важнее красивого дашборда
До чтения результата сравните размер аудитории эксперимента с числом пользователей в основной метрике. Если вариант видит 20 тысяч пользователей, а покупка считается только у 11 тысяч из них, нужно понять, это ожидаемый знаменатель или потеря события. Отдельно проверьте дубли, задержку загрузки и одинаковую доступность целевого действия в обеих группах.
Полезно иметь диагностический график до primary metric: назначения по дням, доля пользователей с событием, доля неизвестных вариантов, ошибки и время от exposure до целевого действия. Он не заменяет анализ, но быстро показывает, где искать поломку.
| Проверка | Что должно быть понятно |
|---|---|
| Распределение | Фактическое соотношение групп и дата/момент назначения |
| Exposure | Пользователь действительно увидел вариант, а не только был записан в конфигурацию |
| Знаменатель | Одна и та же популяция и правило включения для control и test |
| Событие | Нет систематической потери или дублей целевого события в одной группе |
| Правило решения | Заранее понятно, что делаем при победе, нейтральном результате или guardrail-риске |
Чеклист аналитика
Перед финальным выводом пройдите список сверху вниз. Если на каком-то шаге нет ответа, честнее отложить решение и починить эксперимент, чем превращать статистический отчёт в аргумент за уже принятое решение.
- Группы назначаются случайно и сохраняются за пользователем?
- SRM проверен на общей аудитории и ключевых технических срезах?
- Тест прожил заранее заданный срок или достиг плановой выборки?
- Primary metric и guardrails были определены до запуска?
- Данные одинаково полно собираются в обеих группах?
- Сегментные находки отделены от подтверждающего результата?
SRM: не только проверить процент
При плане 50/50 сначала посмотрите на фактическое число назначений, но на этом проверка не заканчивается. Разбейте распределение по дате, платформе, версии приложения, стране и статусу авторизации. Если общий результат близок к плану, а на iOS test почти не доставляется, среднее по всей аудитории скроет проблему, которая влияет на решение.
Сравните момент назначения, момент exposure и первый целевой шаг. Пользователь мог быть записан в test, но не увидеть интерфейс из-за ошибки загрузки. Для primary metric это уже не та популяция, которую команда думала сравнивать. Не удаляй перекошенный сегмент молча: зафиксируйте масштаб, причину и правило, по которому он входит в итоговый анализ.
| Слой | Вопрос |
|---|---|
| Assignment | назначается ли пользователь в вариант по плановой доле? |
| Exposure | действительно ли вариант показан пользователю? |
| Eligibility | не меняется ли фильтр включения после назначения? |
| Persistence | сохраняется ли вариант между сессиями и устройствами? |
| Delivery | нет ли ошибок загрузки, fallback и повторного назначения? |
Подглядывание и последовательный дизайн
Открывать дашборд для контроля данных можно. Проблема начинается, когда команда каждый день сравнивает p-value с 0,05 и останавливает обычный тест в первый удачный момент. При множественных просмотрах случайный всплеск с большей вероятностью будет принят за доказательство, чем при одном заранее заданном чтении.
Если бизнесу нужно принимать решение постепенно, используйте последовательный дизайн с заранее заданными границами и способом расчёта. Не называйте обычный тест последовательным задним числом. В рабочем документе отделите технический мониторинг от формального условия остановки: первый защищает качество, второй определяет вывод.
Один и тот же массив данных может дать разные решения, если правило остановки меняется после каждого обновления дашборда. Планируйте не только выборку, но и способ чтения.
- До запуска записать минимальный срок и плановую выборку.
- Разрешить ежедневный мониторинг SRM, ошибок и потери событий.
- Не менять primary metric после просмотра промежуточных результатов.
- При досрочной остановке явно назвать ограничение и метод.
Бюджет в 5% делится между взглядами: 0,005 + 0,005 + 0,010 + 0,030
Запрет на досрочную остановку выглядит неудобно: решение обычно нужно раньше, чем закончится запланированный месяц. Выход есть, и он не в том, чтобы смотреть пореже. Пять процентов — это бюджет ошибки на весь эксперимент, и его можно разложить по чтениям заранее.
Допустим, вы собираешься читать результат четыре раза: на четверти выборки, на половине, на трёх четвертях и на полной. Разделите 0,05 между этими моментами — например 0,005; 0,005; 0,010 и 0,030. В сумме ровно 0,05. На каждом чтении p-value сравнивают не с общим порогом, а с границей именно этого чтения, и тогда вероятность объявить победу при отсутствии эффекта возвращается к заявленной. График расхода фиксируют до старта вместе с числом чтений: расходовать можно только то, что расписано заранее.
Раскладка бюджета — это выбор, а не формальность. Схема Покока тратит его почти поровну: остановиться рано легко, но за это финальная граница оказывается строже 0,05, и тест, дотянувший до конца с p = 0,04, победителем уже не считается. Схема О’Брайена — Флеминга оставляет почти весь бюджет на финал: рано остановиться получится только при крупном эффекте, зато последнее чтение почти не отличается от обычного. Для продукта, где ранний выход нужен лишь при сильном результате, вторая удобнее.
Остановка с выводом «эффекта нет» бюджета не расходует вовсе. Ошибка первого рода — это объявленная победа, которой не было; решение закрыть тест раньше срока такой победы не создаёт. Оно увеличивает другой риск — пропустить реальный эффект, — поэтому границу бесполезности удобно задавать по интервалу: если верхняя граница уже ниже порога окупаемости, продолжать нечего.
У досрочной победы есть цена, которую редко называют в отчёте. Тест, остановленный в момент пересечения границы, почти всегда переоценивает эффект: он останавливается на удачном колебании, а не на средней оценке. Поэтому при ранней остановке пишите нижнюю границу интервала и заранее предупреждай команду, что на раскатке прирост окажется скромнее.
Если эксперименты идут на платформе, проверьте, какое p-value она показывает. Часть платформ считает always-valid вариант (mSPRT и родственные методы), где смотреть можно в любой момент — ценой более широких интервалов. Если платформа отдаёт обычное p-value, свободы смотреть нет, как бы часто ни обновлялся дашборд.
| Чтение | Доля выборки | Граница p-value | Что значит пересечение |
|---|---|---|---|
| 1-е | 25% | 0,005 | выходим только при очень крупном эффекте |
| 2-е | 50% | 0,005 | выход возможен, оценка будет завышена |
| 3-е | 75% | 0,010 | ранний вывод с широким интервалом |
| 4-е | 100% | 0,030 | финальное решение по этой границе, а не по 0,05 |
Много метрик и сегментов: где появляется случайная находка
Если проверить десять метрик, пять стран, три устройства и несколько окон, красивый результат почти наверняка найдётся даже при отсутствии реального эффекта. Это не делает сегментный анализ бесполезным. Он помогает находить механизм и риски, но его статус должен быть понятен: подтверждающий вывод или exploratory-наблюдение.
Для подтверждающего анализа зафиксируйте небольшой список до запуска. Если после теста обнаружился интересный сегмент, повторите проверку в новом эксперименте или подтверди её на отдельном периоде. Не превращайте случайную находку в обещание для всей аудитории.
Условная схема: каждая дополнительная проверка создаёт ещё одну возможность увидеть красивый результат.
Десять независимых проверок превращают 5% в 40%: сначала посчитайте семейство
Арифметика здесь короткая. При m независимых проверках и пороге 0,05 вероятность хотя бы одной ложной находки равна 1 − 0,95^m: пять проверок дают 23%, десять — 40%, двадцать — 64%. Счёт занимает полминуты, и делать его стоит до того, как открываете разрезы, а не после.
У формулы есть оговорка, которая объясняет разницу с графиком выше. Повторные чтения одной метрики сильно связаны между собой: каждый новый день добавляет к выборке немного, поэтому риск растёт медленнее формулы. А вот десять разных метрик или десять стран ближе к независимости — там 1 − 0,95^m работает как рабочий ориентир.
Поправка начинается с вопроса, что входит в семейство. Семейство — это набор проверок, отвечающих на одно решение. Один primary, посчитанный на четырёх платформах, чтобы понять, где раскатывать, — семейство из четырёх. Шесть secondary, которые объясняют механизм и ничего не решают, в семейство не входят: права объявить победу они не дают.
Возьмите десять p-value одного семейства, отсортированных по возрастанию: 0,002; 0,008; 0,012; 0,030; 0,060 и дальше вверх. Бонферрони делит порог на число проверок: 0,05 / 10 = 0,005, проходит только первое значение. Холм идёт по порядку и сравнивает k-е значение с 0,05 / (10 − k + 1); на втором шаге 0,008 уже больше 0,00556, перебор останавливается — снова одна находка. Бенджамини — Хохберг решает другую задачу: он держит долю ложных среди объявленных и сравнивает k-е значение с k · 0,05 / 10. Третье значение 0,012 меньше 0,015, поэтому подтверждаются три.
Поправка не бесплатна: любая из них снижает мощность, то есть повышает шанс не заметить настоящий эффект. Поэтому к guardrail-метрикам семейные поправки обычно не применяют — у защитной метрики безопасная сторона противоположная, пропустить вред дороже, чем лишний раз проверить подозрение. Единственный primary, зафиксированный до запуска, тоже не требует ничего: это семейство из одной проверки.
И последнее разделение, которое снимает путаницу: поправка закрывает количество проверок, но не их повторение во времени — этим занимается бюджет alpha из раздела выше. А самый надёжный ответ на случайную находку остаётся нестатистическим: повторить её на новой выборке или на holdout. Сегмент, выживший в двух независимых экспериментах, убедительнее любого пересчитанного порога. Полный расчёт трёх поправок на пяти каналах DragonKeep — в разборе множественных сравнений.
| № | p-value | Бонферрони | Холм | Бенджамини — Хохберг |
|---|---|---|---|---|
| 1 | 0,002 | ≤ 0,005 — проходит | ≤ 0,0050 — проходит | ≤ 0,005 — проходит |
| 2 | 0,008 | выше порога | выше 0,0056 — перебор остановлен | ≤ 0,010 — проходит |
| 3 | 0,012 | выше порога | не проверяется | ≤ 0,015 — проходит |
| 4 | 0,030 | выше порога | не проверяется | выше 0,020 |
| 5 | 0,060 | выше порога | не проверяется | выше 0,025 |
Если в отчёте нет списка всех сравнений, поправку считать не из чего: семейство задним числом всегда оказывается меньше того, что реально смотрели в ноутбуке.
Сделайте pre-read частью процесса
Pre-read — это короткий документ перед решением, а не ещё один отчёт ради отчёта. В нём должны быть дизайн, аудитория, период, primary, guardrails, фактическое распределение, состояние трекинга и правило остановки. Если любой блок неизвестен, его нужно обозначить как ограничение.
После теста сохраните не только победителя, но и технический разбор. Что произошло с delivery? Какие события задерживались? Были ли пользователи с несколькими вариантами? Какой срез оказался рискованным? Такой журнал экономит время в следующих экспериментах и превращает отдельную ошибку в улучшение платформы.
| Состояние | Решение |
|---|---|
| Дизайн и данные чистые | читать primary и guardrails по заранее заданному правилу |
| Есть ограничение, но риск локален | исключить или отдельно разобрать сегмент с документированным обоснованием |
| SRM или tracking сломан | не интерпретировать uplift; исправить и перезапустить |
| Есть неожиданный exploratory-сигнал | сформировать новую гипотезу и подтвердить отдельно |
Назначение, exposure и анализ: три разные популяции
Одна из частых причин путаницы — считать всех пользователей эксперимента одной аудиторией. Assignment означает, что система выбрала вариант. Exposure означает, что пользователь действительно увидел изменение. Analysis population может дополнительно иметь условие целевого действия или период наблюдения. Эти множества могут не совпадать, и расхождение нужно объяснить.
Если отбрасывать пользователей без exposure после просмотра результата, можно нарушить преимущество рандомизации. Для основного intent-to-treat-анализа обычно сохраняют всех корректно назначенных пользователей, а exposure и технические исключения используют для диагностики. Другой подход возможен, но его нужно планировать до запуска и описывать явно.
| Слой | Пример | Риск ошибки |
|---|---|---|
| Assignment | пользователь получил flag B | флаг не сохранился между сессиями |
| Exposure | вариант B отрисовался на экране | ошибка загрузки скрыла изменение |
| Eligibility | пользователь подходит под окно анализа | фильтр появился после назначения |
| Outcome | событие покупки произошло | сравнили только дошедших до checkout |
Поломка трекинга может выглядеть как эффект
Если событие покупки отправляется только после нового компонента, test может показать рост не потому, что пользователи стали покупать чаще, а потому, что событие стало записываться полнее. Обратная ситуация выглядит как проигрыш: интерфейс работает, но одна ветка не отправляет финальное событие. Поэтому перед primary смотрите на технический funnel и долю пользователей, для которых доступен каждый шаг.
Сравните не только количество событий, но и отношение к устойчивому якорю: exposure, сессии, заказы из платёжной системы или серверные записи. Если клиентский и серверный источник расходятся только в одной группе, блокируйте интерпретацию и разбирайте доставку.
- Доля неизвестных вариантов и повторных exposure.
- Задержка между назначением и показом интерфейса.
- Доля пользователей с missing event в каждой группе.
- Сверка клиентских событий с серверным источником.
- Изменения схемы и версии SDK в период теста.
Как расследовать странный результат
Если uplift появился внезапно, не начинайте с объяснения про поведение пользователей. Сначала постройте timeline: запуск флага, релизы, ошибки, изменение источника, рост трафика и момент появления сигнала. Потом разбейте результат по платформам, версиям, странам, новым и возвращающимся пользователям. Временная и техническая локализация часто быстрее находит причину, чем новый статистический тест.
Проверьте три версии: эффект реальный, эффект вызван поломкой измерения, результат случайный. Для каждой запишите наблюдение, которое подтверждает или опровергает версию. Такой incident-style подход полезнее общего вывода «данные странные», потому что превращает проверку в последовательность действий.
Условный пример: рост метрики начинается одновременно с изменением SDK, поэтому сначала проверяем доставку события.
Проверка перед публикацией результата
Финальный отчёт должен содержать отдельный раздел «что может быть неправильно». Это не ослабляет вывод, а показывает зрелость анализа. Укажите, какой риск проверен, какой остался и влияет ли он на решение. Если эксперимент нельзя интерпретировать, напишите это прямо: перезапуск с чистым трекингом полезнее спорного rollout.
После исправления сохраните regression check. Он может проверять долю вариантов, уникальность exposure, наличие целевого события и дневной объём. Следующий эксперимент не должен снова обнаруживать ту же проблему вручную.
| Проверка | Pass condition |
|---|---|
| Распределение | соотношение соответствует плану, SRM объяснён |
| Стабильность | вариант сохраняется и нет cross-over |
| Доставка | exposure и технический funnel сопоставимы |
| Метрики | primary, guardrails и знаменатели заданы заранее |
| Решение | есть действие для победы, нейтрального исхода и риска |
Перед любым выводом проверьте источник истины
Для коммерческих метрик заранее выберите источник истины: клиентские события, серверные записи, платёжный реестр или нормализованную витрину. Разные источники могут быть полезны для диагностики, но финальное решение должно опираться на понятный контракт. Иначе один и тот же эксперимент можно «выиграть» или «проиграть» выбором таблицы.
При расхождении сохраните обе цифры и причину расхождения в pre-read.
Это позволит отличить баг данных от реального изменения поведения и не потерять контекст через неделю.
Источник истины указывается рядом с primary и проверяется до rollout.
Если источник меняется во время эксперимента, остановите интерпретацию и добавьте это в ограничения.
Это короткое правило защищает отчёт от подмены источника после получения результата.
Если строка не означает то, что написано в названии метрики, точный расчёт не делает вывод надёжнее.
Мини-кейс: красивый uplift оказался поломкой delivery
Представим тест нового экрана оплаты. На третий день dashboard показывает +9% к paid order rate, p-value ниже порога, и команда готовит rollout. Аналитик замечает, что рост начался сразу после обновления SDK и почти полностью сосредоточен на Android 14. В test при этом стало на 7% меньше событий checkout_started, а серверный реестр заказов роста не подтверждает.
Разбор показывает: новый экран отправляет purchase при возврате из платёжного приложения, хотя заказ ещё не подтверждён. Primary выросла в клиентской аналитике, но бизнес-результат не изменился. Ошибка не в формуле p-value, а в том, что событие перестало означать то же самое. Такой тест нельзя «починить» фильтром после просмотра: нужно исправить событие, пересобрать baseline и перезапустить эксперимент.
Этот сценарий полезен как стандарт расследования. Сначала сравните клиентский и серверный источники, затем постройте технический funnel, локализуйте платформу и дату, проверьте версии SDK и только потом возвращайтесь к продуктовой интерпретации. Так команда не тратит неделю на обсуждение эффекта, которого не было.
| Наблюдение | Скорее продукт | Скорее данные |
|---|---|---|
| Рост primary | виден в нескольких источниках | есть только в одном клиентском событии |
| Время появления | постепенно после изменения поведения | точно совпадает с релизом SDK |
| Сегмент | логично связан с гипотезой | одна версия ОС или канал доставки |
| Guardrail | изменяется согласованно | технические ошибки растут одновременно |
Материалы по теме
Проверка SRM: как понять, что сплит A/B-теста перекосило
Что такое SRM в A/B-тесте, как проверить перекос сплита критерием χ², почему порог здесь 0,0005 вместо 0,05 и что делать, когда трафик разошёлся по группам не по плану.
A/B-тест в SQL: конверсия, проверка сплита и разбор по сегментам
Как посчитать A/B-тест запросом: конверсия по группам, проверка SRM, разница, z и доверительный интервал в SQL и срез по устройствам, после которого меняется решение.

A/A-тест: как проверить систему экспериментов до первого A/B
A/A-тест проверяет назначение, экспозиции и метрики до анализа эффекта. Учебный menu_color показывает SRM 54/46 при плане 50/50 и путь расследования.