Раздел 42 · Аналитика данных с нуля

Босс П5. Куда делась выручка после 2.0

middle~25 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Через неделю после июньской цифры Бран пишет не в рабочий чат, а Ладе лично. Над письмом ответ Кима двухнедельной давности: «падение из-за релиза 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 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.