A/B-тестування елементів сторінки
A/B-тестування — контрольований експеримент, у якому одна група користувачів бачить оригінальну версію (контроль), інша — змінену версію (варіант). Статистично значуща різниця в конверсії доводить ефект змін.
Що можна тестувати
- Заголовок і текст CTA-кнопки
- Колір, розмір, розташування кнопки
- Форма: кількість полів, порядок, мітки
- Зображення та відео на головному екрані
- Соціальні докази (відгуки, лічильники)
- Елементи довіри (значки безпеки, гарантії)
- Ціни та їх відображення
Мінімальний розмір вибірки
Перед запуском тесту розрахуйте необхідну вибірку:
from scipy import stats
import math
def calculate_sample_size(baseline_rate, min_detectable_effect, alpha=0.05, power=0.8):
"""
baseline_rate: поточна конверсія (0.05 = 5%)
min_detectable_effect: мінімальний ефект (0.10 = 10% відносне поліпшення)
"""
p1 = baseline_rate
p2 = baseline_rate * (1 + min_detectable_effect)
z_alpha = stats.norm.ppf(1 - alpha / 2)
z_beta = stats.norm.ppf(power)
p_avg = (p1 + p2) / 2
n = (z_alpha * math.sqrt(2 * p_avg * (1 - p_avg)) +
z_beta * math.sqrt(p1 * (1 - p1) + p2 * (1 - p2))) ** 2 / (p2 - p1) ** 2
return math.ceil(n)
# Приклад: конверсія 3%, хочемо виявити поліпшення 15%+
n = calculate_sample_size(0.03, 0.15)
print(f"Need {n} users per variant = {n*2} total")
# Need 3,842 users per variant = 7,684 total
Реалізація клієнтського A/B-тесту
// Простий A/B тест без зовнішніх інструментів
function getVariant(testName) {
const stored = localStorage.getItem(`ab_${testName}`)
if (stored) return stored
const variant = Math.random() < 0.5 ? 'control' : 'variant'
localStorage.setItem(`ab_${testName}`, variant)
// Відправити в аналітику
gtag('event', 'ab_test_assignment', {
test_name: testName,
variant: variant
})
return variant
}
// Застосування тесту
const variant = getVariant('cta_button_color')
const ctaButton = document.getElementById('main-cta')
if (variant === 'variant') {
ctaButton.style.backgroundColor = '#FF6B35' // помаранчевий замість синього
ctaButton.textContent = 'Почати безкоштовно'
} else {
// Залишити за замовчуванням
}
// Відстеження конверсії
ctaButton.addEventListener('click', () => {
gtag('event', 'cta_click', {
test_name: 'cta_button_color',
variant: variant
})
})
Серверний A/B-тест (переважно)
Клієнтський тест викликає мерехтіння (flicker effect). Серверний варіант — краще:
// Laravel Middleware: призначити варіант перед рендерингом
class AbTestMiddleware
{
public function handle(Request $request, Closure $next)
{
$testName = 'checkout_form_v2';
$userId = auth()->id() ?? $request->session()->getId();
// Детерміністичне призначення за ID користувача
$variant = (crc32($userId . $testName) % 2 === 0) ? 'control' : 'variant';
$request->merge(['ab_variants' => [$testName => $variant]]);
View::share('ab_variants', [$testName => $variant]);
$response = $next($request);
$response->headers->set('X-AB-Variant', $variant);
return $response;
}
}
{{-- У шаблоні --}}
@if($ab_variants['checkout_form_v2'] === 'variant')
@include('checkout.form-v2')
@else
@include('checkout.form-v1')
@endif
Аналіз результатів
from scipy.stats import chi2_contingency, proportions_ztest
import numpy as np
def analyze_ab_test(control_visitors, control_conversions,
variant_visitors, variant_conversions):
# Коефіцієнти конверсії
cr_control = control_conversions / control_visitors
cr_variant = variant_conversions / variant_visitors
relative_change = (cr_variant - cr_control) / cr_control * 100
# Z-тест для пропорцій
count = np.array([variant_conversions, control_conversions])
nobs = np.array([variant_visitors, control_visitors])
z_stat, p_value = proportions_ztest(count, nobs)
print(f"Control: {cr_control:.2%} ({control_conversions}/{control_visitors})")
print(f"Variant: {cr_variant:.2%} ({variant_conversions}/{variant_visitors})")
print(f"Relative change: {relative_change:+.1f}%")
print(f"P-value: {p_value:.4f}")
print(f"Statistically significant: {'YES' if p_value < 0.05 else 'NO'}")
analyze_ab_test(
control_visitors=3842, control_conversions=115,
variant_visitors=3891, variant_conversions=148
)
Типові помилки
- Зупинити тест завчасно — p-value коливається спочатку, потрібно дочекатися розрахункового обсягу
- Тестувати кілька змін одночасно — незрозуміло, яка зміна дала ефект (це вже Multivariate тестування)
- Ігнорувати сегменти — тест нейтральний в цілому, але поліпшує мобільних на 25%
- Не враховувати сезонність — тестуйте в репрезентативний період
Час виконання
Налаштування A/B-тесту з серверним сплітом, аналітикою та аналізом результатів — 2–4 робочих дні.







