Як провести А/В тест на фронтенді в продукті. Кейс з різними формами оплати
Мене звати Артем Дрей, я займаюсь Frontend-розробкою в венчур-білдері SKELAR. Ми будуємо продуктові IT-бізнеси на західні ринки. В процесі у нас як правило з’являється багато ідей щодо покращення наших продуктів: додати нові фічі в інтерфейс, оновити механіку реєстрації або перемалювати форму оплати.
Для визначення, що наші дії виправдали себе й принесли очікуваний результат, ми проводимо A/B тестування. Запускаємо продуктовий тест всередині продукту, збираємо репрезентативну вибірку й на основі цього приймаємо рішення на користь одного з варіантів, який найбільше відгукнувся у наших користувачів.
В цьому матеріалі я вирішив поділитися одним з наших кейсів про тестування двох варіантів форм оплати на фронтенді. Описую, як ми це проводили та як збирали результати.
Проведення А/В тестування — теорія
А/B тест (спліт-тест) — це інструмент для прийняття рішень на основі даних. У процесі проведення тестування порівнюються дві версії одного й того самого продукту, різниця між якими лише в одному елементі. Так можна тестувати, наприклад: форму реєстрації, email-розсилку, ціни і т.д.
В результаті A/B тесту отримуються дані, провівши аналіз яких можна зробити висновок про те, який саме варіант дасть найбільшу конверсію в цільову дію. Після цього тест закривається на користь групи, що перемогла в процесі тестування.
- «0» — контрольна група юзерів, яка бачить продукт без змін;
- «1» — група юзерів, які бачать змінений продукт (нові UI/UX, фічі, логіка).
План А/B тестування на нашому кейсі — практика
У даному кейсі ми розберемо, як покращити форму оплати в продукті. Для цього юзеру відображаються pop-up для вибору пакета, після чого його переводить на крок оплати.
Для тестування була створена наступна гіпотеза: «Якщо зробити вибір пакета простішим та без зайвого інформаційного шуму, тоді конверсія в оплату збільшиться».

Платіжна форма ліворуч — відображається у групі «0», праворуч — у групі «1».
Механіка проведення тесту:
- Реєстрація юзера на продукті.
- Після реєстрації, за допомогою splitter-сервісу на backend відбувається розподіл юзера в одну з груп: «0» або «1».
- З кожним запитом «me» отримуються дані по спліт-тесту.
- Спираючись на цю інформацію, frontend відображає різні версії продукту.
- Після збору й отримання статистично значущих даних, спліт-тест закривається на користь однієї з груп.
Як ми організували проведення А/В тесту на фронтенді
Ділюся своїми напрацюваннями та фрагментами коду, які я використовував для розв’язання задачі.
1. Звідки фронтенд дізнається про тест
При ініціалізації React застосунку спочатку відбувається запит me, після якого повертається інформація про спліт-тест: splits: []
idSplit — унікальний ідентифікатор cпліт-тесту.splitGroup — група: «0» чи «1» (в даному кейсі розподіл трафіку 50/50).
me: {
id: 'ID_ME',
// ...,
splits: [
{
idSplit: 'ID_SPLIT_TEST',
splitGroup: '0',
createdAt: '2022-12-17T10:11:54Z',
},
],
},
2. Створюємо додаткові компоненти, що будуть реалізовувати зміни в інтерфейсі/ логіці
У випадку якщо зміни мінорні, зробити це можна в рамках одного компоненту.
Проте, як показує практика, краще одразу створити поруч новий компонент, та реалізувати там всю необхідну логіку. Тому:

2.1. <PaymentPackage /> — компонент, який відображає деталі пакета (ціна, кількість валюти), після вибору якого переходимо на етап оплати (група «0»).
2.2. <SplitPaymentPackage /> — новий компонент з такою самою логікою, але іншим UI (група «1»).
Для всіх назв використовується конвенція з додавання слова Split на початку назв. Це є необхідним задля простішої орієнтації в коді й знаходження всіх рядків зі спліт-тестом, коли потрібно буде його закрити (тобто, замінити два варіанти на той один, що переміг).
3. Імплементація A/B тесту
Далі в компоненті, який рендерить список пакетів, за допомогою getSplitTestById перевіряємо, чи знаходиться юзер в гуппі «1». Якщо так, то показуємо йому новий інтерфейс. У всіх інших випадках показуємо попередній.
getSplitTestById — утиліта, яка повертає дані спліт-теста по id. У випадку, якщо нічого не знайдено, повертається порожній стейт.
type IdSplitTest = 'simple_payment_modal_ui' | 'some_other_id' | ...;
interface SplitUser = {
idSplit: string;
createdAt: string;
splitGroup: string;
};
const getSplitTestById = (splits: SplitUser[], idSplitTest: IdSplitTest) => {
const emptyState = {
idSplit: '',
splitGroup: '',
createdAt: '',
};
return splits.find(({ idSplit }) => idSplit === idSplitTest) || emptyState;
};
Рендеринг пакетів, спираючись на спліт-групу.
const { splitGroup } = getSplitTestById(splits, 'simple_payment_modal_ui');
const isSplitGroupOne = splitGroup === 1;
{packages.map((package) => {
if (isSplitGroupOne) {
return <SplitPaymentPackage key={package.id} {...package}/>
}
return <PaymentPackage key={package.id} {...package}/>
})}
Усі інші компоненти та фічі розгалужуються за такою ж самою схемою.
Результати тесту
Спліт-тест триває до того моменту, поки не накопичить достатньо статистично значущих даних. На їх основні, аналітики приймають рішення про те, на користь якої з груп закривати тест — який варіант інтерфейсу спрацював краще.
В нашому кейсі переможцем стала контрольна група «0». Отже, наразі ми залишаємо продукт без змін допоки не виникне потреби в проведенні наступних тестів. Коли рішення прийняте, необхідно залишити один з варіантів реалізації та видалити весь зайвий код (в нашому кейсі для варіанту «1»).
5 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів