Entry Point
A user completes a Duolingo lesson. The lesson is short — 3–5 minutes. They feel good about it. They see their streak counter increment: Day 1.
That moment of incrementing is not the product. It's the beginning of the loop.
Trigger → Action → Reward
Trigger: At 22:00 in the user's local timezone, if they haven't practiced that day, Duolingo sends a push notification: "Don't lose your streak! Practice before midnight."
This trigger is precise. It fires at the last moment of urgency — not at 9am when the day is long and the urgency is low, but at 10pm when the deadline is real and imminent.
Action: The user opens the app and completes one lesson. The minimum viable action to preserve the streak is small by design. You don't need to do 30 minutes. You need to do 3. The barrier to continuing the streak is kept as low as possible.
Reward: The streak counter increments. The UI celebrates: confetti animation, a sound effect, the number ticking up. The reward is immediate and unambiguous.
Loop Mechanics
The loop works because of loss aversion, not reward. Once the streak exists, breaking it feels like a loss. Behavioral economics research (Kahneman, Tversky) consistently shows that losses are felt roughly twice as strongly as equivalent gains.
A 47-day streak is not motivating because of what it represents. It's motivating because losing it would feel terrible.
Duolingo amplifies this with three mechanics:
Streak Freeze: You can buy a streak freeze in advance (using in-app currency) that automatically activates if you miss a day. This serves two purposes: it generates revenue (streak freezes are premium) and it reduces churn anxiety ("I can go on holiday without losing my streak").
Streak Repair: If you miss a day without a freeze, Duolingo lets you pay to repair the streak. Many users do. The psychology: they've invested 47 days. The cost of repair is small relative to the loss of the entire streak.
Streak Society: Users with streaks above certain thresholds (100 days, 365 days) get badges and social recognition. This creates aspirational milestones and social proof — visible in the leaderboard — that extend the loop.
Why It Compounds
Every day a user maintains their streak, the perceived cost of breaking it increases. On day 3, breaking a streak is low cost. On day 100, breaking a streak feels catastrophic. The longer the streak, the stronger the retention mechanic — the loop self-amplifies over time.
This is the ratchet effect: each successful loop iteration makes the next iteration more likely, and the cost of stopping higher.
How You Can Build This
The technical implementation is simpler than the psychology:
Database schema:
user_streaks (
user_id UUID,
current_streak INT DEFAULT 0,
longest_streak INT DEFAULT 0,
last_activity_date DATE,
streak_freeze_count INT DEFAULT 0,
updated_at TIMESTAMP
)
Daily cron job (runs at midnight UTC):
- —Find all users where
last_activity_date < today - —If
streak_freeze_count > 0: decrement freeze count, preserve streak - —Else: reset
current_streakto 0 - —Update
longest_streakifcurrent_streak > longest_streak
Notification trigger:
- —At 22:00 user local time, check if
last_activity_date < today - —If true, send push: "Don't lose your X-day streak"
- —Use a queue (Bull, Inngest) to handle timezone-aware delivery at scale
Streak shield logic:
- —On lesson completion: set
last_activity_date = today, incrementcurrent_streak - —On shield purchase: increment
streak_freeze_count - —Display remaining shields in the UI with clear visual affordance
Estimated complexity: 2–3 days for the core streak system. 1 additional day for notification delivery. 1 day for the shield/repair mechanics.
Tech stack suggestion: Next.js + Supabase + Inngest (for cron) + Expo Push Notifications (for mobile) or web push.
Written by
Ross
Founder & Strategy Lead, Greta Agency
Ross has spent 10+ years building growth engines for companies from seed to Series C.