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

Какую гипотезу проверял в последнем A/B тесте?

1.3 Junior🔥 171 комментариев
#A/B тестирование#Опыт и проекты

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

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

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

Последний A/B тест: гипотеза и результаты

Основываясь на своем опыте, расскажу о недавнем тесте, который показал интересные результаты и научил меня важным урокам.

Контекст

Я работал в SaaS компании с инструментом для управления контактами. Компания замечала, что многие пользователи были озадачены по поводу стоимости добавления новых членов команды.

Проблема

  • Пользователи кликали на цену много раз
  • В support приходило много вопросов о количественных скидках
  • Мы видели, что пользователи не добавляли новых людей в команду
  • Была догадка: прозрачность цены на добавление пользователя критична

Гипотеза

Основная гипотеза: "Если показать точную стоимость добавления нового пользователя в момент приглашения, то пользователи будут приглашать больше людей, потому что избавятся от неопределенности."

Метрика успеха: +15% в среднем количестве приглашенных пользователей на одного аккаунта за месяц.

Вторичные метрики:

  • Avg team size (средний размер команды)
  • MRR growth (прирост monthly recurring revenue)
  • Support tickets о цене (снижение)

Дизайн теста

Control (текущий вариант):

  • Кнопка "Пригласить члена команды"
  • При клике: открывается форма, но БЕЗ информации о цене
  • Пользователю нужно отправить приглашение, а потом увидеть в счете

Variant (новый вариант):

  • Та же кнопка
  • При клике: всплывающее окно ДО отправки приглашения
  • Показывает: "Это стоит $X в месяц"
  • Рассчитывает примерную сумму: "Приглашаешь 3 человека = +$Y в месяц к твоей подписке"

Техническая реализация

-- Создание тестового сегмента
CREATE TABLE ab_test_assignments (
  test_id UUID PRIMARY KEY,
  user_id UUID NOT NULL,
  variant VARCHAR(20),  -- 'control' или 'variant'
  assigned_at TIMESTAMP DEFAULT NOW(),
  CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id)
);

-- Рандомизация
INSERT INTO ab_test_assignments (user_id, variant)
SELECT 
  id,
  CASE 
    WHEN RANDOM() < 0.5 THEN 'control'
    ELSE 'variant'
  END
FROM users
WHERE created_at > NOW() - INTERVAL '30 days'
  AND team_size < 20  -- Новичков и SMB, не Enterprise
ON CONFLICT DO NOTHING;

-- Проверка баланса
SELECT 
  variant,
  COUNT(*) as count,
  COUNT(*) * 100.0 / SUM(COUNT(*)) OVER () as percent
FROM ab_test_assignments
GROUP BY variant;

Метрики для отслеживания

-- Главная метрика: среднее количество приглашений
WITH monthly_invites AS (
  SELECT 
    ta.user_id,
    ta.variant,
    DATE_TRUNC('month', ie.created_at) as month,
    COUNT(*) as invites_count
  FROM ab_test_assignments ta
  JOIN invitation_events ie ON ta.user_id = ie.user_id
  WHERE ta.test_id = $1
    AND ie.created_at >= ta.assigned_at
    AND DATE_TRUNC('month', ie.created_at) = CURRENT_DATE
  GROUP BY ta.user_id, ta.variant, month
)
SELECT 
  variant,
  COUNT(DISTINCT user_id) as active_users,
  ROUND(AVG(invites_count), 2) as avg_invites_per_user,
  ROUND(SUM(invites_count), 0) as total_invites,
  STDDEV(invites_count) as stddev_invites  -- для проверки дисперсии
FROM monthly_invites
GROUP BY variant;

-- Вторичная метрика: размер команды
WITH team_growth AS (
  SELECT 
    ta.variant,
    DATE_TRUNC('month', u.created_at) as signup_month,
    COUNT(DISTINCT tm.user_id) as avg_team_members
  FROM ab_test_assignments ta
  JOIN users u ON ta.user_id = u.id
  LEFT JOIN team_members tm ON u.team_id = tm.team_id
  GROUP BY ta.variant, signup_month
)
SELECT * FROM team_growth;

-- Отслеживание поведения в тесте
SELECT 
  ta.variant,
  COUNT(*) as times_shown,
  COUNT(CASE WHEN clicked THEN 1 END) as times_clicked,
  COUNT(CASE WHEN confirmed THEN 1 END) as times_confirmed,
  ROUND(100.0 * COUNT(CASE WHEN clicked THEN 1 END) / COUNT(*), 2) as click_rate,
  ROUND(100.0 * COUNT(CASE WHEN confirmed THEN 1 END) / COUNT(*), 2) as confirmation_rate
FROM ab_test_assignments ta
JOIN pricing_popup_events pe ON ta.user_id = pe.user_id
WHERE ta.test_id = $1
GROUP BY ta.variant;

Результаты после 2 недель

Поведение в тесте:

Control:
- Pricing shown: 500 раз
- Clicked through: 428 (85.6%)
- Invitations sent: 120 (приглашений из этих кликов)
- Conversion: 28%

Variant:
- Pricing shown: 510 раз
- Clicked through: 389 (76.3%)  ← МЕНЬШЕ!
- Invitations sent: 156 (приглашений)
- Conversion: 40%  ← БОЛЬШЕ!

Главная метрика (средние приглашения в месяц):

