Какую гипотезу проверял в последнем A/B тесте?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Последний 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 для нашего размера выборки
Почему тест удался
- Хорошая гипотеза — основана на реальных наблюдениях (support tickets, поведение в продукте)
- Правильная рандомизация — случайное распределение пользователей
- Достаточный размер выборки — 1600+ пользователей дали нам мощность 90%
- Четкие метрики — знали, что мерим, ДО теста
- Правильная длительность — 2 недели достаточно для этого паттерна
- Анализ не просто одной метрики — смотрели на поведение, деньги, 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!
Что я выучил из этого теста
- Прозрачность > неопределенность — люди готовы платить, если понимают цену
- Меньше кликов может быть лучше — качество > количество
- Слушай support — они видят реальные проблемы
- Сегментируй результаты — тест может работать по-разному для разных групп
- Смотри на несколько метрик — одна метрика может обманывать
Итого
Этот тест показал, что иногда решение простое — нужно просто быть прозрачным с пользователями. Это привело к:
- Уверенности клиентов в цене
- Сокращению поддержки
- Значительному увеличению дохода
- Общему улучшению удовлетворения пользователя
Именно такие тесты показывают ценность Product Analytics — мы не просто смотрим данные, мы решаем реальные бизнес-проблемы.