Через неделю после июньской цифры Бран пишет не в рабочий чат, а Ладе лично. Над письмом ответ Кима двухнедельной давности: «падение из-за релиза 2.0». Студия две недели искала, что сломалось в релизе, и спорила, не откатить ли его целиком.
Это первый клиент «Северного Тракта» и первый кейс в портфолио, по которому «Монетка» выбрала агентство. Вечером в офисе остаётесь вдвоём с Кимом. Он долго смотрит в свой старый запрос и говорит, что взял его из папки ушедшего старшего: сумма по дням, без чистки и без поясов.
Пятый босс курса и первый, где вопрос звучит точно так, как в жизни: «упало, почему». Это самый частый ad-hoc в продуктовой аналитике. Новых конструкций не будет, будет всё сразу: чистка из блока C, пояса из блока E, воронки и монетизация из блока G и метод из урока про ad-hoc. У Брана три версии, у Кима четвёртая. Проверим все.
Пятый босс курса и первый, где вопрос звучит точно так, как в жизни: «упало, почему». Это самый частый ad-hoc в продуктовой аналитике. Новых конструкций не будет, будет всё сразу: чистка из блока C, пояса из блока E, воронки и монетизация из блока G и метод из урока про ad-hoc.
Фазы прежние. Разведка: подтвердить падение на чистых данных и отсечь артефакты. Бой: разложить выручку на множители и найти, где просело. Трофей: записка Брану.
Фаза 1. Разведка
Сравниваем равные окна
«Упала вдвое» это сравнение чего с чем? Месяц до и месяц после релиза дадут разное число дней, а последние дни июня ещё не набрали поздних покупок. Берём два окна по 28 дней, то есть ровно по четыре недели с одинаковым набором будних и выходных:
до 2.0: с 16 апреля по 13 мая включительно;после 2.0: с 14 мая по 10 июня включительно.
Релиз вышел 14 мая в 00:00 UTC. Чистая покупка та же, что у первого босса: оплачена, один раз на receipt_id, игрок не тестировщик.
WITH clean AS (
SELECT *
FROM purchases
WHERE status = 'paid'
AND id IN (SELECT min(id) FROM purchases GROUP BY receipt_id)
AND player_id NOT IN (SELECT id FROM players WHERE email LIKE '%@studio.dev')
)
SELECT CASE WHEN created_at < '2026-05-14 00:00:00+00' THEN 'до 2.0' ELSE 'после 2.0' END AS period,
count(*) AS purchases,
sum(amount_rub) AS revenue,
round(sum(amount_rub) / 28, 2) AS revenue_per_day
FROM clean
WHERE created_at >= '2026-04-16 00:00:00+00' AND created_at < '2026-06-11 00:00:00+00'
GROUP BY 1;
period | purchases | revenue | revenue_per_day
-----------+-----------+-----------+-----------------
до 2.0 | 289 | 189547.00 | 6769.54
после 2.0 | 153 | 106604.00 | 3807.29
Падение подтверждается: выручка в день упала с 6770 до 3807 рублей, на 44%. «Вдвое» это почти правда.
Мусор не объясняет падение
Если в одном окне мусора много, а в другом мало, падение может оказаться артефактом. Проверяем: в сырых строках «до 2.0» 198 320 рублей, в чистых 189 547; «после 2.0» 115 464 против 106 604. Эхо вебхуков, возвраты и тестировщики есть в обоих окнах, по 3-4 повтора и 5-7 возвратов. Сырые суммы завышены на 4,6% и 8,3%, на картину это не влияет.
Пояса: день студии московский
created_at хранится в UTC, а дашборд Брана считает дни по Москве. Покупки идут вечером, и московский вечер это ещё прошлый день по UTC. 12 мая по UTC дало 2737 рублей, а по Москве 6025: больше половины выручки дня уехало бы в соседнюю дату. Когда сверяешься с дашбордом заказчика, дни считай так:
(created_at AT TIME ZONE 'Europe/Moscow')::date
Для сравнения окон по 28 дней это почти неважно: сдвиг на три часа касается только краёв. А вот для фразы «в день релиза было вот столько» важно очень.
урок из курса
Урок из курса «Аналитика данных с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.
Оплата пока не подключена: премиум и курсы открываются промокодом.