Control:  2.4 приглашения/пользователь
Variant:  3.1 приглашения/пользователь
Lift:     +29% ✅ (выше, чем 15% целевое)

Статистическая значимость:

Control (n=800):  μ=2.4, σ=3.8
Variant (n=820):  μ=3.1, σ=4.2

t-тест:
t-value: 2.84
p-value: 0.0047 ✅ (p < 0.05, значима!)
95% CI: [+0.29, +1.04]

Вторичные метрики:

Avg team size:
Control: 3.2 человека
Variant: 3.8 человека
Lift: +18%

MRR growth (за месяц):
Control: $2,400 (средний прирост)
Variant: $3,100
Lift: +29%

Support tickets о цене:
Control: 45 тикетов
Variant: 28 тикетов
Lift: -38%

Интересные выводы

1. Парадокс: меньше кликов, больше конверсия

Люди реже кликали на pricing popup, но те, кто кликал, ДЕЙСТВИТЕЛЬНО приглашали людей.

Интерпретация: Прозрачность цены работает как фильтр качества.

  • Control: люди кликают из любопытства, потом не приглашают (75% не конвертятся)
  • Variant: люди кликают, видят цену, и только те, кто готов платить, приглашают (40% конвертятся)
# Анализ когда-то
control_curiosity_only = 428 - 120  # 308 человек
variant_informed_decision = 389 - 156  # 233 человека

print(f"Control: {308} человек кликали просто из любопытства")
print(f"Variant: {233} человека кликали из любопытства")
# Сокращение на 24% "шумовых" кликов

2. Размер и стабильность данных

Деньги хорошо коррелируют с поведением. MRR вырос более предсказуемо, чем просто количество приглашений.

-- Корреляция между invites и MRR
SELECT 
  variant,
  CORR(invites_count, mrr_change)::numeric(3,2) as correlation
FROM ab_test_data
GROUP BY variant;

-- Control: 0.72
-- Variant: 0.85  ← Более стабильная связь

3. Сегментный анализ

Тест работал по-разному в разных сегментах.

WITH segment_analysis AS (
  SELECT 
    ta.variant,
    CASE 
      WHEN u.signup_days < 7 THEN 'new_user'
      WHEN u.signup_days < 30 THEN 'active'
      ELSE 'dormant'
    END as user_segment,
    COUNT(*) as users,
    ROUND(AVG(invites_count), 2) as avg_invites,
    ROUND(AVG(mrr_change), 0) as avg_mrr_change
  FROM ab_test_assignments ta
  JOIN users u ON ta.user_id = u.id
  GROUP BY ta.variant, user_segment
)
SELECT * FROM segment_analysis;

Результат:

Variant работает хорошо для ВСЕХ:
- New users (0-7d):    lift +22%
- Active users (8-30d): lift +35% ← ЛУЧШЕ!
- Dormant (30+d):      lift +12%

Особенно важно: пользователи, которые уже привыкли к платформе, БОЛЬШЕ приглашали после видения цены.

Проверка качества данных

# Проверка на аномалии
from scipy import stats

# Проверка outliers
for variant in ['control', 'variant']:
    data = variant_data[variant]
    z_scores = np.abs(stats.zscore(data['invites']))
    outliers = np.where(z_scores > 3)[0]
    print(f"{variant}: {len(outliers)} outliers (>{3*sigma})")
    
    # Проверка распределения
    stat, p = stats.shapiro(data['invites'])
    print(f"{variant}: Shapiro-Wilk p-value = {p:.4f}")
    # Не нормально, но это OK для нашего размера выборки

Почему тест удался

  1. Хорошая гипотеза — основана на реальных наблюдениях (support tickets, поведение в продукте)
  2. Правильная рандомизация — случайное распределение пользователей
  3. Достаточный размер выборки — 1600+ пользователей дали нам мощность 90%
  4. Четкие метрики — знали, что мерим, ДО теста
  5. Правильная длительность — 2 недели достаточно для этого паттерна
  6. Анализ не просто одной метрики — смотрели на поведение, деньги, support

Принятое решение

Развернули Variant для всех потому что:

  • ✅ p-value < 0.05 (статистически значима)
  • ✅ Lift +29% > целевое +15%
  • ✅ MRR выросла на +29%
  • ✅ Support tickets снизилась
  • ✅ Никаких побочных эффектов
  • ✅ Тест работал хорошо для всех сегментов

Финансовый результат

Дополнительный доход за год:
- Среднее увеличение: +29% invites per user
- Сегодня: 5000 активных пользователей
- Lift: 5000 * (3.1 - 2.4) = 3500 дополнительных приглашений/месяц
- При цене $10/пользователь/месяц = $35k дополнительного MRR
- За год: $35k * 12 = $420k!

Что я выучил из этого теста

  1. Прозрачность > неопределенность — люди готовы платить, если понимают цену
  2. Меньше кликов может быть лучше — качество > количество
  3. Слушай support — они видят реальные проблемы
  4. Сегментируй результаты — тест может работать по-разному для разных групп
  5. Смотри на несколько метрик — одна метрика может обманывать

Итого

Этот тест показал, что иногда решение простое — нужно просто быть прозрачным с пользователями. Это привело к:

  • Уверенности клиентов в цене
  • Сокращению поддержки
  • Значительному увеличению дохода
  • Общему улучшению удовлетворения пользователя

Именно такие тесты показывают ценность Product Analytics — мы не просто смотрим данные, мы решаем реальные бизнес-проблемы.