Хабрахабр

Как Браузер для iOS А/Б-тестирование улучшал. Доклад Яндекса

Не так давно мы посмотрели, как устроены A/Б-эксперименты в Поиске. Руководитель бригады разработки iOS-версии Яндекс.Браузера Андрей Сикерин sav42 на последней встрече CocoaHeads Russia тоже рассказывал про инфраструктуру А/Б-тестирования, только уже в своем проекте.

Я хочу рассказать, что представляет собой платформа экспериментов браузера для iOS, как мы научились ее использовать, поддержали ее более продвинутые возможности, как диагностировать и отлаживать фичи, раскатываемые с помощью системы экспериментов, а еще о том, что же такое источник энтропии и где же хранится монетка.
Итак, начнем. — Привет, меня зовут Андрей Сикерин, я разрабатываю Яндекс.Браузер для iOS. Сначала мы проводим А/Б-тестирование, анализируем продуктовые и технические метрики, чтобы понять, как раскатываемая фича влияет на пользователя, нравится она ему или нет, просаживает ли она какие-то технические метрики. Мы в Браузере для iOS никогда не раскатываем фичу на пользователей сразу. Наша аналитика выглядит примерно так: Для этого мы используем аналитику.

Мы сравниваем несколько групп пользователей. Здесь порядка 85 метрик. Значит, пользователям фича нравится и можно катить на большую группу пользователей. Предположим, это увеличивает наши метрики — например, способность продукта удерживать пользователей (retention) — и не просаживает другие, которые не представлены на слайде.

Строим гипотезы, подтверждаем их. Если же все-таки мы что-то просаживаем, то разбираемся, почему. Исправляем их и перепроводим эксперимент заново. Если мы просаживаем технические метрики — это является блокером. Таким образом, мы выкатываем фичу, которая не является источником регресса. И так пока не прокрасим все.

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

Второе — она изначально умела раскатывать фичи на разные группы пользователей и имела систему фильтров, на которую можно было задать требования к устройствам. Первое — она основана на системе экспериментов Chromium и была поддержана в iOS не полностью. Или версию iOS, на которой эта фича будет доступна, или дату, до которой этот эксперимент будет валидным — например, если мы хотим проводить эксперимент только до какой-то даты. То есть — версию приложения, из которой фича доступна, локаль устройства — допустим, мы хотим эксперимент только для русской локали. В общем, было множество тегов и это было достаточно удобно.

То есть для одного эксперимента может быть сразу несколько конфигураций. Сама система экспериментов состоит из файла, в котором содержатся описания конфигураций экспериментов. Этот файл — текстовый, он компилируется в protobuf и выкладывается на сервер.

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

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

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

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

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

В браузере есть такая фича — Переводчик. Давайте я, собственно, покажу вам файл с описаниями экспериментов. Файл начинается с блока study. Она раскатывалась в эксперименте. Эксперимент называется translator. Конфигурация любого эксперимента начинается с этого блока. И внутри блока study есть много блоков experiment, котоым присвоены различные имена. Таких блоков study с этим именем может быть несколько. И есть блок filter, который, собственно, и описывает, при каких условиях эта конфигурация может стать активной, то есть ее критерии. В данном случае мы видим группу эксперимента enabled.

Channel означает вид сборки. Здесь есть два тега — channel и ya_min_version. Для App Store-сборки эта конфигурация по критерию channel не может стать активной. Тут указана BETA, значит, именно эта конфигурация в файле может стать активной только для тех сборок, которые мы посылаем в TestFlight.

3. ya_min_version означает, что с минимальной версией приложения 19. 43 эта конфигурация может стать активной. 4. Собственно, в этой версии приложения фича уже приобрела такой вид, что можно ее включать.

Таких блоков study в файле может быть много. Это простейшее описание группы конфигурации эксперимента translator. С помощью тегов в блоке filter мы задаем их для разных каналов, для внутренних сборок, для BETA-сборок, для различных критериев.

Это целое неотрицательное число, используемое для определения активной группы в тот момент, когда подкидывается монетка. Здесь одна группа эксперимента, которая называется enabled, и у нее есть тег probability weight, вес группы эксперимента.

