Перейти до змісту
Увійти

Теорія

План трекінгу для нової фічі

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

~9 хв читання · 10 вправ · 250 балів

Перед початком

Сплануй дані до запуску

У продуктовій команді Halves за три тижні запускають нагадування про оплату: Halves підштовхуватиме тих, хто винен гроші, розрахуватися. Після запуску Maya потрібні дві відповіді: чи розраховуються люди швидше завдяки нагадуванням і чи не дратують вони когось? План даних вона хоче отримати від тебе ще до запуску.

Цей урок завершує модуль планом трекінгу: від запитань до подій і властивостей, місця, де кожна записується, визначень метрик і перевірок перед запуском.

План трекінгу починається з запитань

План трекінгу — документ, який каже, що продукт записує і навіщо. Типова помилка — почати з екранів і кнопок («записувати кожне натискання на екрані нагадувань») і отримати сотні подій, якими ніхто не користується, і жодної, що відповідає на справжнє запитання.

Будуй його згори вниз, у п'ять кроків:

  1. Запитання. Запиши, що команда має дізнатися після запуску. Для нагадувань про борги: чи доходять вони й чи їх відкривають, чи закривають борги після нагадування і як швидко, чи швидше це, ніж без нагадування, і чи вимикають їх люди?
  2. Події та властивості, потрібні кожному запитанню.
  3. Джерело: де записується кожна подія, у застосунку чи на сервері, і що зберігається на користувачі.
  4. Визначення метрик із цих подій, записані так само, як DAU минулого уроку.
  5. Перевірка до запуску: виклич кожну подію на тестовому акаунті й прочитай сирі рядки.
Хмаринка репліки вгорі розгалужується донизу на маленькі чеки та бирки, які закінчуються планшетом із галочками.
Спершу питання, потім події, що на них відповідають, а тоді перевірки.

Порядок важливий, бо кожен крок залежить від попереднього. Подія, якої не потребує жодне запитання, — це шум, який однаково треба підтримувати правильним у кожному релізі.

  • Так
    Початок: «Чи закривають борги швидше після нагадувань?» → потім вирішуємо, які події на це відповідають.
  • Не так
    Початок: «Записуймо кожну кнопку на екрані нагадувань, запитання знайдемо потім».

    Подій багато, а зв'язку між нагадуванням і розрахунком однаково немає.

Подія на запитання, записана там, де стається

Кожному запитанню в плані потрібна подія, яка на нього відповідає, записана тією частиною системи, що бачить, як це стається.

  • «Скільки нагадувань ми намагалися доставити?» reminder_sent, записує сервер, бо лише він знає про кожну спробу, включно з пушами, що не дійшли.
  • «На скільки натиснули люди?» reminder_opened, записує застосунок, коли людина відкриває нагадування.
  • «Які борги закрили після нагадування?» settle_up_recorded з reminder_id, коли розрахунок починається з нагадування.
  • «Чи дратує це людей?» reminders_muted зі scope (ця група чи всі групи).

reminder_id — частина, про яку забувають. Він пов'язує надсилання, відкриття й розрахунок. Без нього розрахунок після нагадування виглядає як будь-який інший, і на «як швидко після нагадування?» не відповісти.

Один reminder_id на подіях надсилання, відкриття й розрахунку вимірює час до розрахунку; holdout-група дає чесне порівняння, а вимкнення показують роздратування.

Для роздратування немає кнопки, але воно видно в поведінці: вимкнення і люди, які перестали відкривати нагадування. Плануй події для поведінки, яку очікуєш, а не для почуттів.

Рука вимикає дзвіночок на телефоні, а сусіди по квартирі на задньому плані роздратовано дивляться у свої телефони.
Про роздратування ніхто не повідомляє, але вимкнення нагадувань — це дія, яку можна записати.
  • Так
    reminder_sent (сервер, reminder_id) → reminder_opened (застосунок, reminder_id) → settle_up_recorded (reminder_id)
  • Не так
    reminder_opened і settle_up_recorded без спільного id.

    Буде видно, що нагадування відкривали і борги сплачували, але не те, що одне спричинило інше.

Те, що зробили з користувачем, — не активність

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

reminder_sent записується, коли сервер надсилає пуш. Людина нічого не робила; пуш може й не дійти, бо сповіщення вимкнені або застосунок видалено. Якщо запит «активних користувачів» рахує будь-яку подію, кожна кампанія нагадувань тихо піднімає графік активних, і більша кампанія виглядає як здоровіший продукт.

Тож план робить дві речі. Він визначає активність через цільову дію, яку робить людина, як-от app_opened чи reminder_opened. І лишає reminder_sent з позначкою «серверна» та виключеною з метрик активності.

Звичка, яка робить це автоматичним: поруч із кожною новою подією запиши, хто її спричиняє — людина чи система. Одна така колонка запобігає цілому класу роздутих чисел.

  • Так
    Рядок плану: reminder_sent · спричиняє: система · виключено з метрик активності
  • Не так
    «WAU зріс на 8% того тижня, коли ми запустили нагадування».

    Спершу перевір, чи не рахує запит reminder_sent як активність.

Властивості для завтрашніх порівнянь

Кожне порівняння, яке знадобиться потім, потребує властивості на події вже зараз. До подій минулого місяця властивість не додаси.

Запитання Maya на перший день: «чи другі нагадування вимикають частіше за перші?» Щоб відповісти, дані мають сказати дві речі:

  • яке нагадування яке: reminder_number на reminder_sent (1 для першого нагадування про борг, 2 для другого);
  • після якого нагадування сталося вимкнення: reminder_id на reminders_muted, останнє нагадування, яке людина отримала перед вимкненням.

Маючи обидві, групуєш вимкнення за номером нагадування, після якого вони сталися. Без них відомо лише, що хтось колись вимкнув.

Практичний спосіб знайти відсутні властивості: для кожного запитання запиши графік, який малюватимеш («частка вимкнень: перше й друге нагадування»), і перевір, що кожен розріз і фільтр на ньому існує як властивість. Властивості, яким не потрібен жоден запланований графік, як-от версія застосунку на серверній події, можуть почекати.

  • Так
    reminder_sent {reminder_id, reminder_number}; reminders_muted {scope, reminder_id}
  • Не так
    Запустити reminders_muted лише зі scope і розраховувати визначити номер нагадування потім.

    Потім у даних ніщо не пов'язує вимкнення з нагадуванням.

Записуй значення в момент події

Деякі подробиці існують і як стан людини, і як факт про один момент, і план має зберігати їх там, де їх потребують запитання.

Візьмімо суму боргу. Властивість користувача на кшталт current_debt_usd каже, скільки людина винна сьогодні. Це корисно для екрана балансу, але марно для запитання Theo: поки він подивиться, чимало боргів після нагадувань уже сплачено, і поле показує 0. Щоб порівняти частку вимкнень для малих і великих боргів, потрібна сума така, якою вона була в момент нагадування: властивість події debt_amount_usd на reminder_sent. Вона зберігається разом із подією, і її можна групувати через місяці.

Та сама логіка стосується вимкнення. Вимкнення — подія (reminders_muted), а «нагадування зараз увімкнені» — властивість користувача (reminders_enabled). Потрібні обидві: подія для «скільки людей вимкнули цього тижня?», властивість для «кому ще можна нагадувати?»

Правило: якщо запитання про те, що було правдою, коли щось сталося, зберігай це на цій події.

  • Так
    reminder_sent {debt_amount_usd = 48, reminder_number = 2}
  • Не так
    Приєднати сьогоднішній current_debt_usd до нагадувань минулого місяця.

    Сплачені борги показують 0, тож великі борги здаються рідкісними.

Як записати частку: хто в базі

Частка має чисельник і знаменник, і визначення має зафіксувати обидва, в одній одиниці.

Частка вимкнень для нагадувань: з-поміж унікальних користувачів, які отримали нагадування цього тижня, частка тих, хто вимкнув їх протягом 7 днів.

  • Одиниця — люди, а не події. Хто п'ять разів вимикав і вмикав нагадування, — це одна людина, яка вимкнула. Якщо рахувати події reminders_muted, кілька неспокійних користувачів роздують частку.
  • Чисельник усередині бази. У чисельнику лише отримувачі. Люди, які вимкнули нагадування в налаштуваннях, не отримавши їх того тижня, можуть бути важливі для іншого запитання, але це не реакція на нагадування цього тижня.
  • Вікно. «Протягом 7 днів» означає, що нагадування, надіслане вчора, ще не завершене; чекай, поки його вікно закриється.

