Успешность любого продукта во многом зависит от того, насколько точно он отвечает запросам конечных пользователей. Но даже если продакт-менеджер обладает гигантским опытом и великолепным чувством вкуса, есть вероятность принять неверное решение, ведь продукт должен быть ориентирован на определенную аудиторию и учитывать постоянно меняющиеся тренды. Поэтому перед разработкой любые продуктовые гипотезы желательно проверять на целевой аудитории. Есть несколько методик проведения подобных исследований, одна из них — A/B-эксперименты. 

Меня зовут Евгений Мочалин. Я работаю в технической команде медицинской компании СберЗдоровье. В этой статье я хочу рассказать о нашей A/B-платформе, предпосылках ее появления, итоговой реализации и планах на будущее. Но начнем с небольшой теории. Поехали!

Об A/B-экспериментах «на пальцах»

Представьте ситуацию: дизайнер рисует скин для условной игры, но не может определиться, как сделать лучше. Как быть? Делать наугад? Очевидно, нерациональный путь. Разумнее — спросить у геймеров, для которых скин и делается, какой из вариантов лучше.

Тем более с подобным опросом все условно просто: если за первый вариант проголосует 100 человек, а за второй 150, то останавливаем выбор на втором варианте.

Это и есть A/B-тестирование в его простейшем виде.

Такой тип исследования практически универсальный — например, с его помощью веб-разработчики могут подменять часть сайта и понимать: какой цвет и нейминг лучше подходит для определенных блоков, где лучше размещать меню, с какими блоками контента пользователи взаимодействуют активнее и так далее.

Здесь важно, что A/B-эксперимент — простой, но достоверный тип исследований. Это достигается за счет того, что гипотезы проверяются одновременно на разных группах пользователей: одна работает с обычным продуктом, а другая — с экспериментальным. С каждой группы собираются метрики, по которым можно определить, какой продукт лучше.

Почему важны A/B-эксперименты

При разработке без проведения A/B-экспериментов неизбежно возникает ряд сложностей.

  • Легко пойти в неверном направлении. Есть много вводных, оказывающих влияние на результат, поэтому при разработке стратегии легко не учесть какой-то из факторов, а через несколько спринтов, в результате реализации нескольких фич, получить ухудшение продукта.

  • Высокий ценовой порог фичи. Когда продукт завоевывает определенную долю рынка, возрастает важность удержания позиций. Все выпускаемые фичи должны быть выполнены в максимальном качестве, что значительно повышает стоимость поддержки. При этом нет гарантий, что фича вообще нужна пользователям.

  • Риск упущенной выгоды. Смелые и революционные идеи воплощать рискованно, поэтому они часто отбрасываются, хотя потенциально могли принести прибыль.

Проведение A/B-тестов помогает избежать подобных издержек.

  • Уменьшение влияния когнитивных искажений. В каждую гипотезу вложены личные силы и надежды, поэтому эмоционально формируются завышенные требования. Проводя эксперименты, можно получить метрики, которые отражают реальные факты без когнитивных искажений. Это позволяет переосмыслить и скорректировать стратегию на ранних этапах.

  • Дешевые эксперименты. Цель эксперимента — проверить одну продуктовую гипотезу. Поэтому код эксперимента будет жить в проекте недолго: можно быстро реализовать фичу и удалить ее сразу после завершения эксперимента. Небольшой тест явно дешевле полноценной реализации фичи.

  • Смелые эксперименты. Появляется возможность раскатить экспериментальную версию на небольшую часть аудитории, поэтому неудача при проведении эксперимента затронет незначительную часть бюджета. А успех поможет выявить конкурентное преимущество.

Таким образом, следуя практике регулярных A/B-экспериментов, можно инкрементально повышать удовлетворенность пользователей продуктом.

Кейс близкий к реальному

Алгоритм проведения А/В-эксперимента следующий:

  • формулируем гипотезу;

  • выбираем метрику;

  • определяем размер выборки;

  • запускаем эксперимент и собираем метрики;

  • обрабатываем результаты.

Формулируем гипотезу

