Перейти к содержимому

Что должна нести строка события и где люди застревают

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

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

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

Почему панель пересобрать можно, а строку — нет

Заголовок раздела «Почему панель пересобрать можно, а строку — нет»

Что вы сможете спросить потом, решает строка, а не панель, которая её читает. Панель, которая оказалась неверной, стоит одного вечера, а поле, которое никто не писал, потеряно за весь период, пока его не писали. Обратной дороги нет.

Поэтому форма строки — то единственное решение, на которое стоит потратить время, а в строке лежат семь вещей: кто, что, когда, откуда пришёл, сколько стоило, на чём сделал, чем кончилось.

create table events (
user_id bigint not null,
name text not null,
created_at timestamptz not null default now(),
source text,
cost numeric,
client text,
outcome text not null
);

user_id — стабильный внутренний id: сессии кончаются, а спрашивать вы будете про человека. name берётся из закрытого списка в коде. outcome хранит ok или названный класс ошибки, потому что булев флаг выбрасывает ту часть падения, с которой можно работать.

Что обязано записаться в момент вставки, потому что join не вернёт

Заголовок раздела «Что обязано записаться в момент вставки, потому что join не вернёт»

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

Заодно положите рядом с разобранными полями сырой payload. Когда-нибудь вы зададите вопрос, которого парсер не ждал, и сырой payload — это разница между «ответил» и «развёл руками».

Вот моя таблица. Она проваливает и стоимость, и источник. Замер 13 августа 2026-го: 1180 строк с 13 мая, колонок с cost или price ноль, колонок с источником ноль.

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

С источником хуже. Джойнить не к чему: его не записали, значит, его нет.

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

Строку пишите и на первый контакт, до всякого действия, иначе те, кто пришёл и сразу ушёл, не попадут ни в одну вашу таблицу. А судят ваш первый экран как раз они.

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

Какие события обязаны существовать, чтобы у провала было место

Заголовок раздела «Какие события обязаны существовать, чтобы у провала было место»

Каждому экрану между приходом и активацией дайте своё событие, потому что провалу нужно место, а не только размер. Вы знаете, что большинство уходит. Это бесполезно, пока вы не знаете, на какой экран они при этом смотрели.

Активация — единственное действие, после которого у человека есть построенное вами, и определяется она на странице какие числа смотреть еженедельно. Всё до неё — шаги, и на каждом шаге кто-то теряется.

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

Как читать падения, чтобы они говорили о продукте

Заголовок раздела «Как читать падения, чтобы они говорили о продукте»

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

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

У меня оно делится почти пополам. Из 76 строк error 44 — то, что сломалось: шлюз, слишком большой файл, перекодировщик. Остальные 32 — сообщение про кончившийся лимит. Это решение о тарифах в костюме ошибки, и упёрлись в него шесть человек.

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

Чинится это одним полем с коротким именем из закрытого списка, рядом с текстом, а не вместо него.

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

Читайте целые сессии от начала до конца, по несколько в неделю, пока объём это позволяет, потому что на этом масштабе один полный путь говорит больше, чем любое среднее за ту же неделю.

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

  • Собрать панель раньше, чем записаны вопросы. Я каждую неделю читал панели, которые ни на что не отвечают. Делал я это дольше, чем хочется записывать, и в итоге снёс большую часть таблиц событий и пересобрал всё вокруг расходов и исчерпания.
  • Вообще не завести колонку стоимости. Я думал, что оставил её пустой. Полез в таблицу ради этой страницы: колонки нет и не было за все 1180 строк. Каждую стоимость я восстановил задним числом из тарифов. На графике это неотличимо от замера.
  • Дать параметру источника приходить и не сохранять его. Он попадал в строку лога и там кончался, а ссылки при этом неделями выглядели размеченными. До строки не доезжало ничего.
  • Не записывать, какого вида приходит вход. Поле появилось на восемнадцатые сутки, и 219 строк до него останутся пустыми. С тех пор видно: залитые файлы спорят с наговорённым в чат, 256 против 341. Все написанные до этого экраны обращались к другому человеку.
  • Читать свой лог ошибок как эксплуатационный. Туда я заглядывал, когда что-то падало, и больше ни за чем. Сгруппированные по входу, а не по времени, те же самые строки оказались описанием краёв продукта.
  • Считать события вместо людей. Три действия одного человека читаются как три пользователя, и мой тестовый аккаунт читается как тяга ровно так же.
  • Не иметь строки на тех, кто не сделал ничего. Это самая большая группа и самый жёсткий сигнал. Ни одна таблица событий их не содержит, поэтому они ни в одном моём разборе и не всплывали как проблема.
  • Усреднять по всем вместо когорт. Среднее держалось ровным, пока регистрации росли, и я читал это как стабильность, а двигалась ли при этом хоть одна когорта, эта панель сказать не могла.
  • Сделать одно настоящее действие и прочитать его строку — кто, что, когда, источник, стоимость, клиент, итог, — потому что null в любом из полей окажется вопросом, на который вы потом не ответите.

  • Взять человека, который зарегистрировался и не активировался, и восстановить его путь по одним строкам, не открывая код.

    select created_at, name, outcome, client
    from events
    where user_id = $1
    order by created_at;

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

  • Посчитать людей, которые не сделали ни одного действия, потому что если такой запрос не пишется, первый контакт не записывается.

    select count(*)
    from users u
    where not exists (
    select 1 from events e where e.user_id = u.id
    );
  • Взять падения за прошлую неделю и сгруппировать по классу входа: если все они попадают в один класс с именем error, лог ещё не размечен.

  • Разложить одно число из недельного списка по источнику и по клиенту. Не раскладывается — значит, этих полей в строке нет.

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

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

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

Предложить правку · Страница не помогла