Приклад: 240 отримувачів, 15 із них вимкнули (27 подій вимкнення), ще 2 не-отримувачі теж вимкнули. Частка вимкнень = 15 / 240 = 6,25%.

Ще одне запитання плану, «швидше, ніж без нагадування», потребує holdout-групи: випадкової частки боргів, які мали б отримати нагадування, але не отримують. Заплануй її до запуску, інакше жодне порівняння не буде чесним.

  • Так
    Частка вимкнень = 15 отримувачів, які вимкнули / 240 отримувачів = 6,25%
  • Не так
    Частка вимкнень = 27 подій вимкнення / 240 отримувачів = 11,25%

    Змішує одиниці: зверху події, знизу люди.

Рахуй спроби там, де вони стаються

Частка чесна рівно настільки, наскільки чесний її знаменник. Для частки відкриттів знаменник — кожне нагадування, яке Halves намагався доставити, і всі їх бачить лише одна частина системи: сервер.

У чернетці розробника застосунок записує reminder_shown, коли нагадування з'являється на телефоні, а частку відкриттів рахують як reminder_opened / reminder_shown. Виглядає розумно, але ховає кожне нагадування, що так і не дійшло до екрана: вимкнені сповіщення, видалений застосунок, розряджений чи офлайн-телефон. Ці нагадування зникають зі знаменника, тож частка відкриттів виглядає вищою, ніж є, а проблема охоплення, можливо найбільша, стає невидимою.

Виправлення — записувати спробу там, де вона стається, reminder_sent на сервері, а реакцію там, де стається вона, reminder_opened у застосунку. Тоді можна чесно показати обидва кроки: скільки нагадувань надіслано, скільки відкрито і яка між ними різниця.

Назва reminder_shown ніколи не була проблемою, вона вже в минулому часі. Проблема — у джерелі.

  • Так
    Частка відкриттів = reminder_opened (застосунок) / reminder_sent (сервер)
  • Не так
    Частка відкриттів = reminder_opened / reminder_shown (обидві із застосунку)

    Нагадування, що не дійшли до телефона, зникають із бази.

Перевірка — це про дані, а не лише про функцію

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

На тестовому акаунті виклич кожну подію з плану й прочитай сирі рядки. Чотири перевірки:

  1. Спрацьовує один раз. Одне нагадування — одна reminder_sent; повтори після тайм-ауту — класичне джерело дублів.
  2. Властивості заповнені. Жодного порожнього reminder_id чи reminder_number.
  3. Тестові акаунти позначені як внутрішній трафік, інакше натискання під час перевірки виглядатимуть як справжні відкриття.
  4. Часовий пояс збережено. Час записують в UTC; зберігай timezone користувача, щоб помітити нагадування, яке прийшло о 3-й ночі за місцевим часом.
Людина з лупою перевіряє стрічку чеків, що виходить із тестового телефона, і позначає один дублікат.
Перед запуском QA перевіряє самі рядки, а не лише фічу.

Коли перевірка знаходить проблему, виправляй її в джерелі й перевіряй знову. Обхідні шляхи на кшталт «ділити на 2» ламають інші числа або ховають наступний баг; рахувати різні reminder_id — страховка, а не заміна правильній події.

Як це складається докупи: запитання обирають події, кожну записують там, де вона стається; властивості готують завтрашні розрізи; визначення перетворюють події на числа; перевірка підтверджує рядки до запуску. Цей модуль відповів на запитання «що саме ми вимірюємо?»; наступний питає, на які числа рівнятися.

  • Так
    «Повтор записує reminder_sent двічі з одним reminder_id: баг заведено, повторна перевірка до запуску».
  • Не так
    «Нагадування прийшло мені на телефон, отже, трекінг у порядку».

    Перевіряє функцію, а не рядки, які вона записує.

Готові потренуватися?

Виконайте вправи уроку й отримайте 250 балів, коли пройдете його вперше.

Розпочати вправи