Привет, Хабр! Меня зовут Андрей Бирюков. Я являюсь независимым экспертом в области ИТ и ИБ, преподаю в учебных центрах и пишу книги.
Для любого аналитика одной из самых больших проблем являются некачественные исходные данные. Например, вы как-то утром открыли таблицу с сырыми событиями и увидели там такое:
event_name= “Click”event_name= “form_submit_2”event_name= “send”event_name= “Пользователь нажал кнопку” (кириллицей, да)
Вас можно поздравить, вы стали жертвой событийного бардака. Типичная история: разработчики просто «запилили трекинг, как просили». А ведь просили вы, скорее всего, что-то вроде: «Сделайте, чтобы видно было, кто что делает».
И что мы имеем в итоге – у нас есть воронка, но считать по ней нельзя. Потому что этапы размыты, события дублируются, а пользователь может скачать файл 50 раз и засчитать себе 50 лидов.
В этой статье мы представили гайд, посвященный тому, как спроектировать систему событий, которая будет работать как часы, начиная с первого касания и до закрытой сделки.
Шаг 1. Забудьте слово «Click»
Начнем с названий событий. Событие не должно называться по типу действия в интерфейсе. То есть название события «Click» — это плохой вариант. Вместо этого называем события по их бизнес‑смыслу.
Примеры боли и решений:
Вы говорите разработчику | Он делает | Аналитик потом |
«Отправь событие на клик по кнопке “Оставить заявку”» |
| Не поймет, это заявка, подписка или просто нажатие |
«Отследи отправку формы» |
| А какая форма? Карьеры? Скидки? Консультации? |
«Событие на успешную отправку» |
| Простите, что? Успех чего? Регистрации? Платежа? Скачивания? |
В общем, как в той «баянистой» картинке:

А теперь серьезно.
Правило. Схема именования: [объект]__[действие]_[результат]
Вот несколько примеров:
- lead__created_success — лид создался;
- payment__completed_initial — первая оплата прошла;
- file__downloaded_brochure — скачали брошюру.
Здесь в качестве разделителей выступает двойное подчеркивание, если событие с частями. И никакого CamelCase и no_spaces.
Для лучшего понимания давайте выполним небольшой тест-драйв. Откройте любой сырой лог событий и найдите три самых непонятных названия. Затем переименуйте их в своей голове по новой схеме.
Шаг 2. Нарисуйте воронку на салфетке перед тем, как писать техническое задание
Аналитик и разработчик часто говорят на разных языках. Аналитик — про «лиды, клиенты, жизненный цикл». Разработчик — про «DOM-дерево, асинхронные вызовы, колбэки». Ваша задача — переложить воронку на события до того, как разработчик начнет писать код.
Возьмите лист бумаги (да, да, реальную бумагу). Напишите сверху «Пришел на сайт». Снизу — «Купил». Между ними — этапы вашей воронки.