То есть мы действительно поставили приложение с public beta, и у нас действительно версия 19. Давайте представим, что эта конфигурация на слайде стала активной. 4. 3. Как же кинется монетка? 43 и далее. Монетка — это случайное число, которое генерируется локально от нуля до единицы.

Пока будем считать так. Чтобы во время следующего запуска мы попадали в ту же самую группу, оно хранится на диске. Выкидывается монетка. Далее расскажу, как сделать так, чтобы она не хранилась. Данная монетка масштабируется в отрезок от нуля до суммы групп экспериментов. Предположим, выкидывается 0,5. «0,5» масштабируется в значение 500. В данном случае у нас одна группа enabled, вес у нее 1000, то есть сумма всех групп будет 1000. И активной становится та группа, в чей промежуток будет указывать масштабированное значение монетки. Соответственно, все группы экспериментов разбивают отрезок от нуля до суммы экспериментов га промежутки.

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

Во-первых, понятно, что на 100% раскатывать фичу глупо, мы используем такое только для beta или для внутренней сборки. Дальше мы посмотрим более сложные конфигурации экспериментов, которые мы используем в продакшене. Для продакшена мы используем следующую механику.

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

Мы устанавливаем в блоке filter условия на теги min_install_date и max_install_date. Каким образом мы добиваемся этого разбиения? Тогда max_install_date для старых пользователей будет X минус 21 день, до релиза сборки с фичей. Предположим, X равно 14 марта 2019 года — это дата релиза сборки с фичей. А до релиза была версия без фичи. Если приложение имеет такую install date, то весьма вероятно, что его первый запуск был до релиза. И если сейчас у него стоит, условно, версия с фичей — значит, он получил приложение с помощью апдейта.

Мы выставляем его как X плюс несколько дней. А для новых пользователей мы выставляем min_install_date. Он сейчас имеет версию с фичей, но install date был позже, чем эта версия с фичей зарелизилась. Это означает: если у него такой install date, то есть первый запуск он осуществил после даты релиза версии с фичей, то у него была чистая установка.

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

Мы видим следующую конфигурацию эксперимента — Переводчик для App Store, новые пользователи. Давайте рассмотрим вот такой эксперимент. Префикс new означает, что мы описываем конфигурацию для множества пользователей, которые новые. Блок study, имя translator, группа enabled_new. Control_new, вес 500, это вторая группа. Вес 500 (если сумма всех весов 1000, значит, мощность этого множества — 50%). Версия, в которой фича появилась: 19. И самое интересное — фильтры для канала STABLE, то есть для сборок, которые собираются для продакшена. 1. 4. Здесь в формате Unix time зашифровано 18 апреля 2019 года. И здесь есть тег min_install_date. 4. Это несколько дней после релиза версии 19. 1.

Вот префикс control, он не случаен. Здесь есть и еще одна часть кроме префикса new, это enabled и control. И помимо того, что мы разбиваем пользователей на новых и старых, мы разбиваем их внутри эксперимента группами еще на несколько частей.

В ней фичи нет. Первая часть пользователей — это контрольная группа, та, которая имеет префикс control. Также есть фичевая группа, обычно она называется enabled. У нее вес X. И есть дефолтная группа, которая имеет вес 1 минус 2X (1000 минус 2X, так как 1000 — значение суммарного веса всех групп внутри одной конфигурации, которое принято у нас по умолчанию). Она также имеет вес X, и это важно: там фича должна включиться. Она просто хранит пользователей, которые остались после разбиения на контрольную и фичевую. Дефолтная группа тоже никакую фичу не включает. Также из нее можно перепровести эксперимент, если это нужно.

Мы увидим здесь фичевую и контрольную группу. Давайте посмотрим, скажем, конфигурацию для старых пользователей. control_old, — контрольная, 10%. enabled_old — фичевая. default_old — дефолтная, 80%.

4. Обратим внимание на filter, ya_min_version 19. Это дата раньше, чем дата релиза. 1, max_install_date 28 марта 2019 года. 4. Соответственно, это конфигурация со списком пользователей, которые получили версию 19. Они пользовались приложением и теперь пользуются новой версией. 1 после апдейта.