Есть страница анкеты врача. В текущем варианте фото врача занимает почти половину страницы. Предполагаем, что часть этого места стоит перераспределить и добавить основные активные элементы: запись на конкретный слот, выбор клиники, обратный звонок.

Гипотеза: «если сделать карточку врача более компактной, то конверсия увеличится»

Выбираем метрику

У пользователя появляется несколько вариантов для быстрой записи в один клик уже на первом экране, поэтому ключевой метрикой возьмем конверсию в заявку. Считаем, что обычно на этой странице показатель около 10%. Если наша гипотеза верна, тогда конверсия для пользователей, у которых высота экрана менее 800px, поднимется, допустим, на 30%, и составит 13% в абсолютном выражении.

Определяем размер выборки

Понятно, что чем больше трафика пустить в каждый эксперимент, тем достовернее результат. С другой стороны: чем меньше трафика нужно под каждый эксперимент, тем больше экспериментов за единицу времени мы проведем, и тем больше полезных улучшений сможем сделать. Поэтому нужно найти минимальное достаточное количество измерений, которые нужно сделать, чтобы подтвердить или опровергнуть гипотезу.

Чтобы определить оптимальные размеры выборок можно использовать любой из многих доступных калькуляторов. Так можно получить достоверный результат быстро и с минимальными вложениями.

Например:

  • Количество вариантов в нашем случае два: стандартная карточка и экспериментальная.

  • Средний показатель конверсии: 10%. 

  • Ожидаемый абсолютный процент: +3% (с 10% до 13%).

  • Достоверность: оставляем 95%.

  • Мощность: оставляем 80%.

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

Если качества анализа, предоставляемого калькулятором недостаточно, тогда можно привлечь одного или нескольких аналитиков.

Примечание: Надо помнить, что при любом измерении возможны ошибки. Чем больше измерений, тем выше достоверность, но шанс ошибки всегда не нулевой. Поэтому важно понимать суть происходящего и знать, в какой момент нужно остановиться.

Запускаем эксперимент и собираем метрики

Тут дело техники: отображаем каждой группе свой вариант эксперимента, отправляем события, собираем статистику. 

Обрабатываем результаты

На первый взгляд: «где больше — там и лучше». Но это не всегда так — важно учитывать реальную значимость изменений.

В первом приближении для этого также можно воспользоваться калькулятором.

Допустим, мы показали наш эксперимент в 4000 визитов. В первой группе получили 211 заказов при 2000 показов, что соответствует 10,5% конверсии, а во второй 248, что соответствует 12,4% конверсии. 

Получаем вывод калькулятора: «Варианты A и B значимо не различаются». Различия вроде есть, но при выставленных критериях они не значимы.

Дальше можно:

  • добавить трафик;

  • отбросить этот эксперимент и перейти к следующему;

  • обратиться к аналитику и глубже разобраться в вопросе и сложившейся ситуации.

Когда с методологией все более-менее прояснилось, стоит заняться выбором инструмента.

Инструмент для проведения A/B-экспериментов

До конца 2021 года мы использовали комбинацию Google Tag Manager + Google Optimize и не особо горевали.

Optimize давал много профита с минимальными затратами:

  1. Даром. Сам инструмент бесплатный, порог входа низкий, поэтому разрабатывать эксперименты было быстро и дёшево.

  2. Гибкость. Можно писать gtm-скрипты, которые будут выполняться только на выбранных страницах и пользователях. В gtm-скриптах доступна максимальная гибкость: можно перестраивать DOM, загружать какие-либо сторонние скрипты. При этом изменения долетают до прода быстро.

  3. Функционал из коробки. Есть веб-интерфейс, где можно выбрать нужную страницу и определенный сегмент пользователей. К тому же Optimize максимально просто интегрируется с GTM и GA.

