Привет, меня зовут Анна Саботович, я техлид в Альфа-Банке (команда платежей и переводов), отвечаю за развитие BDUI платформы на Android. Я покажу, как мы внедрили аналитику в контракт экранов на BDUI и как связали их с трекерами. Расскажу, с какими сложностями мы столкнулись по пути, какие ограничения предусмотрели и какие задачи положили в бэклог. Вы узнаете, как применяем BDUI в Альфа-Банке, и как собираем продуктовую аналитику, когда пользователь взаимодействует с экраном BDUI.  Начну с описания проблемы.  В мобильной разработке с релизами всегда была тяжело. Релизный цикл мобилки и время раскатки изменений на пользователей гораздо больше, чем на вебе или бэкенде. Кроме того, даже если мы выпустим новую версию приложения, это ещё не значит, что клиент захочет обновиться — он может годами сидеть на старой версии приложения, потому что не хочет ничего менять или боится багов в новой версии.  На графике мы видим, что 42% наших пользователей использует версии приложения, выпущенные в прошлом году. Это значит, что нам нужен какой-то механизм, который бы позволил не полагаться на релизы, а доставлять пользователям новые фичи в обход релизного цикла. И дальше я расскажу про этот механизм. Нам на помощь приходит BDUI. Что это такое? Это когда мобильное приложение не знает, что рисовать, и за контентом отправляется на бэкенд. Бэкенд отправляет конфиги для отрисовки в формате JSON, а фронт верстает экран используя эти конфиги. Это позволяет менять UI на фронте без релиза и максимально быстро поставлять изменения пользователям.  У нас в Альфе есть несколько фреймворков реализующих BDUI подход. Сегодня мы остановимся на одном из них — это многошаг.  Четыре года назад на вкладке платежей началось развитие BDUI фреймворка. Мы интегрировали платежный сервис, который может присылать набор различных полей для совершения платежа. Этот набор полей зависит от провайдера, он разный для ЖКУ Москвы и Ростелекома, а также зависит от выбора пользователя на каждом шаге.  Поля такого экрана мы назвали динамическими полями, а весь динамический сценарий — многошагом.  Количество шагов — от 1 до бесконечности. Многошаг также может иметь ветвления в логике и выбирать следующий шаг, в зависимости от ввода пользователя на предыдущем шаге. Например, если при поиске штрафа выбран поиск по данным водителя, то следующим шагом придет список полей для ввода данных СТС и водительского удостоверения. А если будет выбран поиск по номеру постановления, то на следующем экране появится поле ввода для номера постановления и картинка с подсказкой, где его искать. Сейчас многошаг вышел за пределы платежей и распространяется по всему Альфа Мобайлу. Например месяц назад зарелизилась фича — обновленный семейный банк, где можно заказать карту свои родным. Весь сценарий сделан на многошаге. На схеме вы видите полный сценарий со всеми развилками, который состоит из 26 уникальных шагов.  Примечание. Если вы захотите поподробнее узнать про наши BDUI технологии и их реализацию, то добро пожаловать в мою статью «Эволюция Server-Driven UI: динамические поля, хэндлеры и многошаг» Каждый переход между экранами, показ попапа или ошибки — это риск, что пользователь покинет сценарий и не дойдет до конца. Чтобы этого избежать и довести пользователя до цели, например, оплаты, нам нужно собирать метрики поведения пользователя, какой экран он открыл, какую кнопку нажал, какую ошибку увидел. Для этого мы и собираем продуктовую аналитику. Тут для нас и начались трудности.  Раньше единственными событиями аналитики в многошаге были вход в сценарий и успешное завершение оплаты. По этим данным мы могли построить только очень смутные гипотезы.  Вот график из суперсета, где мы видим количество уникальных клиентов, оплативших штраф. И ещё могли посмотреть конверсию оплаты штрафов для посетивших многошаг. Но в нашем сценарии оплаты штрафов ГИБДД много экранов и полей ввода. На каждом шаге пользователь может отвалиться из-за проблем соединения или ошибок валидации. И мы не узнаем почему. Кроме того отправка этих событий аналитики была захардкожена на стороне мобилки и любые изменения параметров требовали релиза мобильного приложения. Нам нужно было отвязать генерацию событий от релизного цикла фронта и поместить его на бэкенд, там же, где у нас хранится контекст экрана и поля имеют бизнес смысл. Первая идея, с который мы начали груммить — «А не сделать ли нам дешёво и сердито? Не отправить ли просто все значения динамических полей, как они есть, в трекер?» И это было бы действительно хорошим и быстрым решением для прототипа. Если бы мы не были банковским приложением. Ведь у нас хранятся самые ценные пользовательские данные — паспортные, адреса, номера карт и количество денег на счету. И мы поняли, что отправлять поля без всякой фильтрации в трекеры совершенно нельзя.  Значит нужно на каждое поле повесить какой-то признак можно его отправлять или нет? А если можно, то нужно ли его маскировать, например, если это номер карты или счета? И мы решили выбрать путь героя и сделать всё правильно.  В этом варианте, как и раньше, фронт запрашивает контент экрана у бэкенда. Но теперь бэкенд возвращает нам не просто список полей с правилами их отрисовки, но и каждому полю добавляет специфичную для него модель аналитики. Это, конечно, даёт увеличение трафика, нужно хранить конфиги на бэкенде и маппить на фронте.  Но, с другой стороны, мы получаем возможность гибко настраивать каждую модель аналитики и обогащать события любым бизнес-контекстом, доступным на бэкенде. Вот как это выглядит — у каждого динамо поля появляется модель. Здесь есть поля label, screen, category, которые необходимы для конструирования события аналитики, и массив параметров, в которых может передаваться дополнительный контекст. Дальше я вам расскажу, с какими проблемами мы сталкивались и какие вопросы задавали себе, чтобы разработать финальный контракт. Покажу, как можно расширять и обогащать BDUI дополнительной функциональностью.  Итак, вопрос, о котором мы задумались: «Что делать со сложными полями, с которыми пользователь может взаимодействовать по-разному?» Например, в поле ввода он может вводить информацию, может вставить целиком через буфер обмена или очистить всё, нажав крестик?  Или, например, поле выбора счета: на него можно нажать, откроется шторка со счетами, пользователь выбирает счет и этот выбранный счет заполняет поле. Если мы будем иметь только одно событие аналитики на каждое динамополе, то мы не сможем разделить пользовательское поведение: он первый раз нажал на поле выбора счета или уже выбрал конкретный счет? Какой тут выход?  Мы решили, что раз нам не хватает одного объекта аналитики, то мы заведём массив объектов! И каждый из них будет содержать конкретный тип события, чтобы мы могли выбрать правильный объект при наступлении события. И добавили eventTypes. И массив объектов аналитики. А что делать, если у нас не один трекер, а несколько? И ещё если одни из них внутренние, а другие внешние? И мы хотим приватные данные по счетам пользователей отправлять только во внутренние трекеры??  Тогда мы добавляем ещё один уровень фильтрации — вводим дополнительный параметр, который будет говорить, в какие трекеры мы можем отправить это событие аналитики. Чтобы разделить события по трекерам, мы добавили параметр analyticsSystems. До этого все параметры были предзаполнены и приходили с бэкенда. Как нам отправить данные, введенные пользователем в динамическом поле?  Нам же надо знать в какой параметр и дименшн положить введенные данные? Значит, номер дименшена должен прийти к нам с бэкенда, чтобы мы туда положили результаты пользовательского ввода. Мы добавили еще одну модель — это currentValue. Она дублирует модель параметра, но в ней отсутствует поле value, потому что оно заполняется при введении данных пользователем. Что делать, если произошло событие внешнее, относительно динамо полей, например нажатие кнопки сабмит, которая не является частью динамополей? Мы думали о том, не отправлять ли просто все события аналитики из массива динамополей при нажатии кнопки? Но опять же решили, что нужно больше гибкости, и добавили специальный eventType FORM_SUBMIT. Это значит, что у динамо поля может быть модель аналитики с eventType FORM_SUBMIT,и при нажатии на кнопку мы пробежимся по всем динамополям и соберем только те модели, у которых есть eventType FORM_SUBMIT. Таким образом мы даем гибкую настройку списка динамополей, которые хотим отправлять в аналитику. Последний вопрос — что делать с контекстом предыдущих шагов? Представим, что пользователь заполняет анкету на заказ карты для мамы. Он прошёл уже три шага анкеты, ввёл много разных данных и находится на четвертом. Нажимает на время доставки карты, а мы бы хотели в этот момент отправить ещё данные с прошлых шагов, например номер счёта и ФИО пользователя. Но ведь наше динамическое поле не знает ничего даже о данных на других полях этого же экрана,  не то что уж о других экранах! И тут конечно всегда есть наивная идея собирать все данные с формы или ещё лучше — передавать все модели аналитики с прошлых экранов. Но мы же уже выбрали путь, так что этот метод не для нас. Мы решили завести ещё одну сущность — это аналитика уровня экрана, которая бы хранила дополнительный контекст и текущего экрана и всех прошлых шагов. Весь этот контекст будет лежать в параметрах этой модели. Тогда получается, что при нажатии на какое-то динамополя, мы можем примерджить в его модели аналитики ещё и данные из модели аналитики уровня поля, чтобы обогатить динамополе дополнительным контекстом. Это уже получается почти что наследование!!     Итак, я рассказала про некоторые наши челленджи, давайте подытожим основные моменты. У каждого динамического поля появляется массив объектов аналитики. Конкретный объект мы выбираем по фильтру eventType.  Отправляем только в те трекеры, которые указаны внутри выбранной модели. На уровне экрана у нас тоже есть объект аналитики, внутри которого мы можем хранить дополнительный контекст прошлых шагов многошага. Основная часть позади, осталось всё это собрать в единый финальный контракт. На картинке вы видите экран многошага и сокращенный ответ от бэкенда.  Здесь видно два уровня аналитики — уровень экрана и уровень динамополя.  Ещё раз повторю: Аналитика уровня экрана содержит общий контекст экрана, например результаты выборов пользователей на предыдущих шагах многошага. Аналитика уровня поля содержит в себе контекст конкретного поля. Мы видим в параметрах название экрана, номер шага многошага, номер транзакции. Также может быть добавлена любая информация с прошлых шагов и есть другие поля, необходимые для формирования события аналитики: label, screen, category. Содержит данные, актуальные для поля. В отличие от экрана, динамополе содержит массив аналитик. Конкретная модель выбирается по произошедшему типу события, фильтруется по eventTypes. В этой модели также может быть набор дополнительных параметров. Это событие будет отправлено, только если произойдет клик на кнопку сабмит, потому что у поля есть только eventTypes FORM_SUBMIT.  Если захотим отправлять аналитику при выборе номера счета, нам нужно будет просто прислать с бэкенда ещё одну модель с eventTypes ON_CHANGE. Мы посмотрели на контракты моделей аналитики уровня экрана и уровня поля, и наверное у вас возник закономерный вопрос — «И что же со всем этим делать?»  У нас для поля и экрана используется одна и та же модель, то есть их данные могут дублироваться и пересекаться. Что делать при пересечении, у какого объекта будет приоритет? А что если на уровне поля и экрана у параметра будет одинаковый дименшен, но разные значения? Сейчас я расскажу, как мы это делаем. Мердж при клике на поле. Мердж при клике на экран. Итак, мы научили многошаг отправлять события аналитики при разных взаимодействиях с пользователем, при переходах на следующие экраны и при возникновении ошибок валидации. Мы разработали гибкую аналитику, которая настраивается на миддле и приходит на фронт вместе с динамическими полями.  Теперь мы можем узнать, что пользователь выбирал на каждом шаге и какие проблемы у него возникали при переходе на следующий шаг. Мы можем посмотреть конверсию по шагам, строить воронки и видеть проблемные места. Например, на графике ниже мы видим, что две трети пользователей отваливаются на самом первом шаге, когда мы предлагаем им выбрать, как искать штраф: по данным водителя или по номеру постановления.  А еще теперь мы можем посмотреть, какие ошибки валидации полей ввода чаще всего случаются с пользователями. Например, конкретно для оплаты штрафов, самая частая ситуация, что штраф просто не найден для указанного документа. А следующая по популярности ошибка, это то, что пользователь не ввел все обязательные данные. На основе всех этих данных мы можем строить гипотезы, что мешает нашим пользователям, и как нам улучшить доступность наших фичей. Например более явно подсвечивать обязательные поля. Мы провели тестирование BD Аналитики на штрафах ГИБДД и теперь эта функциональность доступна для любого сценарий на многошаге. И без необходимости релиза мобилки, нужно только добавить модели аналитики на стороне бэкенда! В этой статье я привела только один из примеров, что backend driven может быть не только интерфейс, но и аналитика.  Встречались ли у вас подобные проблемы и как вы решили задачу аналитики? А может у вас есть идеи, как можно решить задачу аналитики другим способом?  Мы уже вынесли настройку UI и аналитики на сервер, как думаете, что еще можно вынести, чтобы сделать код еще более гибким? Рекомендованные статьи: Эволюция Server-Driven UI: динамические поля, хэндлеры и многошаг Как катить фичи без релизов. Часть 1: про виджеты Как катить фичи без релизов. Часть 2: про низкоуровневый Server Driven UI Про IT рекрутмент и людей Разбираюсь в мок-серверах и пишу свой 8 типичных нетипичных советов Android-разработчику перед интервью COROUTINES-23: code review fixes 100 дней из жизни новичка: как устроен онбординг в мобильной разработке Как снимать логи с устройств на Android и iOS: разбираемся с инструментами Про IT рекрутмент и людей Багатон как в первый раз Как оптимизировать процесс привлечения клиентов B2B с помощью методов Продвинутой Аналитики Подборка материалов для Android-разработчиков Также подписывайтесь на Телеграм-канал Alfa Digital — там мы постим новости, опросы, видео с митапов, краткие выжимки из статей, иногда шутим.  Андроид тех лид