В той аналитике, которую я показывал на первом слайде, мы сравниваем контрольную группу и фичевую. Для чего нужны фичевые и контрольные группы? Они обязаны быть равной мощности, чтобы их продуктовые метрики можно было сравнить.

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

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

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

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

То есть, если вы вдруг решили, что часть фичи можно было бы выкатить, а часть еще оставить в эксперименте, то так сделать нельзя, надо было заранее продумывать и разбивать эту фичу на две, и независимо их в эксперименте принимать. Второе, нельзя раскатить части фичи или разбить фичу на части уже после того, как вы начали эксперимент.

В Переводчике, например, есть параметр — время timeout к API Переводчика. Третье, нельзя конфигурировать фичу или сравнивать конфигурации. То есть, если мы не успели перевести за сколько-то миллисекунд, то мы говорим, что, попробуй еще раз, ошибка, не повезло.

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

То есть чтобы разработчику начать разработку фичи, он обычно начинает, собственно, с политики доступности этой фичи. Четвертое, аналитики и разработчики вынуждены согласовывать названия групп заранее. А для этого аналитик должен объяснить механику эксперимента — будем ли мы разделять пользователей на новых и старых или все пользователи окажутся в одной группе без деления. И ему нужно знать имена фичевых групп.

Например, мы можем сразу посчитать, что фича включена, но иметь возможность выключить ее. Или это может быть обратный эксперимент. Он определит механику эксперимента, когда она будет готова. Аналитику это не очень интересно, потому что фича не готова. А разработчику требуются названия групп и механика эксперимента заранее, иначе ему постоянно придется вносить изменения в код.

Так родился проект Make Experiments Great Again. Мы посовещались и решили, что хватит это терпеть.

Если раньше мы привязывались к коду, кодом к именам групп активных, которые нам передавал аналитик, то теперь мы внесли две дополнительные сущности. Ключевая идея этого проекта заключается в следующем. И, таким образом, программист придумывает фичи и фиче-параметры самостоятельно, выбирая для них идентификаторы, выбирая для них дефолтные значения, и программирует относительно них доступность фичи. Это фича (Feature) и параметр фичи (FeatureParam).

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

Вот мы смотрим группу эксперимента. Как это выглядит в файле с конфигурациями экспериментов? Имя enabled, добавляется дополнительный тег feature_association, в этом теге команды enable_feature или disable_feature, и добавляются идентификаторы.

Тут тоже есть имя — timeout, и добавляется значение, которое надо установить. Также есть блок param, которых может быть несколько.

Программист объявляет сущности класса Feature и FeatureParam. Как это выглядит из кода? Затем он передает этот идентификатор аналитику, и он уже в файле конфигураций задает идентификаторы в блоке группы эксперимента с помощью тега feature_association. И в слой доступа к фиче прописывает значения из этих примитивов. Если в группе нет параметров и фич — используются дефолтные значения, которые указаны из кода. Как только группа эксперимента становится активной, значения фич и параметров с этими идентификаторами в коде задаются из файла.

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

Аналитик разрабатывает тогда, когда фича готова, а программист придумывает эти идентификаторы тогда, когда он пишет код, в самом начале. Во-вторых, мы разнесли время, когда программисты придумывают эти идентификаторы для фич и параметров фич, со временем, когда аналитик разрабатывает механику эксперимента.

Допустим, здесь есть фича Переводчик, translator, которая включает, собственно, Переводчик. Также это позволяет разбить фичу на части. Таким образом, мы можем сделать две группы экспериментов, в обеих из которых Переводчик включен, но при этом мы сравниваем, какое значение лучше: 300 миллисекунд или 600. А есть фича TranslateServiceAPITimeout, она включает дополнительную функциональность, которая может задать кастомный timeout к API Переводчика.

И сравнивать. То есть можно разбить фичу на подфичи и потом часть подфич включить в эксперименте в разных группах. А можно конфигурировать одну и ту же фичу разными параметрами за счет новых сущностей параметра фичи (FeatureParam).

Но, нет, за нами пришли. Казалось бы, мы решили все проблемы, победа, можно праздновать. Пришло за нами тестирование. Пришли за нами не другие разработчики, не аналитики, не менеджеры. В чем заключалась проблема тестирования?

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