Например:
Пришел на сайт.
Посмотрел товар (страница карточки).
Добавил в корзину.
Начал оформление.
Оплатил.
Теперь для каждого этапа укажите одно единственное событие, которое означает переход на этот этап.
Этап | Событие |
Пришел на сайт |
|
Посмотрел товар |
|
Добавил в корзину |
|
Начал оформление |
|
Оплатил |
|
Важнейшее правило: один этап — одно ключевое событие. Не надо на этап «Оплатил» вешать click_pay_button, payment_processing_started, payment_success. Аналитик сам запросит детали, если надо. Ему нужна чистая воронка.
Что вы можете сделать прямо сейчас? Нарисуйте эту схему на стикере (да, тоже на бумажном) и повесьте на монитор разработчика. Пусть он видит её каждый день.
Шаг 3. Убейте 99% мусора на этапе отправки события
Самая частая жалоба аналитика: «В логах 70% — боты, роботы сканирования и наши же тестировщики, которые накликали по 300 заявок».
Здесь есть два простых фильтра, которые нужно резать на стороне клиента до отправки, а не в аналитике.
Фильтр 1. Режим разработчика
Событие не отправляется, если:
- localhost, 127.0.0.1, dev- или test- в домене.
- Пользователь залогинен как tester@company.com или любой адрес с доменом вашей компании (если это внутренний тест).
Фильтр 2. Боты по User-Agent
Перед отправкой события делаете простую проверку на JavaScript:
const botPattern = /bot|crawl|spider|scrape|scan|headless/i;
if (botPattern.test(navigator.userAgent)) {
return; // не отправляем событие
}
```И обязательно — фильтр на window.innerWidth > 0 (безобидный способ отсечь headless-браузеры, которые часто не инициализируют размер окна).
На этом шаге, прямо сейчас проверьте, в каком проценте ваших сырых событий есть userAgent со словами bot, crawl или headless. Если больше 5%, — вы потеряли кучу денег на обработку мусора.
Шаг 4. Проектируйте обязательные поля как у пистолета — с предохранителем
Запомните три простых правила: событие без идентификатора пользователя — мусор. Событие без метки времени — мусор. Событие без идентификатора сессии — полумусор.
Ваша задача сделать так, чтобы эти поля были обязательными на уровне сбора. В Google Tag Manager — это настройка «Require» для полей. В собственном SDK — это проверка на бэкенде перед записью в лог.
Минимальный обязательный набор для любого события:
- event_name (схема из шага 1);
- user_id (если есть авторизация) или anonymous_id (если нет);
- session_id (один и тот же для всей сессии);
- timestamp (ISO 8601, с часовым поясом);
- page_url (без UTM-меток, они отдельно).
Если разработчик говорит: «А можно без session_id, мы его через IP и User-Agent склеим», — не соглашайтесь. Склейка вероятностная, точность упадет. Генерируйте session_id на клиенте при первом событии и тащите его во все последующие.
Напишите однострочный документ «Обязательные поля для всех событий» и отправьте в чат с разработкой. Скажите: «Без этих полей ваши события уходят в черную дыру и не попадают в отчеты к гендиректору».
Шаг 5. Боритесь с «двойным счетом» через один флаг
Вспомним еще одну классическую проблему: пользователь нажал кнопку «Отправить» 5 раз, потому что форма долго грузилась. У вас в логах — 5 событий lead__created_success. В CRM — 1 лид. Получаем разрыв.
Решение: событие отправляется один раз на бизнес-результат, а не на действие в интерфейсе.
Как это выглядит в коде (псевдокод для понимания):
// Плохо
document.getElementById('submit').addEventListener('click', () => {
sendEvent('lead__created_success');
});
// Хорошо
form.addEventListener('submit', async (e) => {
e.preventDefault();
const response = await sendFormData();
if (response.status === 'success' && !alreadySent) {
sendEvent('lead__created_success', { lead_id: response.lead_id });
alreadySent = true;
}
});
```Здесь нашим спасением может стать флаг alreadySent, хранящийся в памяти сессии. Пользователь может тыкать кнопку 100 раз — событие уйдет один раз.
Прямо сейчас найдите в вашей системе самое часто дублирующееся событие (подсказка: это обычно form_submit). Проверьте, есть ли там защита от двойного счета. Если нет — это ваша первая точка для исправления.
Шаг 6. Сделайте «песочницу» для новых событий, а не правку в боевых
Разработчики любят делать плохую вещь - добавлять события прямо в боевой код. Потому что «быстро, надо срочно отчет завтра». Через месяц таких правок у вас 5 разных версий одного и того же события.
Здесь вам поможет следующее правило: все новые события сначала идут в тестовый контейнер (тестовый GTM-контейнер, dev-ветку SDK). Там они живут не менее 2 дней, за это время вы проверяете:
Имя события соответствует схеме.
Все обязательные поля есть.
Нет ложных срабатываний (например, событие
purchaseне уходит при добавлении в корзину).Только после этого событие передается в прод.
Прямо сейчас вы можете создать в Jira/Notion/Trello простой чек-лист для принятия нового события. Три пункта: «схема имени соблюдена?», «обязательные поля есть?», «протестировано на dev 3 дня?». Без этих галочек — не выпускать.
Шаг 7. Добавьте в события «контекст сделки», а не просто «действие»
Самая частая ошибка после всех предыдущих: событие есть, но оно не связано с воронкой продаж.
Пример:
Вы ловите событие
file__downloaded_pdf.Но не знаете, к какому продукту относится этот PDF.
И не знаете, был ли пользователь уже лидом.
И не знаете, какая рекламная кампания привела этого пользователя.
Итог: все, что вы знаете, это что кто-то что-то скачал, но продажи от этого не приблизились. Здесь нужен обязательный блок для событий, которые находятся в середине воронки (между первым касанием и сделкой):
{
"event_name": "file__downloaded_brochure",
"user_id": "12345",
"session_id": "sess_67890",
"context": {
"product_id": "PROD-42",
"campaign_source": "google_cpc_summer_sale",
"current_step": "consideration",
"lead_id": "LD-100500",
"is_known_lead": true
}
}Эти поля позволяют ответить на вопрос: «А люди, которые скачали брошюру по продукту X из кампании Y, купили потом или нет?» Без этого контекста вы никогда не построите нормальную атрибуцию.
Для начала возьмите три самых частых события в вашей системе (например, просмотр карточки, добавление в корзину, отправка контактной формы). Допишите к ним контекст из примера выше. Увидите, как много не хватает.
Итоговый чек-лист для внедрения (повесьте над рабочим столом)
Перед тем как сказать разработчику «давай добавим событие», пробегитесь по списку:
Имя — по схеме
объект__действие_результат? (да/нет)Один этап = одно событие? (нет второго события для той же бизнес-точки)
Обязательные поля —
user_id,session_id,timestampесть?Защита от дублей — флаг
alreadySentстоит?Отсев ботов — проверка User-Agent и размера окна есть?
Песочница — сначала на dev, потом в прод?
Контекст сделки — привязано к продукту, кампании, этапу воронки?
Если хотя бы один пункт — НЕТ, событие не принимается.
На этом наш гайд можно с чистой совестью завершить. Да, возможно, он получился и не идеальным, но с его помощью ваша воронка продаж начнет приносить прибыль.

Если в событийной аналитике хаос начинается с Click, send и form_submit_2, то в финансовых моделях — с разрозненных таблиц, ручных сверок и данных, которым никто до конца не доверяет.
На бесплатных уроках OTUS разберем, как перейти от AS IS-хаоса к TO BE-контролю: собрать единую автоматизированную финансовую модель, сократить ручные операции и убрать «человеческий фактор» из расчетов.
3 июня в 20:00. «От хаоса к контролю: как построить финансовую модель, которой можно верить». Записаться
Обсудим, как перестать тратить большую часть времени на сбор данных и сверки, а БДР и ДДС собирать автоматически.17 июня в 20:00. «Как убрать “человеческий фактор” из финансовых моделей: от расчёта NPV до сложных систем оплаты труда». Записаться
Поговорим о том, как построить единую автоматизированную модель, где покупка оборудования, найм специалиста и KPI сотрудников считаются без ошибок и ручных правок.
А если хотите выбрать другое занятие под свои задачи — загляните в календарь открытых уроков.
Больше материалов про IT, аналитику, разработку и обучение — на канале OTUS в MAX. Подписывайтесь, чтобы не пропускать новые статьи, разборы и анонсы открытых уроков.

















