Implementation worksheet · 5 min read
A Streak Freeze and Recovery Product Policy
Write four rules before shipping a streak: the grace window (how late an action still counts for yesterday — typically a few hours past local midnight), the freeze allowance (how many missed days a user can absorb, how they are earned, and whether they apply automatically), the recovery path (what happens after a break — reset to zero, restore on return within N days, or keep a longest-streak record alongside the current one), and the timezone rule (streaks are evaluated in the user's local day, fixed at their profile timezone, not the server's). Streaks exist to reward returning, so a policy that punishes a single missed day in a way the user experiences as unfair converts your most engaged cohort into churn.
A streak is the one mechanic that creates loss aversion deliberately, which means the break is a designed moment rather than an edge case. Teams design the accumulation carefully and leave the break to whatever the code happens to do.
Put it into practice
1. Set the grace window in local time
An action at 00:30 local counts for the previous day if your grace window is three hours. Without one, timezone travellers and night owls lose streaks they behaviourally earned — the most annoying possible way to lose one.
2. Decide how freezes are earned and spent
Earned by consistency (one per seven-day run), granted on a plan, or bought. Then decide whether they apply automatically on a miss or must be activated in advance — automatic is kinder and harder to reason about; explicit is clearer and less forgiving.
3. Design the break, not just the streak
Three options: hard reset, restore-on-return within N days, or current-plus-longest. Whichever you choose, show the longest streak somewhere permanent — it preserves the sense of accomplishment that a reset otherwise erases entirely.
4. Fix the timezone at the profile, not the request
Evaluate in the user's stored timezone. Deriving it per request means a trip abroad silently shifts what 'today' means and can break a streak mid-flight, which reads as a bug because it is one.
5. Test the boundaries explicitly
Action at 23:59 and at 00:01 local, a day skipped with and without a freeze available, a timezone change mid-streak, and a DST transition. Four tests; each one is a real user in production.
Streak policy
Copy this structure into your review document and record your observed result for each row.
| Rule | Options | Chosen | Why |
|---|---|---|---|
| Grace window | 0 / 3h / 6h past local midnight | ||
| Freeze allowance | none / earned / granted / purchased | ||
| Freeze application | automatic / explicit | ||
| After a break | reset / restore within N days / keep longest | ||
| Timezone source | profile / request / account | profile | travel must not break a streak |
A failure worth checking
The unfair-break churn: a user with a 60-day streak crosses a timezone, the server evaluates in UTC, and the streak resets while they are asleep. They were not less engaged; they flew somewhere. The support ticket reads as a bug report and the behaviour change reads as churn, and both are downstream of a policy that never considered local time.
Common questions
Should streaks reset at all?
Something has to distinguish a current streak from a historical one, or the number stops meaning anything. Keeping both — current and longest — preserves the signal without making a single miss feel like deletion.
Do freezes undermine the mechanic?
Sparingly, no: a limited freeze earned through consistency keeps the incentive while removing the cruelty of one bad day. Unlimited freezes remove the mechanic entirely, which is its own answer.
Basis and scope
This is a proposed implementation method using illustrative examples, not a measured benchmark or a customer case study. Prepared with AI assistance. Validate product-specific behavior against current documentation and your own test environment.