Короткий ответ: тест нужно держать ровно столько, чтобы набрать заранее рассчитанный размер выборки при твоём реальном трафике, — но не меньше одного-двух полных недельных циклов. То есть берёшь нужный размер выборки (он выводится из MDE и мощности), делишь на дневное число пользователей, попадающих в эксперимент, округляешь вверх до целых недель. Получается минимум одна, чаще две недели. И самое важное: дату окончания ты фиксируешь ДО запуска, а не двигаешь её каждый раз, когда p-value один раз подмигнул «значимо».
Дальше разберу, откуда берётся эта формула, почему две недели — не суеверие, а недельная сезонность плюс novelty-эффект, и почему «подглядывать» в результаты — самый частый способ обмануть самого себя.
От чего вообще зависит длительность A/B-теста?
У длительности всего три входа, и все они простые.
Первый — размер выборки. Сколько наблюдений нужно, чтобы уверенно отличить реальный эффект от шума. Он растёт, когда ты хочешь ловить более мелкие изменения (маленький MDE), и когда базовая метрика ближе к 50% (максимум дисперсии у конверсии). Формально размер выборки выводится из четырёх вещей: базового уровня метрики, MDE (минимального эффекта, который тебе интересно поймать), мощности (обычно 80%) и уровня значимости (обычно 5%).
Второй вход — трафик. Сколько пользователей в день реально попадают в тест. Не общий DAU сайта, а именно та аудитория, которая видит изменение и учитывается в метрике. Если ты тестируешь новый чекаут, то в знаменателе не все посетители, а только дошедшие до корзины.
Третий вход — календарь. Даже если по выборке хватило бы трёх дней, тест всё равно нельзя останавливать раньше полного недельного цикла. Про это ниже отдельный раздел.
Собираешь всё вместе: длительность в днях = размер выборки на обе группы / дневной трафик в тесте, а потом округляешь вверх до целых недель и берёшь не меньше двух. Всё, это и есть ответ.
Как размер выборки превращается в недели?
Покажу на цифрах. Допустим, размер выборки — 40 000 пользователей на вариант, то есть 80 000 суммарно на контроль и тест. В эксперимент ежедневно попадает 6 000 человек.
n_per_variant = 40_000
variants = 2
daily_traffic = 6_000
total_needed = n_per_variant * variants # 80 000
days_raw = total_needed / daily_traffic # 13.3 дня
weeks = -(-int(days_raw) // 7) # округляем вверх: 2 недели
duration_days = max(weeks * 7, 14) # пол floor в две недели
print(round(days_raw, 1), "дней ->", duration_days, "дней")
# 13.3 дней -> 14 дней
Обрати внимание на два округления. Сначала вверх до целого числа недель — чтобы каждый день недели был представлен одинаковое число раз в обеих группах. Потом — floor в две недели как минимум. Здесь 13,3 дня удачно легли в две недели. Если бы вышло 16 дней, я бы округлил до трёх недель (21 день), а не оставил кривые «две недели и два дня». Прикинуть такой расчёт под свои числа удобно прямо в Python-тренажёре — заодно проверишь, как меняется результат при разном MDE.
Как быстро прикинуть длительность на практике?
Пять шагов, каждый занимает пару минут.
- Достаёшь базовый уровень метрики из данных. Для конверсии это доля пользователей с целевым событием.
- Выбираешь MDE — какой минимальный прирост тебе действительно важен. Не «хоть что-нибудь», а бизнес-значимый порог.
- Считаешь размер выборки на вариант через любой калькулятор или формулу мощности.
- Смотришь дневной трафик, который реально попадёт в тест.
- Делишь, округляешь вверх до недель, берёшь минимум две.
Первый и четвёртый шаги — это обычный SQL по таблице событий. Вот как достать и базовую конверсию, и дневной поток пользователей одним заходом:
WITH daily AS (
SELECT date_trunc('day', e.created_at) AS d,
COUNT(DISTINCT e.user_id) AS users,
COUNT(DISTINCT e.user_id) FILTER (WHERE e.name = 'purchase') AS buyers
FROM events e
WHERE e.created_at >= now() - interval '28 days'
AND e.name IN ('checkout_view', 'purchase')
GROUP BY 1
)
SELECT ROUND(AVG(users)) AS avg_daily_traffic,
ROUND(SUM(buyers)::numeric / SUM(users), 4) AS baseline_conversion
FROM daily;
Средний дневной трафик идёт в знаменатель формулы длительности, базовая конверсия — в расчёт размера выборки. Если хочешь потренировать такие запросы на живой базе, загляни в SQL-тренажёр или разбери похожие задачи в разделе заданий. Кстати, знаменатель тут — это по сути прикладная версия DAU, только суженная до аудитории эксперимента.
Почему тест должен идти хотя бы один-два недельных цикла?
Потому что поведение людей зависит от дня недели, и сильно. В будни и выходные покупают по-разному, B2B-продукты вообще проседают в субботу-воскресенье, а какой-нибудь доставочный сервис наоборот живёт по выходным. Если запустить тест во вторник и остановить в четверг, ты измеришь только «будний» кусочек мира и сделаешь вывод обо всём.
Отсюда правило целых недель. Тест длиной 7 или 14 дней даёт каждой группе ровно по одному понедельнику, вторнику и так далее. А тест на 10 дней даёт лишние понедельник-вторник-среду, и эти дни перекошивают среднее. Это не про статистику в чистом виде, это про то, чтобы состав аудитории в контроле и тесте был сопоставим по календарю.
Второй аргумент за две недели, а не одну, — устойчивость. Одна неделя может попасть на что-то нетипичное: акцию у конкурента, праздник, сбой. Вторая неделя показывает, повторяется ли эффект, или это была случайность конкретных семи дней.
Что такое novelty-эффект и почему нельзя верить первым дням?
Novelty-эффект — это когда пользователи реагируют на изменение просто потому, что оно новое, а не потому, что оно лучше. Выкатил новую яркую кнопку — по ней в первый день кликают из любопытства, метрика взлетает. Через неделю новизна стирается, и клики оседают на реальном уровне. Если остановить тест на пике любопытства, поверишь в эффект, которого на дистанции нет.
Есть и зеркальная история — change aversion, неприятие изменений. Постоянные пользователи привыкли к старому интерфейсу, новый их сбивает, и первые дни метрика проседает не из-за плохого решения, а из-за того, что люди переучиваются. Тут ранняя остановка убьёт хорошую фичу.
Оба эффекта особенно заметны на продуктах с возвращающимися пользователями. Лечится это временем: даёшь тесту отработать полный цикл, чтобы поведение устаканилось. Это ещё одна причина, почему одного-двух дней «значимого» результата недостаточно. Такие вопросы про novelty и недельные циклы обожают на собеседованиях аналитиков — их стоит уметь объяснять на пальцах.
Почему нельзя останавливать тест по первому «значимо»?
Это главная ловушка, и называется она peeking — подглядывание. Логика кажется безобидной: «дай проверю, вдруг уже значимо, тогда закончу раньше». Проблема в том, что при таком поведении твой реальный уровень ложных срабатываний перестаёт быть 5%.
Смысл простой. Уровень значимости 5% означает: если эффекта нет, ты ошибочно объявишь «есть» примерно в 5% случаев при однократной проверке в конце. Но если ты смотришь на результат каждый день и готов остановиться, как только увидишь значимость, ты делаешь не одну проверку, а десятки. А p-value гуляет случайно вокруг границы — рано или поздно оно случайно нырнёт ниже 0,05, даже когда никакой разницы между вариантами нет. Чем чаще подглядываешь и хватаешься за первый зелёный результат, тем выше шанс поймать чистый шум и принять его за победу.
Вот грубая иллюстрация того, как «пустой» тест без всякого эффекта регулярно пересекает порог, если проверять его каждый день:
import numpy as np
from scipy import stats
rng = np.random.default_rng(42)
false_alarms = 0
runs = 2000
for _ in range(runs):
a, b = [], []
hit = False
for day in range(14): # добавляем данные и проверяем каждый день
a += list(rng.normal(0, 1, 400))
b += list(rng.normal(0, 1, 400)) # эффекта нет: обе группы одинаковы
if stats.ttest_ind(a, b).pvalue < 0.05:
hit = True
break
false_alarms += hit
print(round(false_alarms / runs, 3)) # заметно больше 0.05
Эффекта в коде нет вообще, группы берутся из одного распределения, а «значимо» всё равно выскакивает гораздо чаще, чем в 5% прогонов, — именно из-за ежедневного подглядывания. Отсюда правило: горизонт фиксируется заранее, и решение принимается на дате окончания. Если очень хочется смотреть на промежуточные результаты и иметь право останавливаться раньше — это можно, но только через методы, которые заранее закладывают многократные проверки (групповой sequential-дизайн, поправки Pocock или O'Brien-Fleming). Просто «глазами каждый день» — нельзя. Разобрать подобный кейс с ошибкой раннего стопа можно в практических кейсах.
Как связаны MDE, размер выборки и длительность?
Связь квадратичная, и это надо чувствовать нутром. Размер выборки обратно пропорционален квадрату MDE. Захотел ловить эффект в два раза мельче — выборка вырастет примерно в четыре раза, а вместе с ней и длительность.
Как быстрый ориентир держи в голове правило «примерно 16»: чтобы поймать эффект при мощности 80% и значимости 5%, на вариант нужно порядка 16 * дисперсия / MDE² наблюдений. Коэффициент зависит от выбранной мощности и уровня значимости, так что это прикидка, а не точная цифра. Но она отлично показывает масштаб: MDE в знаменателе стоит в квадрате.
Практический вывод — не гонись за микроскопическим MDE. Если поставить себе цель поймать прирост конверсии в 0,1 процентного пункта, тест может растянуться на месяцы, и всё это время ты не сможешь катить другие изменения. Лучше честно спросить: «прирост меньше какого размера мне вообще не интересен для бизнеса?» — и взять его за MDE. Часто оказывается, что мелкие эффекты не стоят того, чтобы месяц держать эксперимент. Поиграть с зависимостью MDE, выборки и недель полезно в том же Python-тренажёре, а формулы под рукой держать удобно в Python-справочнике.
Какие ошибки чаще всего ломают расчёт длительности?
Собрал те, что вижу постоянно.
- Остановка по первому «значимо». Разобрали выше — раздувает ложные срабатывания, чаще всего именно так «находят» несуществующие победы.
- Некруглое число недель. Тест на 9-10 дней перекашивает дни недели. Всегда округляй вверх до 7 или 14.
- Игнор novelty-эффекта. Верить пику первых дней на продукте с возвращающимися пользователями — самообман.
- Запуск на аномальный период. Праздники, крупные распродажи, конец квартала искажают поведение. Либо избегай таких дат, либо закладывай, что результат про этот период, а не про обычную жизнь.
- Смена MDE или метрики на ходу. Решил в середине «а давай ловить эффект помельче» — весь расчёт выборки и горизонт пересчитываются, старые дни уже неоднородны.
- Слишком мелкий трафик. Если в тест попадает 200 человек в день, а нужно 80 000, честная длительность — больше года. Значит, либо крупнее MDE, либо это не тот эксперимент, который стоит запускать.
Чеклист перед запуском
Перед тем как нажать «старт», пробеги пять пунктов. Базовый уровень метрики взят из данных за последние недели, а не из головы. MDE выбран по бизнес-смыслу, не по принципу «поймать хоть что-то». Размер выборки посчитан для мощности 80% и значимости 5%. Дневной трафик оценён именно для аудитории теста, а не для всего сайта. Итоговая длительность округлена вверх до целых недель и не меньше двух — и записана как фиксированная дата окончания, которую ты не будешь двигать.
Если держаться этих правил, A/B-тест перестаёт быть лотереей: ты заранее знаешь, что измеряешь, сколько это займёт и когда примешь решение. А это ровно то, что отличает аналитика, которому доверяют катить изменения, от того, кто «покрутил и вроде выросло».
Если хочешь довести расчёт выборки и разбор экспериментов до автоматизма — на тренажёрах и в кейсах для этого собрано много практики, а Pro открывает весь банк задач и AI-разборы. Начать можно бесплатно с первого блока SQL-курса — база для таких запросов лишней не бывает.