Кейс: разметка приложение с нуля на конкретном примере
Привет! На связи Сергей Матросов и команда X5 Tech, ответственная за аналитику в «Пятёрочке». Хотим поделиться с вами тем, как мы внедрили трекер AppMetrica от Яндекса и сделали с помощью него разметку для приложения «Пятёрочки». Почему мы остановились именно на этом фреймворке, какую мы выбрали архитектуру разметки, как писали правила и словари, а также разберём процесс разметки на живом примере разметки экрана приложения. Очень надеемся, что эта статья поможет вам сэкономить много времени, если перед вами будет стоять аналогичная задача.
Выбор AppMetrica как целевого решения
Прежде, чем мы перейдём к процессу разметки, расскажем, почему мы выбрали именно AppMetrica.
Сейчас на рынке есть достаточное количество трекеров, среди которых мы выделили следующие:
GA4 –, но из-за угрозы санкций внедрить его и одним днём потерять — это очень больно.
Matomo — можно развернуть внутри компании на своих серверах, но, к сожалению, из-за MySQL он не потянул бы нагрузки, при этом слабо развит для наших нужд SDK. К тому же, у него неудобный формат разметки.
UXRocket — есть ограничения по количеству параметров для событий. 30 параметров — это немало, но не так уж и много, риск упереться в них есть. Также нас не устроил формат событий. При этом цена в год сопоставима с ценой на AppMetrica.
Ещё для нас было важно, чтобы AppMetrica закрывала следующие функции:
Поддержка отправки событий, которые пользователь совершил офлайн: речь тут не о покупке непосредственно в магазине, а ситуации, когда при использовании приложения у пользователя пропадает интернет. Возможность сохранять продолжительное время события, которые юзер делает без интернета, важно для консистентности данных о взаимодействии с приложением.
Обогащение Яндекс.Директ событиями: логика тут такая же, как и с Google Ads — свои родные события Яндекс любит больше, чем из прочих трекеров по мобильной атрибуции.
Стоит отметить и выделенный канал техподдержки и сопровождения по согласованию, которые мы получили от Яндекса на этапе внедрения и развёртывания AppMetrica: все наши вопросы были оперативно решены.
Конечно, стоит также сказать, что мы рассматривали вариант написать свой трекер. Однако, хоть это и звучит круто, но это дорого, долго, с вероятностью постоянных проблем по трекингу и с сомнительной рентабельностью. Ключевое тут всё же — долго. Результаты нам нужны были быстро.
Что такое разметка
Теперь давайте непосредственно перейдём к разметке.
Итак, разметка приложения — это описание возможных действий пользователя в приложении/веб-сайте, которые те могут отслеживать с последующей отправкой этих данных в трекер, который группирует эти события по-юзерно (иногда и нет), по-сессионно.
Основная цель разметки — отслеживать, как взаимодействуют пользователи с приложением для последующего анализа этих взаимодействий с выводом метрик, поиска узких мест, возможности тестировать изменения на продукте и пр.
Если вы делаете разметку с нуля, тогда на ваши плечи ложится выбор алфавита разметки, фреймворка разметки (то есть способа размечать), составление правил и словарей для событий, их параметров и пр. Об этом и будем говорить, но сначала отметим вот что: на наш взгляд, хороша та разметка, которая в рамках сырых данных, логов — то есть на уровне строк — даёт вам понимание, что именно за событие произошло, где, на что было совершено воздействие и т. д.
Алфавит разметки
Кажется очевидным, что это латиница, но та же AppMetrica переваривает и кириллицу, а значит выбор как будто есть. Основное обоснование латиницы — это не только минимизация проблем с обработкой полей в таком формате, но и возможность перенести разметку на другой трекер, который, исключая AppMetrica, с большой долей вероятности будет иностранной разработки, а значит будет рассчитан именно на латиницу.
Итак, определились — латиница.
Фреймворк разметки
Часто встречаемый способ
На практике это способ разметить данные по модели «Объект_Действие» или «объектДействие».
По-английски: Object_Action.
Сразу пример в двух стилях:
button_click
buttonClick
Определимся с понятиями:
Действие (Action) — то, что совершается пользователем. Например, «клик», «просмотр», «скролл». Именно это — базовое содержание события, которое отправляется в систему трекинга.
Объект (Object) — каждая сущность в приложении/на веб-сайте — это объект. У объекта есть конкретные параметры. Объектов может быть много, и чтобы ориентироваться среди них, нам надо их группировать по определённому типу, то есть вводить классы. Например, объект «кнопка». «Кнопка» — это класс.
Скажем, у нас есть кнопка, мы можем её нажать. Итого:
Всё вместе «кнопка_нажатие».
А так как мы оперируем латиницей, тогда и получается, что:
Ранее мы сказали, что основа события — это Действие. Потому что без действия не было бы факта события как такового. Недаром говорят об action-based аналитике. Поэтому сначала Действие, а потом Объект. Таким образом, схема «Объект_Действие» меняется на «Действие_Объект».
Более того, Действие и будет event«ом, то есть событием как таковым, а Объект — первым из его параметров.
Параметры | |
Cобытие (Действие) | Объект |
click | button |
Может возникнуть вопрос: почему бы сразу всё в событие не записать, например, clickButton? Уверен, найдутся те, кому так привычнее. То есть было бы так:
Параметры | |
Cобытие (Действие) | Объект |
clickButton |
Здесь надо подсветить вопрос об ограничении уникальности имён на события для… GA4 (для бесплатной версии на текущий момент это 500 уникальный имён на поток (из приложения), для платной — 2000). Дело в том, что хорошим тоном является учёт переезда разметки на другой трекер, а для GA4, например, на текущий момент есть ограничение на количество уникальных имён событий.
У нас есть мнение, что основная причина в том, что GA4 из коробки бесплатный, а данные надо где-то хранить, и именно уникальность создаёт проблему в объёме хранимых данных. В теории его сложно достичь, но нельзя недооценивать мощь усердия людей в преодолении пределов! А потому clickButton, clickBanner уже заняли бы два уникальных места, а просто click с параметром object (который принимает значение button или banner) — одно. Другое дело, что и уникальность имён параметров не резиновое (об этом далее).
Впрочем, если вы решили остановиться на AppMetrica, то ограничений ни по названиям, ни по параметрам у неё нет (точнее, их достичь практически невозможно, даже если будете стараться). Более того, она поддерживает и вложенность параметров до 10 штук.
Итого, самый распространённый тип разметки — это Действие_Объет (например, нажатие кнопки click_button). Но в нашем случае этого было недостаточно, поэтому мы пошли дальше.
Расширенный фреймворк
Мы решили обогатить предыдущий фреймворк ещё двумя важными обязательными параметрами — Субъект и Пространство.
«Субъект_Пространство_Действие_Объект»
или по-английски:
«Subject_Space_Action_Object»
Начнём с новых понятий:
Субъект — источник действия. Это может быть, скажем, пользователь или система, то есть user, system. Например, пользователь кликает, система возвращает ошибку. Если вы часто работаете с UML-схемами, то можете мыслить вместо Субъекта — Актора. Мы находим, что это синонимы, но нам ближе понятие Субъект, так как оно как-то более персонализировано что ли.
Пример: пользователь кликнул где-то на кнопку — user_пространство_click_button
Пространство — дело в том, что у нас различные части приложения поддерживаются различными командами разработки, поэтому нам нужно ещё одно уточнение. Отсюда пространство — это команда, отвечающая за конкретный участок приложения, который размечается. Мы находим полезным знать, чьи события мы анализируем, к кому идти, если что-то будет не так.
Дополняя наш пример: »user_delivery_click_button» — то есть юзер нажал на кнопку в рамках той части приложения, за которую отвечает команда Delivery.
Тут хочется сказать, что будет очень здорово, если названия ваших команд содержательны и несут смысл того, чем они занимаются.
Параметры
Cобытие (Действие)
Объект
Субъект
Пространство
click
button
user
delivery
А пример действия системы? Легко: например, показ экрана. Приложение/веб-сайт вам показывает экран после его открытия вами: system_cart_view_screen
Параметры
Cобытие (Действие)
Объект
Субъект
Пространство
view
screen
system
delivery
Как и в случае с Объектом, Субъект и Пространство будут параметрами события. Подчеркнём: обязательными параметрами. Всё вместе — это тот необходимый минимум для трекинга, который отвечает на четыре базовых вопроса:
Что произошло?
На что было воздействие?
Где (в ведомстве какой команды) произошло?
Кто это сделал?
Ещё одно дополнение
В процессе мы выделили ещё два обязательных параметра — Section и Screen. Section обозначает раздел, который содержит в себе некоторое количество экранов — Screen-s. Основная задача этих параметров проста: вместе они позволяют лучше понять, где именно в приложении было событие. Это как с адресом: регион и город (и да, иногда они совпадают, как Санкт-Петербург, Санкт-Петербург). Но при этом важно понимать: Section можно и не внедрять, но Screen обязателен в любом случае, иначе будет потеряна локация события.
На всякий случай подчеркнём разницу: Space — это команда разработки, Section — раздел, а Screen — один из экранов раздела.
Правила разметки
Тут всё просто: правила нужны для того, чтобы был единый стиль разметки, чтобы в неё не попадали персональные данные. Вот несколько примеров этих правил:
Не передавать персональные данные в события и его параметры.
Во всех наименованиях используется только нижний регистр. Слова разделяются нижним подчёркиванием (snake_case).
Используемый язык символов — латиница, при этом названия событий, параметров и значений пишутся на английском языке.
Для названий событий и параметров используйте готовые (согласованные) внутри команды аналитиков словари.
Если есть сомнения в том, что использовать — ещё раз загляните в словарь. Если нет ничего подходящего, обсудите и только после этого введите по необходимости новое название для события (действия)
и т. д.
Здесь сложно выделить что-то универсальное, кроме п.1. Правила вам нужно будет написать самим до разметки и посмотреть их внутри вашей команды аналитиков, поэтому поставим точку в этой теме.
Словари разметки
Очень важно одни и те же действия называть одним и тем же именем. А так как вы вряд ли будете размечать в одиночку (если так, то соболезнуем), а командой, то вы должны на что-то оглядываться при названии событий и параметров. Иначе у одного будет click, у другого — tap. Это не только множит зря сущности, но и путает. Унификация решает! Начнём с событий:
Имена событий
Индекс | Название события | Описание |
1 | view | показ (страницы, виджета, пр.) |
2 | tap | касание экрана телефона пальцем |
3 | double_tap | двойное касание по экрану телефону (в короткий промежуток времени) |
4 | stretch | на приложении: будто раздвигаешь пальцами что-то |
5 | pitch | на приложении: будто защипываешь пальцами что-то |
6 | click | клик |
7 | sign_up | регистрация |
8 | refresh | обновление страницы |
9 | update | обновление объекта (например, кнопки, виджета, баннера) |
10 | login | вход по логину-паролю |
11 | logout | разлогин |
12 | success | всплывающее окно-уведомление «Success» или аналогичное ему по смыслу |
13 | confirm | подтверждение (например, при нажатии «ок» на плашке cookie про их сбор) |
14 | reject | отказ (например, при нажатии «cancel» на плашке cookie про их сбор) |
15 | enter | ввод (например, данных ФИО, в навигационном поиске) |
16 | send | отправка (сообщений, обращений по форме на сайте) |
17 | choose | выбор из вариантов |
18 | activate | активация (например, при переходе на сайт при подтверждении почты) |
19 | copy | копирование, нажатие на кнопку копирования |
20 | add_to_cart | добавление в корзину |
21 | remove_from_cart | удаление из корзины |
22 | view_cart | просмотр корзины |
23 | purchase | покупка |
24 | filter_goods | фильтрация товаров |
25 | subscribe | подписка (на новости, на блог) |
26 | unsubscribe | отмена подписки |
27 | hide | сокрытие (клавиатуры) |
28 | open | открытие |
29 | swipe | свайп списка/блока |
30 | swipe_full | свайп списка/блока до конца |
31 | remove | удаление (например, карты оплаты) |
32 | add | добавление (например, карты оплаты) |
Плюс словаря ещё и в том, что индекс позволит вам сразу определить количество уникальных имён, держа в уме информацию об ограничениях. А также он поможет преобразовать словарь в структуру, отфильтровать по паттерну в сырых данных то, что от этих имён отклоняется в написании, найдя ошибку в отправке данных.
Имена параметров
Ограничения на количество уникальных имён на примере GA4 есть и у параметров (и не только: количество символов в названии, количество символов в значении (value). Спрашивается: и зачем тогда переезжать на GA4? Но учитывать эту возможность, повторимся, надо)… В отличии от имён, с ними всё гораздо хуже: для бесплатной версии всего 25 штук. Звучит вроде бы как будто много, но, на самом деле, их может не хватить: объясняется это, прежде всего, не столько нашим усердием, сколько желанием понимать сходу при чтении сырых данных, с каким событием мы имеем дело. А потому — чем больше мы обогащаем событие параметрами, тем лучше (как правило).
Итак, по нашему фреймворку у нас уже три обязательных параметра: object, subject, space.
Давайте рассмотрим параметр object и то, какие значения он может принимать навскидку:
button
screen
banner
widget
warning
пр.
При этом как мы, например, отличим одну кнопку от другой? Ответ: по названию этой кнопки. Первое, что мы могли бы сделать, это записать название через »_» в параметре object, например: object: button_T. Но это нарушает саму логику использования параметров, потому что расширяет их содержание. Тогда логичнее вынести это содержание в другой параметр, скажем, name.
Итого у нас появляется следующая развилка:
либо object_name;
либо класс_name: button_name, screen_name и т. д.;
либо даже просто name.
Просто name (п. 3) можно использовать, но нет однозначного референса к тому, чьё это name. Хотелось бы в самом названии параметра однозначно указывать принадлежность к определённому классу объектов, тем самым в рамках отсмотра параметров в сырых данных можно оставаться более сфокусированным, понимая, о каком объекте идёт речь.
Чтобы не упереться в лимиты, которых, как мы уже говорили, в AppMetrica нет, но которые есть в других трекерах, логичнее пойти через object_name (п. 1). И, в целом, это верное решение, но тут несколько теряется читаемость события в сырых данных. Поэтому мы решили пойти через логику «класс»_name, понимая, что тем самым мы увеличиваем количество параметров. То есть, гипотетически, при переезде нам надо будет это свернуть в object_name. Как поступить вам — решать вам же в рамках команды.
Значения параметров
Параметры могут принимать сколько угодно много значений без ограничений (вот тут как раз нет проблем в лимитах), но есть проблема понимания, например, что было объектом действия: один посмотрит и скажет, что button, другой — icon/widget и т. д. Поэтому надо иметь словарь значений с определениями как минимум (да и как максимум, на самом деле) для Объектов и Субъектов, иначе у одного аналитика свои кнопки на экранах будут размечены как button (object: button), а у другого — icon (object: icon).
Словарь объектов
Название объекта | Описание | комментарий |
bottom_sheet | «выезжающий» снизу компонент экрана | перекрывает часть контента, обычно занимает половину или треть экрана по высоте, закрывается свайпом вниз или нажатием за его пределами |
alert | aлерты | всплывающие уведомления, которые относятся к бизнес-процессу: например, статус доставки |
banner | баннер | инфо с рекламой |
widget | виджет | обобщённый элемент, который может менять свой визуал (изменяемый объект) |
story | история из stories блока | |
map | карта | актуально только для адреса доставки |
keyboard | клавиатура юзера | |
button | кнопка | |
nav_button | навигационное меню | оно у нас постоянное и неизменное |
search_bar | поиск (tabbar) | поиск |
product_search | поиск продуктов (tabbar) | поиск продуктов |
warning | предупреждение | например, предупреждение на стадии оплаты в доставке: чтобы списывать баллы, нужно добавить карту лояльности |
hypertext | текст, содержащий URL для перехода | на экране выделяется другим цветом |
text_field | текстовое поле | |
screen | экран |
Важно: не стоит бросаться в крайности и усердствовать с классами. У нас, например, в разделе Доставки есть поиск товаров. Да, мы могли бы нажатие (tap) на поиск обозначить как:
Параметры | |||
event | object | … | text_field_name |
tap | text_field | … | product_search |
Но поиск по товарам у нас, во-первых, в приложении представлен в единственном числе, а, во-вторых, является важной продуктовой фичей, поэтому можно сразу обозначить как:
Параметры | |||
event | object | … | … |
tap | product_search | … | … |
Также у нас в словаре есть ещё и search_bar — поисковая строка в рамках экрана FAQ. Почему в одном случае мы написали search_bar, а в другом — product_search? Для того, чтобы выделить отдельно поиск по товарам и поиск по FAQ, исключив связь на уровне означающих «search».
Словарь субъектов
Вы можете справедливо заметить, что system можно уточнить до конкретного микросервиса. Да, верно, но такое дробление на названия сложно поддерживать, хотя в потенциале это и помогло бы определить, в какой сервис идти, чтобы разбираться с проблемными событиями. Мы решили обобщить всё до system. Условно, если мы видим, что system нет там, где она должна быть, то начинаем раскапывать.
Подведём промежуточные итоги
У нас есть:
Он даёт нам таблицу/схему событий вида:
Параметры | |||||
Cобытие (Действие) | Объект | Субъект | Пространство | Секция | Экран |
view | screen | system | delivery | my_orders | order |
Правила и словари событий, параметров, объектов, субъектов, пространств (также отметим, что полезным будет в документации привести примеры заполнения).
А чтобы вам было проще, вот вам наш готовый свод правил разметки событий.
Пришло время размечать!
Разметка (Процесс)
Прежде чем брать самый первый экран в работу, мы рекомендуем сделать несколько важных предварительных шагов.
Узнать ФИО тех, кто отвечает за ту или иную часть приложения/веб-сайта. Как правило, речь идёт о менеджере продукта, продакте, Product Owner.
Рекомендуем оформить это в виде таблицы в отдельном разделе про разметку в инструменте, в котором вы ведёте документацию:
Команда | Space | Экраны | PO (ФИО) | Контакты (email, tg) | Комментарии |
Доставка | Delivery | Catalog, Cart, etc. | Профессионал Топовый | ПД | Что-то важное |
Распределить экраны приложения внутри команды аналитиков:
Команда | Space | Экраны | PO (ФИО) | Контакты (email, tg) | Комментарии | Аналитик (ФИО) |
Доставка | Delivery | Catalog, Cart, etc. | Профессионал Топовый | ПД | Что-то важное | Герой Масков |
После этого мы, наконец, приступаем к разметке:
Размечаем-размечаем
Ревью разметки со стороны менеджера (каждый аналитик идёт к тому Product Owner (далее — PO), чьи экраны он размечал)
Правки + новое ревью
ТЗ на внедрение
Приём проделанной работы
Profit!
Что конкретно делают аналитики
Остановимся на самом интересном — на пункте 3 — «Размечаем-размечаем» (как мы, аналитики, это делаем). Пока open-source инструмент разметки от СберЗдоровья не в релизе, о котором ребята рассказали на конференции Aha 2023, мы используем старые-добрые Google Spreadsheets. Вот наш шаблон.
В нём три вкладки:
«Экраны/Виджеты» — это просто описание всех экранов и виджетов этого спредшита (считай, локальный словарь объектов экрана).
«Экран» — это экран приложения, который подвергается разметке. Вместо экрана для веба можно написать «Страница». Обычно мы меняем название вкладки с «Экран» на «Э. {{Название экрана}}».
«Экран ТочкиВхода» — это названия баннеров/виджетов в рамках приложения, через которые можно попасть в этот экран (если есть).
Основная вкладка — это вкладка № 2, «Экран». Разберёмся в нём подробнее.
У нас есть в хеддере два поля с URL-ами: одна на Figma, другая на Confluence. В Figma — актуальные макеты приложения, Confluence — страница с уже внедрённой разметкой на базе этого спредшита.
Поля «Дата добавления…» и «Статус в Android/iOS» говорят сами за себя.
Далее идут «Приоритет внедрения», «Индекс», «Субъект (Subject)», «Пространство (Space)». Все, кроме Индекса, должны быть уже понятны.
C Индексом всё просто: вместо того, чтобы ссылаться на какие-то события в спредшите на строки (так как строки могут меняться), лучше ссылаться на индекс.
Следующие три поля — это «Сценарий», «Описание события» и «Событие». Событие — это уже наше известное Действие. Описание события — это формулировка того, что сделалось и с каким объектом. А сценарий — это что происходит с пользователем, с системой, что даёт событие как результат.
Как вы помните, у нас есть обязательные параметры для заполнения: это Object (string), Subject (string), Space (string). Но к этому списку добавляются ещё два:
Screen_name
Section_name
Так как все они обязательны, мы вынесли их отдельно:
Space — это команда (example), Section — раздел (settings), а Screen — один из экранов раздела (notification). Итак, в секции «Настройки» есть ряд экранов (screens). Один из screen — это «Уведомления». За секцию и этот экран отвечает команда «Example».
Теперь к нашей непосредственной задаче добавляется ещё одна — разметить какой-то экран. У вас может быть либо постановка задачи от PO на первые события, либо «размечай всё, а там уж приоритезируем». Во втором случае вы открываете экран и… описываете все взаимодействия с ним. Вот и весь секрет.
Давайте немного посмотрим разметку главного экрана приложения «Пятёрочки»:
Первое событие, которое можно описать — это показ «Экрана»:
Событие:
Параметры этого события:
Что показалось? — Screen (экран).
Какая команда отвечает? — Growth (название команды).
Кто сделал это действие? — экран показывает system«a (то есть наше приложение показало экран пользователю).
В какой части приложения? — в части главной страницы (main).
Какой это был экран? — главная страница (main).
Доп. параметры (необязательные):
referrer — предыдущий экран
load_status — статус загрузки — успешно или ошибка (с указанием какой)
load_time — время загрузки экрана
(был еще ряд параметров, но они не имеет значения для понимания).
Второе событие, которое можно описать — это tap (нажатие) на виджет со штрихкодом.
Это виджет карты лояльности. За него отвечает другая команда — команда Loyalty.
Событие:
Параметры этого события:
На что было нажатие? — на widget
Какая команда отвечает? — Loyalty (название команды)
Кто сделал это действие? — нажатие делает user
В какой части приложения? — в части главной страницы (main)
Какой это был экран? — главная страница (main)
И доп. параметры (необязательные):
А почему, кстати, Widget? Может, это banner? Может, вообще вид button«a?
Мы внутри команды и с PO Loyalty договорились, что это именно Widget на базе того, что он может менять свой вид в зависимости от наличия карты лояльности.
Таким образом, размечаем все элементы на главной странице и получаем таблицу с событиями (как в шаблоне), которую несём на согласование продакту — PO (п. 4). Затем правим замечания (п. 5), затем опять несём на согласование — и так по кругу, пока не договоримся.
После того, как мы с продактами пришли к единому мнению, из таблицы нужно будет сформировать ТЗ для разработки на внедрение. Нам нужно было из спредшита сделать вот такое ТЗ:
Естественно, делать руками это было бы слишком грустно, поэтому мы написали скрипт, который из пис-оф-шита (GoogleSpread Sheet) делает вот такую конфетку-ТЗ (см. скрин выше). Отметим, что в рамках ТЗ у нас на базе event_name + параметров собирается в отдельном поле имя события в CamelCase: это уже уникальный индекс для разработчиков, который помогает им ориентироваться в событиях, которые отправляет приложение/веб-сайт.
Послесловие
На этом наш экскурс в процесс разметки приложения закончен. Мы поговорили о том, почему мы остановились на AppMetrica, какую мы выбрали архитектуру разметки и разобрали процесс разметки на живом примере экрана приложения.
Надеемся, наш рассказ и материалы вам пригодятся, когда перед вами встанет вопрос о разметке вашего приложения. Данная методика не претендует на единственно верное решение, но у нас она сработала и поэтому имеет право на жизнь.
Соавторы и непосредственные участники разметки:
Денис Кучкильдин
Дмитрий Чернышев
Никита Сурков
Ранее мы сказали, что основа события — это Действие. Потому что без действия не было бы факта события как такового. Недаром говорят об action-based аналитике. Поэтому сначала Действие, а потом Объект. Таким образом, схема «Объект_Действие» меняется на «Действие_Объект».
Более того, Действие и будет event«ом, то есть событием как таковым, а Объект — первым из его параметров.