Но преимущества имели и обратную сторону медали.

  1. Не совсем даром. Как известно: «если на рынке не платишь за товар, то товар — ты». Так и в случае стороннего инструмента: внешний вендор потенциально имеет возможность агрегировать и обрабатывать данные, а также поведение наших пользователей.

  2. Опасная гибкость. GTM-скрипты настолько дешево и быстро прикрывают заплатки, что воспринимаются бизнесом, как волшебная палочка, которая всегда поможет дешево и быстро сделать задачу. Поэтому без тотального контроля скрипты быстро превращаются в свалку мусора, что увеличивало стоимость поддержки.

  3. Функционал из коробки. Ключевой момент, что сначала отрисовывается дефолтная версия, а после ответа Optimize уже на ходу страница переделывается в экспериментальную.

Последний недостаток стоит разобрать подробнее.

Так, при загрузке страницы с сервера прилетает код html + css. Страница отрисовывается и готова к использованию.

Только после этого загружаются и выполняются сторонние скрипты, в том числе и Optimize. Этот скрипт собирает данные о странице и пользователе. 

Далее эти данные отправляются на сервера Optimize, чтобы в ответ получить правильный набор скриптов, соответствующий именно этой странице и именно этому пользователю.

Допустим, пользователь попал в экспериментальную группу. Скрипт должен перестроить ему DOM-дерево, в соответствии с экспериментом. Например, так:

Подобное поведение доставляло неудобства пользователям, да и скачки метрик LCP и CLS на это недвусмысленно намекали.

В какой-то момент мы начали проводить больше A/B-тестов и проблемы, с которыми мы раньше могли мириться, постепенно стали для нас тяжелым грузом, с которым невозможно двигаться дальше. В итоге, в начале 2022 года мы инициировали разработку собственной A/B-платформы.

На основе своего опыта, мы сформировали требования к инструменту:

  1. Только внутри. Исключить передачу пользовательских данных за контур компании.

  2. Сразу правильно. Никаких перерисовок — с сервера сразу должен приходить html, css, js с учетом попадания пользователя в эксперименты.

  3. Единое место с конфигами. A/B-эксперименты хорошо себя зарекомендовали, их количество только растет. Поэтому нам важно понимать, какие эксперименты запущены, какие запланированы и на какую аудиторию.

  4. Гибкость воронки экспериментов. Важно эффективно использовать существующий трафик и шарить его между экспериментами, поэтому нужны бакеты, слои и сегменты.

Строим свою A/B платформу

Мы выделили три основные части A/B-системы:

  • Splitter — ядро системы. Должно хранить информацию об активных экспериментах и отвечать, в какие эксперименты попал конкретный пользователь на конкретной странице. Splitter должен уметь хорошо масштабироваться и быть по возможности stateless.

  • Back — библиотека, подключаемая к разным проектам бека. Должна предоставлять методы для похода в сплитовалку за экспериментами. Тут особым случаем является холодный старт, когда пользователь заходит на страницу впервые — нужно установить ему свободный id (нам не нужно фингерпринтить пользователя, нужно лишь обозначить его, чтобы, например, не отображать разные эксперименты при каждой перезагрузке страницы).

  • Front — библиотека, подключаемая к разным фронтовым проектам. Должна давать стандартные инструменты для работы с экспериментами и типовых сценариев. Вместе с тем, привязка к конкретному фреймворку нежелательна, поэтому остаемся независимыми от фреймворков, настолько долго, насколько это возможно.

Чтобы показать, как работает наша система, разберем конкретный пример.

Пользователь находит в поисковике нужного врача и пытается зайти на сайт, поэтому на один из серверов приходит запрос следующего вида:

GET https://docdoc.ru/doctor/Ivan_Ivanov

Из этого запроса мы должны получить три группы данных:

  • URL, на который идет пользователь;

  • id пользователя;

  • meta — все метаданные, которые можно использовать для сегментации пользователей (user-agent, client hints, …).

Далее сплитовалка определяет, к какому бакету, слою и сегменту относится данный запрос и возвращает все необходимые данные об экспериментах (в минимальном варианте это массив объектов). Для каждого эксперимента в нем один объект, который имеет два поля: 

  • id: string — идентификатор эксперимента;

  • variant: number — вариант, в который попал конкретный пользователь.

На этом этапе мы уже можем вернуть пользователю html + css + js, которые содержат верстку с учетом всех активных экспериментов.

Я в компании занимаюсь фронтом, поэтому давайте про эту часть раскажу подробнее.

Фронтовая библиотека

В СберЗдоровье около 80% фронтового кода написано на React и TypeScript. Также есть один большой проект, который находится на поддержке и в процессе переезда с Vue 2 на React. А если поискать в дальних углах, то пару раз в год можно встретить что-то, написанное на чистом JS. Поэтому фронтовая библиотека нашей A/B-платформы написана на TypeScript, но перед релизом собирается в нативный JS + .d.ts файлы типов.

Версионируется по semver, хранится во внутреннем npm registry.

Установить библиотеку можно любым стандартным способом:

yarn add @sh/experiments
npm install @sh/experiments
bun install @sh/experiments

В проекте обычно есть место с инициализацией разных библиотек, сервисов, сторов. Туда же стоит добавить инициализацию библиотеки экспериментов. Инициализация нужна, чтобы сохранить в замыкании инстанса библиотеки все эксперименты и получать к ним доступ из любой части приложения, а также чтобы отправить события в аналитику обо всех экспериментах, в которые попал конкретный пользователь.

import { init } from ‘@sh/experiments’
import { pushEvent, enterExperiment } from ‘@sh/experiments’
init(experiments).forEach((experiment) => {
  pushEvent({
    eventLabel: experiment.id,
    variant: experiment.variant
  })
})

В большинстве случаев в коде можно обойтись простым условием. Например, таким:

const exp = getExperimentById(‘ab500’)
{exp?.variant === 1 ? <CompA /> : <CompB />}

Множество параллельных экспериментов могут сильно усложнить код в подобных местах, поэтому мы всячески стараемся сохранять простую логику.

Например, у нас есть специальный каталог для экспериментального кода. В него можно положить временные компоненты, методы и другие файлы. Если в эксперименте меняется только 3-4 места в верстке некоторого компонента, мы можем взять готовую реализацию и с помощью небольших правок просто доработать ее под условия нового теста, а переключать одним условием, а не тремя. Такое возможно, потому что суть теста — только проверить гипотезу, и после получения результата код теста можно спокойно удалить.

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

Например, у нас многие эксперименты звучат так: «Заменить один блок другим и, когда пользователь впервые его увидит, отправить событие». Под этот стандартный кейс библиотека предоставляет метод addObserver, который добавляет intersectionObserver к целевому элементу и выполняет callback onFirstTimveVisible.

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

const componentRef = useRef<HTMLDivElement>
useExperiment(componentRef, exp500)
return exp500?.variant === 1 ? <CompA ref={componentRef} /> : <CompB />

Планы на будущее и выводы

Мы продолжаем активно развивать свою A/B-платформу. В рамках ее улучшения, мы уже определили вектор будущей модернизации.

  • Веб админка. Сейчас каждый эксперимент описан в виде YAML-конфига, но различия в бакетах, слоях и сегментах в тексте воспринимать сложно, поэтому будем двигаться в сторону создания удобного и понятного интерфейса.

  • Mobile. Почти всё из вышеперечисленного подходит не только для веба, поэтому уже прорабатывается реализация A/B-экспериментов для мобильных приложений.

  • Back. У нас уже готово решение, с помощью которого можно будет, в том числе, выполнять подмену микросервисов и на бекенде.

На своем опыте мы убедились, что работать со сторонними решениями для A/B-экспериментов можно, но это сопряжено с необходимостью соглашаться на компромиссы. Поэтому в нашем случае рациональнее строить собственную платформу. К тому же, свое решение — гарантия, что инструмент не станет внезапно недоступен. Например, Google Optimize с ноября 2023 года закрыт, поэтому наша миграция на собственное решение и в этом плане оказалась своевременной и оправданной.

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


  1. kozlov_de
    15.05.2024 22:02

    Говорят, а/б тестирование удобно на микросервисах: сделал два варианта и балансируй количество вызовов каждый версии, потом смотри аналитику поведения пользователей