A push engine that stays relevant as users change, and fresh as the months pass.
Drops’ notifications were generic, mistimed and ignored. Push drove just 5.8% of sessions, and almost a third of iOS users had opted out. I rebuilt them as a lifecycle engine: the right message for each stage of a user’s journey, with the copy rotating every 30 days so it never goes stale.
- user reactivation
- +24%
- session completion
- +23%
- purchase conversion
- +22%
- ARPU
- +20%
Notifications so generic that turning them off improved retention.
Drops’ push was the same for everyone: one generic set, sent at all hours, often pointing nowhere useful in the app. It told users they had “5 minutes left” on sessions they’d never started and warned about streaks that weren’t at risk. In review feedback the top asks were control over what arrived (60%), fewer notifications (28%) and messages that made sense in context (24%).
The data was worse than the complaints. Push drove only 5.8% of session starts. Open rates ran from 2% to 27% depending on the type, and nearly a third of iOS users had opted out. The clearest signal: users who disabled notifications on day one went on to retain at double the rate of those who left them on. A primary channel to our users was actively pushing them away.
Map every moment that deserves a message. Then keep the copy fresh on rotation.
I started from the user’s lifecycle. Every meaningful moment became one of 18 scenarios: a streak about to break, a review session overdue, a challenge ending, a long streak worth celebrating. Each scenario was then tuned to five lifecycle stages, from brand-new to disengaged, because a line that motivates an engaged learner lands very differently on someone drifting away. That gives 85 distinct ways to speak to a user, and each person sits in exactly one of them at a time. Only one message goes out a day, prioritised by where they are and what matters most.
Relevance makes a message worth opening once. Freshness keeps it worth opening for months. So the copy lives in two full sets that swap every 30 days, on a timeline personal to each user, so after a month away from a line it reads as new again. Duolingo proved the effect at scale, where fresh copy outperformed even their best-optimised messages. Writing it was the real work: 1,305 notifications drafted, peer-reviewed and translated. Iterable replaced the legacy platform underneath, giving us the per-message tracking the old setup never had.
- STEP.01 Lifecycle map Turned every meaningful moment in the Drops journey into 18 push scenarios, each tied to a clear in-app action.
- STEP.02 Segment the journey Split users into 5 lifecycle stages (new, engaged, at-risk, low-engagement, disengaged) for 85 scenario-segment combinations.
- STEP.03 Write for freshness 1,305 notifications written, peer-reviewed and translated. Two copy sets that rotate every 30 days so nothing feels repetitive.
- STEP.04 Build on Iterable Migrated off the legacy platform to Iterable: per-message analytics, segmentation and journeys the old setup couldn’t support.
- 01 New First 7 days Educate, encourage first steps
- 02 Engaged Active yesterday Acknowledge effort, motivate more
- 03 At-risk Inactive 1 day, active in the prior 6 Don’t let inactivity become a habit
- 04 Low engagement Inactive 7–30 days Begin to hold accountable
- 05 Disengaged Inactive 30+ days Firmer in tone, still friendly
- Turn off Then silence After a stretch of silence, one honest note that we’ll pause notifications until they come back.
| # | Scenario | What it does | Fires when |
|---|---|---|---|
| 01 | Session reminders Reminder | Nudge daily practice and build the habit. | No other scenario applies |
| 02 | Streak rescued yesterday Streak | Tell them a Rescue saved their streak; keep it going. | Rescue used yesterday, Rescues left |
| 03 | Streak endangered Streak | Warn the streak breaks without practice today. | Active streak, no Rescues left |
| 04 | Last chance to save streak Streak | Create urgency in the final window to save it. | No Rescues, no session by 9pm |
| 05 | Guided learning · Dojo Learning | Recommend a review session in the Dojo. | No review in 3 sessions, terms waiting |
| 06 | Guided learning · Quiz Mode Learning | Point to waiting Quiz Mode questions to review. | No review in 3 sessions, 7+ questions |
| 07 | Language Voyage · re-engage Feature | Bring a lapsed user back into their voyage. | Not used in last 10 sessions |
| 08 | Language Voyage · next unit Feature | Remind them they can choose the next unit. | Idle 5 sessions, next-unit step live |
| 09 | Language Voyage · checkpoint Feature | Prompt a checkpoint to test what they’ve learned. | Idle 5 sessions, checkpoint step live |
| 10 | Challenge reminder Feature | Remind an inactive user of their live challenge. | In a challenge, no session in 3 days |
| 11 | Challenge ending soon Feature | Motivate a strong finish before time runs out. | In a challenge, ends in 3 days |
| 12 | Starting a streak Streak | Encourage a fledgling streak to keep building. | Streak of 2–5 |
| 13 | Established streak Streak | Use the effort invested so far to motivate. | Streak of 6–24 |
| 14 | Strong streakers Streak | Celebrate consistency; push to keep going. | Streak of 25–59 |
| 15 | Big streakers Streak | Honour a mega streak and dare them higher. | Streak of 60+ |
| 16 | Recently lost a streak Streak | Reassure that consistency, not perfection, wins. | Lost a streak in the last 7 days |
| 17 | Pushing the elite Elite | Challenge the most active learners to go beyond. | Well above average words per month |
| 18 | Stop sending comms Lifecycle | Say we’ll pause push, so they can opt back in. | No practice for 34 days |
Activation, engagement and revenue all moved together.
- reactivation
- +24%
- of win-backs
- 30%
- streak open rate
- 3–4×
- ARPU
- +20%
The lifts compounded down the funnel. Reactivation rose 24% and early session completion 23%, with the biggest jump between a user’s first and second session, exactly where activation is won. That carried through to money: purchase conversion up 22% and ARPU up 20%, with the revamped audience generating comparable revenue from fewer people.
The freshness-and-relevance bet paid off most where it mattered. Streak notifications, which were personal, urgent and tied to something users had earned, opened at three to four times the rate of generic sends. And 30% of the users the engine won back had been dormant for more than 90 days: the rotating copy reached people the old one-size-fits-all push had long since lost.
The highest-volume messages were the least personal, and it showed.
Streak notifications opened at three to four times the rate of session reminders and learning nudges, and the difference was personalisation: the streak copy spoke to something specific a user had built, while the reminders sat closer to one-size-fits-all. Those reminders were also our two highest-volume sends. The clear next move was to pull the same personal signals into them, the last word a user learned or the topic they were part-way through, so the messages doing the most work also felt the most personal.
Relevance earns the open. Freshness earns the next one.
The obvious fix for weak notifications is to write better copy. The bigger lever turned out to be structural: match the message to where someone is in their journey, then refuse to let any message overstay its welcome. Relevance is what makes a notification worth opening today. Freshness is what keeps it worth opening next month. Do both and the channel keeps re-earning attention instead of slowly burning it, which is the only way something as intrusive as push stays worth having.