Значение фич можно посмотреть косвенно, то есть мы видим «Переводчик», значит, наверное, фича включена. Во-вторых, нельзя посмотреть значение Feature&FeatureParam. А вот значение FeatureParam посмотреть тяжело, потому что как мы определим, каким был таймаут к API Переводчика — 300 или 600 миллисекунд?

О ней я говорил в первом пункте. И третья проблема. Даже если в файл с конфигурациями добавлены конфигурации с фильтром для внутренней сборки и для public beta, все равно этот файл нужно выкатить в продакшен на сервер. Чтобы тестировщику что-то изменить, ему нужно выкатить измененный файл с конфигурациями эксперимента в продакшене. А выкатка в продакшен обладает определенными требованиями, определенным емким процессом.

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

Я сначала перечислю пункты с мерами, которые мы приняли, а потом расскажу подробнее о каждом из пунктов. Как мы решили эту проблему?

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

Третье: мы разработали систему вспомогательных cheat-урлов, дополняющих систему экспериментов снаружи. Второе: мы на служебной странице browser://version поддержали еще один параметр — show-variations-cmd. И четвертое: мы начали разрабатывать диагностический экран.

Новый сервис для выкладки файла с конфигурациями заключался в том, что мы скачиваем файл с конфигурациями экспериментов в продакшене. Давайте про первый пункт. Может быть, с упрощенными фильтрами. Далее тестировщик его декодирует из proto-файла обратно в текстовый файл и после этого дополняет конфигурацию какой-то своей конструкцией, новым study-блоком, который ему нужен. Он видит этот файл с конфигурациями экспериментов, и он точно знает, что в эту конфигурацию будет попадать приложение. И там задает значения Feature&FeatureParam, которые ему требуются для тестирования. Там ровно одна группа, значит, Feature&FeatureParam будут равны заданным.

И сервис обратно компилирует его в proto-файл и выдает ссылку. Затем он этот текстовый файл выкладывает на новый сервис. Для внутренних сборок существуют диагностические экраны, ниже один из них. Эту ссылку он указывает на диагностическом экране. Он указывает, что нужно использовать его ссылку, а не дефолтную.

Как посмотреть значение Feature&FeatureParam? Второе. Chromium генерирует дополнительный блок на странице browser://version, если указать параметр show-variations-cmd. Здесь мы поддержали механизмы Chromium, которые генерируют служебную страницу.

Но если разобраться, он имеет смысл. Этот блок состоит из трех ключей: enabled-features, force-fieldtrials и force-fieldtrials-params, который с первого взгляда похож на абракадабру. Это означает содержимое всей системы экспериментов, как будто мы запустили наш браузер из командной строки. Что это означает? Feature2 активируется экспериментом с именем trial2. Здесь написано, что Feature1 активируется экспериментом с именем trial1. Feature3 активируется просто так.

У эксперимента trial2 задана активная группа group2. У эксперимента trial1 задана активная группа group1. А если trial2 попадает в группу group2, то нужно выставить параметр p3 равный v3, p4 равный v4. И последний force-fieldtrials-params, параметр означает, что если эксперимент имени trial1 попадает в группу group1, то в него нужно выставить параметры p1 равный v1, p2 равный v2.

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

Наш эксперимент Переводчик ключом --force-fieldtrials=translator/enabled_new/ устанавливает группу enabled_new для эксперимента translator. Давайте попробуем на примере.

Далее идет ключ --force-fieldtrial-params==translator.enablew_new:timeout/5000, если вдруг эксперимент translator попадает в группу enabled_new, а мы видим, что в первом ключе он в нее попадает, то для эксперимента translator, для группы enabled_new нужно выставить значение параметра timeout, равное 5 000 миллисекунд.

То есть, умея читать такой формат, мы можем все содержимое системы экспериментов посмотреть, какая группа выбрана, какие в этой группе фичи были включены и какие параметры были заданы. И последний флаг --enabled-features=TranslateServiceAPITimeout<translator говорит, что если вдруг какая-то группа в эксперименте translator в эксперименте translator активна, то включите, пожалуйста, фичу TranslateServiceAPITimeout.

