Зачем вообще нужен A/A
A/A-тест — это эксперимент без воздействия. Берёшь юзеров, случайно делишь их на две группы, обе получают одну и ту же версию продукта, а потом считаешь метрику ровно так же, как считал бы её в A/B. Звучит бессмысленно — мы заранее знаем, что эффекта нет. Но в этом и смысл: если «эффект» появился — где-то баг, и пока его не нашёл, любому A/B-результату верить опасно.
Платформа экспериментов — это много слоёв: сплиттер, трекинг, хранилище логов, агрегация метрик, стат-процедура. Каждый слой может тихо ломаться. A/B такие поломки часто маскирует — реальный эффект перекрывает шум, и команда раскатывает фичу, не понимая, что половина «лифта» была артефактом трекинга. A/A ловит это до того, как оно стоит денег.
Без A/A эти три вещи проверить нечем. Можно полагаться на интуицию архитектора платформы, но интуиция не масштабируется на новые метрики и новые кейсы. A/A — это дешёвая формальная проверка, которая поднимает руку, когда что-то отъехало.
Откуда в A/A берётся «значимость»
Когда команда впервые запускает A/A и получает p < 0.05, реакция всегда одна: «как же так, мы ничего не меняли — а тест значим». Это не баг, это статистика. При уровне значимости α=0.05 ровно 5% A/A-тестов должны давать значимый результат — даже если платформа идеальна. Это и называется False Positive Rate.
Поэтому одного A/A недостаточно. Нужна симуляция: запускаем 1000 (а лучше 10000) A/A на исторических данных и смотрим, какой процент из них дал p < 0.05.
Дальше — простая логика. Идеальный FPR равен α (5%). В реальной выборке из 1000 симуляций будет случайное отклонение — норма примерно 4–6%. А вот выходы за эти границы — повод копать.
| FPR в A/A | Диагноз | Что делать |
|---|---|---|
| 4–6% | Норма | Платформа здорова. Можно запускать A/B. |
| 7–10% | Анти-консервативно | Тест ложно срабатывает чаще нормы. Чинить: SRM, дубликаты, ratio. |
| 2–3% | Консервативно | Дисперсия переоценена. Тест может пропускать реальные эффекты. Часто — delta-method применили там, где не надо, или ICC недооценили. |
| > 15% | Серьёзный баг | Сплитование, метрика или стат-процедура серьёзно сломаны. Останавливай все A/B до выяснения. |
Дополнительная проверка — равномерность распределения p-value. В правильно работающем A/A p-value должен быть распределён равномерно от 0 до 1. Гистограмма с перекосом к нулю — анти-консерватизм. Гистограмма с горбом в районе 0.5 — консерватизм. Это короче и нагляднее, чем считать только долю значимых.
Два типа A/A: офлайн и продакшн
A/A-тесты делятся на два формата, и они проверяют разные вещи. Перепутать — потерять время и не найти баг.
| Офлайн A/A | Продакшн A/A | |
|---|---|---|
| Где запускается | На исторических данных в Python/SQL | В платформе экспериментов, реальный сплит юзеров |
| Сколько симуляций | 1000–10000 (cheap) | Обычно 1–2, реже до 10 (expensive) |
| Длительность | Минуты | 1–4 недели на тест |
| Что проверяет | Метрика + стат-тест + единица анализа | Сплитование + трекинг + кэши + всё сразу |
| Какие баги ловит | Bias в метрике, неверный стат-тест, неучтённая корреляция | SRM, дубликаты, региональные кэши, baking trafic |
| Когда использовать | Перед каждой новой метрикой / новым стат-методом | После изменений в системе сплитования или трекинге |
Хорошая практика: офлайн A/A — обязательный, продакшн A/A — по событию. Офлайн делают каждый раз перед добавлением новой метрики или нового стат-теста (быстро, дёшево, можно автоматизировать в CI). Продакшн запускают после изменений в инфраструктуре экспериментов или при первом запуске на новой платформе.
Офлайн A/A: код
import numpy as np import pandas as pd from scipy import stats def offline_aa(df: pd.DataFrame, metric: str, n_sim=1000): """ Случайно разбивает юзеров на A и B, считает t-тест, повторяет. Возвращает массив p-value длиной n_sim. """ p_values = [] users = df.user_id.unique() for _ in range(n_sim): groups = np.random.choice(['A', 'B'], size=len(users)) mapping = dict(zip(users, groups)) df['group'] = df.user_id.map(mapping) a = df[df.group == 'A'][metric] b = df[df.group == 'B'][metric] _, p = stats.ttest_ind(a, b, equal_var=False) p_values.append(p) return np.array(p_values) p = offline_aa(historical_df, metric='revenue', n_sim=1000) # Доля значимых — должна быть около 0.05 print(f"FPR: {(p < 0.05).mean():.3f}") # Распределение p-value — должно быть равномерным import matplotlib.pyplot as plt plt.hist(p, bins=20); plt.show()
Если FPR в диапазоне 4–6% и гистограмма равномерная — тест и метрика здоровы. Если нет — иди по чек-листу ниже.
Когда запускать A/A обязательно
A/A — не ежедневная процедура. Это контроль, который запускают после конкретных событий в инфраструктуре экспериментов. Пять основных триггеров:
5 причин ложной значимости в A/A
Если FPR в твоём A/A зашкаливает, виновата почти всегда одна из этих пяти штук. По частоте — от самых распространённых к более редким.
p < 0.001. Это означает, что часть юзеров «утекла» — не попала в свою группу из-за поломки трекинга, бот-фильтра, race condition в назначении групп. Самая частая причина ложной значимости в продакшн A/A.scipy.stats.chisquare([n_a, n_b], [exp_a, exp_b]). Если SRM — иди в логи сплита, ищи юзеров, у которых group менялся, и место, где они отсеиваются.events_per_user в обеих группах и распределение по дублям. Если в одной группе хвост толще — копай SDK или ETL. Часто помогает DISTINCT (user_id, event_id) перед агрегацией.sum(orders) per user_id, или применяй cluster-robust стандартные ошибки, или переходи к delta-method.experiment_id и group. Запусти продакшн A/A на каждом региональном edge отдельно — если FPR разный по регионам, кэш — главный подозреваемый.sum(orders) / sum(visits), потом применяешь t-тест к этой ratio по юзерам. Дисперсия ratio считается через дельта-метод (или линеаризацию), не как у нормальной метрики. Если применить простой t-тест — дисперсия занижена, FPR улетает выше 5%. Особенно сильно эффект на маленьких знаменателях.(σ²X / Y² ) + ... − 2·X·Cov(X,Y) / Y³, или линеаризация по подходу Авито/Яндекса. В матрице стат-тестов есть конкретные формулы для типичных ratio.Чек-лист отладки: 6 шагов
A/A показал высокий FPR — что дальше? Не паниковать, не отменять A/B вслепую. Пройти по шести шагам в правильном порядке: от самой частой и самой дешёвой проверки к самой редкой и дорогой.
chisquare([n_a, n_b], [N/2, N/2]). Если p < 0.001 — это самая частая поломка. Останавливайся здесь, пока не починишь сплиттер. На SRM любые дальнейшие проверки бессмысленны.events_per_user и распределение в обеих группах. Если хвосты разные — копай ETL и SDK. Применяй дедупликацию по (user_id, event_id, timestamp) и пересчитай A/A.experiment_id и group в кэш-ключ.A/A в кейс-интервью
A/A — стандартная тема на middle/senior-собесах в продуктовой аналитике. Спрашивают редко в лоб («что такое A/A»), чаще — в формате кейса: «у нас в A/B вышла странная картина, что бы ты сделал». Правильный ход — предложить A/A как первый шаг диагностики, а не сразу копать в продукт.
- Что такое A/A-тест и зачем он нужен, если эффекта заведомо нет?
- Какой процент значимых результатов в A/A считается нормой и почему?
- В чём разница между офлайн и продакшн A/A?
- Что такое SRM? Как его проверить и что делать, если нашёл?
- Сплит по юзерам, метрика по сессиям. Какая будет проблема в A/A?
- Конверсия = orders/visits, простой t-тест на этой метрике. Почему FPR будет выше 5%?
- В A/A одна симуляция дала p=0.01. Это баг или нормально?
Частые вопросы
Что такое A/A-тест простыми словами?
Какой процент значимых результатов в A/A считается нормой?
4–6%. Если 7–10% — есть систематическое смещение, чинить. Если меньше 3% — оценка дисперсии завышена, тесты слишком консервативны и могут пропускать реальные эффекты. Идеальная диагностика — не только доля значимых, но и форма гистограммы p-value: она должна быть равномерной от 0 до 1.Чем офлайн A/A отличается от продакшн A/A?
Что такое Sample Ratio Mismatch (SRM)?
chi-square p < 0.001), и оно означает, что часть юзеров не попала в нужную группу. Причины: трекинг падает по-разному в группах, бот-фильтр перекошен, кэш CDN раздаёт разное, race condition в назначении групп. SRM → результаты A/B нельзя доверять, пока не починишь.Что делать, если в A/A падает значимость?
(1) SRM — хи-квадрат на размер групп; (2) дубликаты — посчитать уникальных юзеров и события на юзера; (3) совпадение unit of randomization и unit of analysis — нельзя считать конверсию по сессиям, если сплитили по юзерам; (4) ratio-метрика — заменить t-тест на delta-method; (5) кэш CDN — проверить, что A/A покрывает все региональные edge-кэши; (6) перезапустить A/A и убедиться, что FPR вернулся к 5%.Зачем запускать A/A, если у нас уже работает платформа экспериментов?
Сколько симуляций A/A нужно для надёжной оценки FPR?
Связанные материалы
Главное про A/A-тесты
A/A — это техосмотр платформы экспериментов. Не для красоты и не для отчёта, а чтобы поймать поломку до того, как она спрятала или придумала эффект в реальном A/B. Стоимость входа низкая: офлайн-симуляция — это 30 строк Python и 5 минут.
Запомни три ориентира: (1) при α=0.05 норма FPR — это 4–6%, а не «0%»; (2) офлайн A/A — обязательный, продакшн — после изменений в инфраструктуре; (3) если FPR уехал — почти всегда виноват один из пяти подозреваемых: SRM, дубликаты, не-iid юзеры, кэш CDN, ratio без delta-method.
Следующий шаг: возьми любую свою боевую метрику, прогони офлайн A/A на 1000 симуляций, посмотри на FPR и гистограмму p-value. Если впервые делаешь — почти гарантированно найдёшь баг. И сразу понятно, почему A/A — самое недооценённое упражнение в продуктовой аналитике.