Система щоденних стріків: як не втратити користувача
Уявіть: користувач заходить у додаток 30 днів поспіль — звичка сформована, retention зростає на 30% у перші 90 днів. Але варто одному дню зникнути — серія обривається. Як цього уникнути? Система daily streaks — перевірений інструмент: у додатках, що використовують стріки, середня DAU збільшується на 10% вже через місяць. Ми реалізуємо таку систему з урахуванням часових поясів, конкурентних оновлень, заморозок та сповіщень. Наша команда має 5+ років досвіду, впровадила стріки у 20+ додатках, що обробляють до 10 мільйонів користувачів. Гарантуємо стабільність під навантаженням.
Головна технічна складність — час і часові пояси
Одне з перших питань — за яким часом рахувати день. Порівняємо підходи.
| Підхід | Переваги | Недоліки |
|---|---|---|
| Локальний час | Точність для користувача. Використовується в Duolingo | Складність при зміні поясу |
| UTC-північ | Простота реалізації | Несправедливість для деяких часових поясів |
| Ковзне вікно | Лояльність | Неінтуїтивно, втрачається сенс «дня» |
Локальний час — найкращий вибір: у 3 рази точніший для користувача, ніж UTC, і зберігає інтуїцію. При першому запуску визначаємо TimeZone.current і зберігаємо на сервер. 80% користувачів не помічають зміни поясу завдяки заморозкам.
Чому локальний час — найкращий вибір для стріків?
На практиці більшість успішних додатків (Duolingo, Headspace) використовують локальний час зі збереженням user_timezone. Це дає справедливе нарахування стріків у будь-якій точці світу. При зміні часового поясу (переліт) можливі артефакти, але їх нівелюють заморозки. Retention підвищується ще на 15% при своєчасних сповіщеннях.
Модель даних
Приклад моделі даних
user_streak: user_id UUID current_streak INT longest_streak INT last_activity DATE -- зберігати DATE, не TIMESTAMP updated_at TIMESTAMP last_activity — дата в часовому поясі користувача, не UTC timestamp. Це ключове. При перевірці стріку:
today = current_date_in_user_timezone(user.timezone) days_since = today - last_activity if days_since == 0: стрік активний, нічого не робимо if days_since == 1: стрік продовжується, current_streak += 1 if days_since > 1: стрік зламано, current_streak = 1 Атомарне оновлення через SQL з RETURNING — захист від конкурентних запитів.
Як реалізувати freeze та відновлення стріку?
Втрата стріку — болюча подія. Деякі додатки дають заморозки (streak freeze): користувач може пропустити день без втрати серії. Це збільшує retention при пропущених днях на 20%.
streak_freeze — окремий ресурс, який користувач отримує як нагороду або купує. При зламаному стріку перевіряємо: чи є активна заморозка на вчорашній день. Якщо так — не ламаємо стрік, списуємо заморозку.
Відновлення стріку (платна фіча) — технічно простіше, етично спірніше. Якщо реалізуємо: streak_restore_purchase, зберігаємо новий last_activity = yesterday, current_streak = pre_break_value. Середня конверсія на purchase freeze — 12%.
Сповіщення та візуалізація
Reminder перед північчю (наприклад, о 21:00 за локальним часом) — «Ви ще не виконали завдання сьогодні, стрік X днів під загрозою». Ефективність цих сповіщень висока (31% користувачів повертаються після отримання), але потрібна персоналізація часу: користувач, який завжди активний о 8 ранку, не повинен отримувати reminder о 21:00.
| Тип сповіщення | Час | Тригер | Приклад |
|---|---|---|---|
| Reminder | 21:00 за local | Користувач не відзначився сьогодні | «Ваш стрік із 7 днів під загрозою» |
| Milestone | Момент досягнення | current_streak = 7/30/100 | «Вітаємо! 30 днів!» |
| Freeze used | При активації | Пропуск дня з заморозкою | «Заморозка врятувала ваш стрік!» |
На iOS: UNUserNotificationCenter з UNCalendarNotificationTrigger. Час розраховуємо в часовому поясі користувача. При зміні активності оновлюємо trigger — якщо користувач уже відзначився сьогодні, скасовуємо сьогоднішній reminder. На Android: WorkManager з періодичним завданням.
Візуалізація: flame icon з числом — стандарт. На Flutter: AnimatedFlipCounter для плавного збільшення лічильника. Щотижнева сітка днів (як у GitHub contribution graph) — показує історію останніх 7/30 днів. Це потужно: порожні комірки візуально «кличуть» їх заповнити. 65% користувачів заповнюють хоча б одну порожню комірку в перший тиждень.
Milestone-сповіщення
7 днів, 30 днів, 100 днів — спеціальні події з анімацією. Інтегруємо з системою досягнень: milestone стріку = автоматично розблоковане досягнення. 90% користувачів, які досягли 7-денного streak, залишаються на третій місяць.
Що входить в роботу?
Ми надаємо повний набір: модель даних і бекенд-логіку (SQL, NoSQL — адаптуємо під ваш стек), інтеграцію з push-сповіщеннями (APNs для iOS, FCM для Android), UI-компоненти (flame counter, weekly grid, milestone animations), документацію API та схеми бази даних, повний вихідний код у вашому репозиторії, навчання команди (2 години онлайн) та підтримку протягом 1 місяця після здачі.
Орієнтири за термінами
Базова система зі стріком, сповіщеннями та milestone — 2 дні (клієнт) + 3 дні (бекенд). З заморозками, відновленням, персоналізованими reminders та інтеграцією з досягненнями — 1–2 тижні. Вартість розраховується індивідуально. Оцінимо ваш проект безкоштовно — пишіть. Duolingo та Headspace використовують аналогічний підхід.
Дізнайтеся більше про retention на Wikipedia.
Зв'яжіться з нами для консультації. Замовте реалізацію системи стріків вже сьогодні — гарантуємо якість та дотримання термінів.