Это, конечно, прекрасно, что мы можем взятьь файл с конфигурациями экспериметов, декодировать его, дополнить его определенной группой, выложить на сервер и задать эту ссылку. Также мы разработали систему вспомогательных урлов (cheat urls). И на первых порах мы с этим жили. Но это достаточно долго. Но после определенного момента мы поддержали ссылку.

То есть у нее было четыре части. Мы разработали ссылку с кастомной схемой yandexbrowser:// (Яндекс.Браузер), которая имела следующий формат, который представлен на слайде. Вторая часть, это параметр enable-features=, где перечисляется список фич который нужно включить, потом параметр disable-features=, где перечисляется список фич которые нужно выключить. Первая часть это my_pet_experiment=group_name. И дальше идут просто параметры, которые через &.

Эта служебная информация будет передаваться каждый старт, помимо, собственно, скачанного файла с конфигурациями экспериментов. Если вы запустите такую вспомогательную ссылку (cheat url), то система экспериментов дополнится служебной информацией. При этом она не будет содержать блок filter, поэтому эксперимент my_pet_experiment гарантированно будет попадать в группу, которую вы зададите в ссылке. И она будет генерировать конфигурацию эксперимента, из информации указанной в ссылке по шаблону представленному на слайде, которая будет в файле в самом начале. Вероятность этой группы будет 1000, у нее будет feature_association, которую вы задали в ссылке, а также заданные вами параметры.

Тестировщик берет ссылку, формирует ее, запускает в приложении. Таким образом, тестирование облегчилось. Неважно, скачала ли система экспериментов файл и какой файл она скачала, потому что этот блок study идет выше всего. И он точно уверен, что теперь именно у эксперимента с нужным именем — в данном случае my_pet_experiment — точно будут заданные значения, такие, какие он хочет.

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

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

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

Генерация монетки должна быть одинаковой от запуска к запуску. Но хранить монетку на диске может показаться избыточно и опасно. Также необходимо равномерно распределять пользователей по группам, что вообще отсекает варианты. Казалось бы, если вы используете генератор случайных чисел, как это обеспечить? Как же не хранить монетку, обеспечив все эти три критерия?

Используется следующий прием. Это возможно.

Ссылка со слайда

Генерируется случайная строка, так называемый источник энтропии. Каждое приложение в браузере хранит, например, UUID, то есть уникальным способом генерируется application Identifier, сохраняется на диск. Эта случайная строка объединяется с именем эксперимента. После этого hash переводится в значение от нуля до единицы.

Будет ли это число случайным? Почему это работает? Будет ли оно различаться на одном и том же устройстве от эксперимента к эксперименту? Да, будет, потому что у вас есть случайно сгенерированный компонент в качестве UUID, то есть по всем устройствам это число будет случайное. А имя эксперимента различно для разных экспериментов. Да, будет, потому что вы объединяете источник энтропии с именем эксперимента. Если вы используете правильный hash и правильный перевод из уже сгенерированного hash в вещественное число от нуля до единицы, то будет. Будет ли оно равномерно распределено? Как этой делать, показано в статье от Google, которая называется An Efficient Low-Entropy Provider.

Такой же алгоритм использует, например, сама система Chromium. Таким образом, вы можете в своей системе экспериментов хранить на диске только одно значение — UUID, а генерировать монетку, которая будет равномерно распределена, уже согласно алгоритму.

Какие критерии к ней предъявляются? Какую систему экспериментов, по нашему мнению, стоит использовать? Она должна быть:

  1. Калькулируемой: не хранить на диске информацию о выбранных группах, значениях их параметров или еще что-то. Пересчет должен происходить при каждом запуске.
  2. Конфигурируемой. Вы разбиваете пользователей на группы, задаете условия, при которых ваш эксперимент может быть проведен, а система сравнивает эти конфигурации между собой.
  3. Тестируемой. У вас должны быть механизмы, которые позволяют переопределять значения групп, фич, параметров или других необходимых вам сущностей.
  4. Такой, где используются разные примитивы для аналитики и для программирования.
  5. Расширяемой. У вас должна быть возможность посмотреть, как она работает, и адаптировать ее к своим нуждам (см. Сhromium variation service).

Система Chromium, которую мы расширяем в Яндекс.Браузере для iOS, такими критериями обладает. Проводите ваши эксперименты, анализируйте их и делайте приложения лучше. Спасибо.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть