← Назад к вопросам

Были ли стратегии принятия решения в последнем A/B тесте

1.2 Junior🔥 231 комментариев
#A/B тестирование#Статистика и математика

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI21 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Стратегия принятия решения в A/B тесте: от планирования до развёртывания

Контекст: Онбординг для мобильного приложения

Компания: Travel booking app (20M DAU) Проблема: 35% пользователей отвалиается после первого запуска (opening day churn) Гипотеза: Добавим интерактивный tutorial вместо static slideshow

Фаза 1: Планирование и расчёт размера

Шаг 1: Определение метрик

Primary metric: Day-1 Retention (процент пользователей, вернувшихся на день 2)

  • Baseline: 65%
  • MDE (Minimum Detectable Effect): 2% lift → 67%
  • Это 3% относительное улучшение, что имеет смысл для бизнеса

Secondary metrics:

  • Day-7 Retention
  • Feature discovery rate (сколько фич пользователь попробовал на день 1)
  • Time spent on onboarding
  • Tutorial completion rate

Guardrail metrics (не должны деградировать):

  • App install time (не должна > 2 сек)
  • Crash rate on day 1 (не должна > 0.5%)

Шаг 2: Расчёт sample size

Формула: n = (Z_α/2 + Z_β)² * (p1(1-p1) + p2(1-p2)) / (p2 - p1)²

Параметры:
- Baseline conversion (p1) = 0.65
- Target conversion (p2) = 0.67
- α (significance level) = 0.05 → Z_α/2 = 1.96
- β (power) = 0.20 → Z_β = 0.84
- Power = 1 - β = 0.80 (80% мощность)

Расчёт:
n = (1.96 + 0.84)² * (0.65*0.35 + 0.67*0.33) / (0.67 - 0.65)²
n = (2.80)² * (0.2275 + 0.2211) / 0.0004
n = 7.84 * 0.4486 / 0.0004
n = 8,795 пользователей в группе

Общий размер = 8,795 * 2 = 17,590 пользователей
Длительность теста = 17,590 / (20M DAU) = ~0.1% трафика
Дни = Длительность теста / (фракция трафика) = 1 день 100%, или 100 дней на 1%

Выбор: 100% трафика на 1 день, ИЛИ 1% трафика на 100 дней
Выбрал: 1% трафика на 7 дней (для покрытия недельных паттернов)

Шаг 3: Определение периода теста

Почему 7 дней?

  • День недели может влиять (weekend vs weekday пользователи)
  • Нужна полная неделя для среднего поведения
  • Минимум 2-3 дня для статистики, но 7 дней = safe choice

Старт теста: Вторник 9 AM (среди недели) Конец теста: Вторник 9 AM (ровно неделя)

Это покрывает 2 выходных дня и нейтрализует недельные паттерны.

Фаза 2: Мониторинг во время теста

День 1 утро (6 часов после старта)

-- Быстрая проверка здоровья теста
SELECT 
  variant,
  COUNT(DISTINCT user_id) as users,
  SUM(CASE WHEN completed_onboarding = 1 THEN 1 ELSE 0 END) as completed,
  ROUND(100.0 * SUM(CASE WHEN completed_onboarding = 1 THEN 1 ELSE 0 END) / COUNT(*), 2) as completion_rate,
  ROUND(AVG(crash_occurred), 4) as crash_rate
FROM onboarding_events
WHERE event_date = CURRENT_DATE
  AND variant IN ('control', 'treatment')
GROUP BY variant;

-- Результат после 6 часов:
variant   | users | completed | completion_rate | crash_rate
----------|-------|-----------|-----------------|----------
control   | 3,200 | 2,080     | 65.0%           | 0.001
treatment| 3,150 | 2,150     | 68.2%           | 0.003

Что проверяю:

  • ✅ Распределение 50/50? Да, 3,200 vs 3,150
  • ✅ Нет аномалий? Crash rate немного выше в treatment (0.3%), но приемлемо
  • ⚠️ Нет раннего прерывания? Вижу +3.2% lift, но это раннее, ждём дальше

День 2 (24 часа)

-- День 1 retention (основная метрика)
SELECT 
  variant,
  COUNT(DISTINCT CASE WHEN saw_onboarding = 1 THEN user_id END) as day0_users,
  COUNT(DISTINCT CASE WHEN saw_onboarding = 1 AND returned_day1 = 1 THEN user_id END) as day1_returned,
  ROUND(100.0 * COUNT(DISTINCT CASE WHEN saw_onboarding = 1 AND returned_day1 = 1 THEN user_id END) / 
        COUNT(DISTINCT CASE WHEN saw_onboarding = 1 THEN user_id END), 2) as day1_retention
FROM users_daily_activity
WHERE day0_date = CURRENT_DATE - INTERVAL '1 day'
  AND variant IN ('control', 'treatment')
GROUP BY variant;

-- Результат:
variant   | day0_users | day1_returned | day1_retention
----------|-----------|---------------|---------------
control   | 3,200     | 2,080         | 65.0%
treatment| 3,150     | 2,162         | 68.6%

Интерпретация:

  • Lift: +3.6 процентных пункта (от 65.0% до 68.6%)
  • Это +5.5% относительный lift
  • Но это после 24 часов, ещё не поздно? Нет, нужно ждать конца недели

День 4 (точка "раннего остановления")

Статистический тест:

from scipy.stats import chi2_contingency

# День 4 результаты
control_returned = 2,080
control_not_returned = 1,120
treatment_returned = 2,162
treatment_not_returned = 988

# Chi-square тест
contingency_table = [
  [control_returned, control_not_returned],
  [treatment_returned, treatment_not_returned]
]

chi2, p_value, dof, expected = chi2_contingency(contingency_table)
# p_value = 0.0031 < 0.05 ✅ СТАТИСТИЧЕСКИ ЗНАЧИМА!

В этот момент я встречаюсь с Product Lead:

"У нас есть результаты. Day-1 retention улучшился на 3.6pp (от 65% к 68.6%), p-value = 0.003 (значима). Но у меня есть 2 вопроса перед тем как дать go/no-go:

  1. Guardrail метрики в норме? Crash rate немного выше (0.3% vs 0.1%), но в пределах приемлемо
  2. Как выглядит вторая неделя? Ждём день 7 для Day-7 Retention?"

Product Lead говорит: "Аналитик, это важное улучшение. Можем ли мы развернуть раньше?"

Фаза 3: Стратегия принятия решения

Мой фреймворк для этого вопроса

Есть 3 стратегии принятия решения:

Стратегия 1: Fixed Horizon (Фиксированный период) — ПРАВИЛЬНО в нашем случае

Определяем период заранее (7 дней)
Не останавливаем даже если результаты "очевидны" на день 2
Почему это работает:
- Избегаем selection bias (ранние adopters != весь трафик)
- Покрываем день-недельные паттерны
- Избегаем ошибки первого рода (false positive)

Моё мнение: "Рекомендую Fixed Horizon. Вот почему:

  1. Selection bias. День 1-4 пользователи - это ранние adopters, они более engaged независимо от фичи
  2. Power. Мы рассчитали мощность на 7 дней. Прерывание на день 4 снижает мощность
  3. Guardrail. Crash rate повышена. Нужно видеть долгосрочный тренд
  4. Регрессия к среднему. Может быть флуктуация, день 5-7 покажет правду"

Стратегия 2: Sequential Testing (Sequential analysis) - ЕСЛИ бы нужно было быстрее

Мониторим p-value каждый день
Если p-value < 0.005 (более строгий порог) - можем остановить
Почему это работает:
- Позволяет остановить очень очевидные случаи
- Использует sequential probability ratio test (SPRT)
- Контролирует family-wise error rate

Эта стратегия для срочных случаев, но требует math expertise.

Стратегия 3: Adaptive Horizon - НИКОГДА

Смотрим на результаты, потом решаем как долго гонять
❌ НИКОГДА так не делай - это p-hacking

Мой вывод для Product Lead

"Вот моя рекомендация:

Вариант 1 (Мой выбор): Жди 7 дней. Это стандартный подход, защищает от ошибок.

Вариант 2: Если очень срочно - можем использовать Sequential Testing (нужен более строгий p-value < 0.005). Это даст нам возможность остановить на день 3-4, но требует более высокого уровня значимости.

Вариант 3: Если просто не терпится - можем сделать так: продолжаем тест 7 дней, но с раннего прерывания на день 4, если p-value < 0.01. Это компромисс.

Советую выбрать Вариант 1, но выбор за вами."

Фаза 4: Итоговое решение (День 7)

День 7 результаты

-- День 7 результаты (полная неделя)
SELECT 
  variant,
  COUNT(DISTINCT user_id) as cohort_size,
  -- День 1 retention
  ROUND(100.0 * SUM(CASE WHEN day1_opened = 1 THEN 1 ELSE 0 END) / COUNT(*), 2) as day1_ret,
  -- День 7 retention
  ROUND(100.0 * SUM(CASE WHEN day7_opened = 1 THEN 1 ELSE 0 END) / COUNT(*), 2) as day7_ret,
  -- Feature adoption
  ROUND(AVG(features_used), 1) as avg_features,
  -- Engagement
  ROUND(AVG(session_time_mins), 0) as avg_session_time
FROM onboarding_cohort
WHERE cohort_date = CURRENT_DATE - INTERVAL '7 days'
GROUP BY variant;

-- Результаты:
variant   | cohort_size | day1_ret | day7_ret | avg_features | avg_session_time
----------|------------|----------|----------|------------|----------------
control   | 3,200      | 65.0%    | 42.1%    | 3.2        | 18
treatment| 3,150      | 68.6%    | 45.3%    | 4.8        | 22

Статистические тесты

Day-1 Retention:
- Control: 65.0% (2,080 / 3,200)
- Treatment: 68.6% (2,161 / 3,150)
- Difference: +3.6pp
- Chi-square p-value: 0.0031 ✅ ЗНАЧИМА
- 95% CI: [1.2%, 6.0%]

Day-7 Retention:
- Control: 42.1% (1,347 / 3,200)
- Treatment: 45.3% (1,427 / 3,150)
- Difference: +3.2pp
- Chi-square p-value: 0.0087 ✅ ЗНАЧИМА
- 95% CI: [0.8%, 5.6%]

Guardrail Metrics:
✅ Crash rate: control 0.10%, treatment 0.11% (OK)
✅ App load time: control 1.2s, treatment 1.2s (OK)

Итоговое решение

Критерии для GO:

  1. ✅ Primary metric улучшена: +3.6% на day-1, +3.2% на day-7
  2. ✅ Статистически значима: p < 0.05 для обеих
  3. ✅ Guardrail метрики не деградировали
  4. ✅ Secondary metrics улучшены: +50% feature discovery (3.2 → 4.8)
  5. ✅ Business impact: +3.6pp on 20M DAU = 720K дополнительных активных пользователей

РЕШЕНИЕ: GO - развёртываем фичу

План развёртывания

Д-1: Code review и подготовка
Д-0: 50% трафика (1 час, монитор)
Д-1: 100% трафика на мобильных платформах
Д+7: Проверка метрик в боевых условиях
Д+30: Итоговый анализ настоящего impact

Фаза 5: Post-launch мониторинг

День 1 после развёртывания (100%)

-- Проверка что нет неожиданных проблем
SELECT 
  DATE(event_time) as date,
  COUNT(DISTINCT user_id) as users,
  ROUND(100.0 * SUM(CASE WHEN crash = 1 THEN 1 ELSE 0 END) / COUNT(*), 3) as crash_rate,
  ROUND(AVG(session_time), 0) as avg_session_time
FROM events
WHERE event_date = CURRENT_DATE
GROUP BY DATE(event_time);

-- Если crash_rate вдруг > 0.5% - отката!

Итог: Моя стратегия принятия решения

Ключевые принципы

  1. Fixed Horizon. Определяю период заранее, не меняю
  2. Multiple metrics. Смотрю не только primary, но и guardrails
  3. Statistical rigor. Требую p < 0.05, не раньше
  4. Business context. Не только статистика, но и реальный impact
  5. Post-launch monitoring. После развёртывания проверяю реальные результаты

Почему это работает

  • Избегаю selection bias
  • Не переоцениваю ранние результаты
  • Защищаю компанию от ошибок первого рода
  • Обеспечиваю confidence в развёртываемые фичи