Modelling a Streak feature 🔥 how would you approach this challenge?
A streak feature has the potential to form habits for your users. Our requirements for this feature made it challenging to design and implement a readable solution.
I work at the health tech startup Myworkout. Just moments before our summer hackathon project selection, our CMO Erling had an intriguing idea;
"What if we could create a streak feature, similar to Snapchat's?"
Three employees embarked on this idea, crafting it into a concept that could be demoed post-hackathon.
Their work was exceptional, resonating with the team, and we decided to integrate it into our product. Sometimes, simplicity is more complex than it seems...
The concept
Not familiar with streaks? Let's delve into it to understand their role in motivating users to form habits.
What are Streaks?
Streaks are a hot feature for some Snapchat users rewarding consecutive days of communication with 🔥 Don't snap back one day? Your 🔥 is gone.
For Duolingo, streaks are a core part of their feedback loop, and it's no accident.
As the research shows, if you repeat an action often enough in the same context, the act of doing it will start to feel automatic. With a steady habit, something as daunting as studying a new language can feel as natural to your daily routine as brushing your teeth.
For more details, check out Duolingo's research on habit-building behind the streak.
At Myworkout, our primary goal is to help users establish healthy exercise habits for a longer, healthier life. This is achieved by boosting your cardiovascular capacity (geek speak for "strengthening your heart"). Our core concept revolves around High-Intensity Training (HIT) for at least 32 minutes a week – that's the weekly goal. We wanted to recognize users consistently achieving this goal. When you meet your goal for consecutive weeks, your HIT Streak count goes up.
Doing Great, but What If You Slip?
One crucial aspect of maintaining a habit is keeping users motivated even if they fall short. Duolingo offers Streak freezes, and Snapchat lets you buy a restore for a recently broken streak
It's a known fact that once you form a habit of exercise, it's easier to stay in, and once you break it, it can often feel harder to get back into the habit again. In terms of health, catching up in the following week still has significant benefits. While many fitness apps can demotivate you when you miss your plan/goal, our approach is different. We aim to motivate you by allowing you to repair your HIT streak – not by buying anything but by exercising extra to make up for what you missed in the previous week.
Missed 5 HIT minutes last week? Repair it by surpassing this week's goal by an extra 5 HIT minutes or more.
Onto the requirements
Enough about streak theory; let's explore the feature requirements (which, of course, expanded over time – hello feature creep!). Here's a breakdown.
Terms and abbreviations
HIT | High Intensity Training. When linked to a number it describes the amount of minutes performed with High Intensity Training. |
HIT Goal | The minimum number of HIT the user has set to reach in a given week. |
Streak | A period of time during which something continues to happen |
Streak repair | A concept where the user did not achieve their HIT Goal for the previous week, but is able to repair and continue the streak by achieving an extra amount of HIT (equal to the missing number of HITs from the previous week). |
Streak count | The number of weeks in a row the user has achieved their HIT Goal, including "repaired" weeks. |
Design
A picture speaks volumes, so let's start with the design of the feature, offering you a better understanding of what we aimed to create. The images depict different streak states:
- The first screen shows an alert when you reach a new streak count.
- The second screen displays the streak count on the home screen, linked to weekly HIT minutes.
- The third screen represents the state when you broke the streak in the previous week and can repair it.
- The fourth screen showcases an alert as you enter the next week, having failed to repair the streak.
The 4th screen has two variants dependent on if you reached last weeks goal or not. In the example shown, you failed to repair the streak, but started a new one instead.
Functional Requirements
- When a user meets their HIT goal in a week, it should increase the streak count.
- If a user falls short of the HIT goal while on a streak, it should be possible to repair it the following week by exceeding the lacking HITs lacking to meet the failed week's goal.
- Able to edit the number of weekly HITs retroactively, potentially affecting and updating streaks.
- Be able to retrieve a user's streaks for rendering on a timeline, such as a calendar, without taxing code performance. The state of each week can be either "fulfilled", "repaired" or "broken".
- Be able to retrieve a user's current streak progress in order to show the corresponding user interface from the design requirements. The state should contain;
- the user's current streak count
- how many minutes remains to meet this week's goal
- How many minutes remains to meet this week's goal and repair the previous week if it was not fulfilled and part of an existing streak (and thus repairable).
- Whether the week from two weeks ago was repairable but the user was not able to repair it ("Failed to repair!" alert)
- Be able to retrieve the user's list of previous streak counts without taxing code performance.
- Be able to retrieve a user's HIT count and goal for any given week.
- Send a push notification when a user achieves a streak increase.
- Send a push notification when the user is about to loose a streak or fail to repair it.
- Database support for PostgreSQL 15.x.
Streak chains / week nodes
The following graph shows some of the transitions that can happen to an existing streak (merge streaks or split streaks). From the initial state it can go to the two next states, and it can also go from both of the last states to the "initial state" (reverse).
Feel Like Taking a Shot?
If you're up for the challenge, I've prepared a small git repository with feature tests, Notifications and Use Cases written in PHP/Laravel. You're free to use and share it as you see fit under the MIT license.
If you do decide to share it with the world, I'd appreciate if you left a comment to this article with a link. I'm really curious on how other people would approach this problem, and I'm sure there's better ways to do it than how we ended up implementing it.
How We Tackled It
A single developer created a working concept of the API in a week, but we went through 3-4 iterations of refactoring and mob programming before we started to be happy about the readability of the code and covering all the cases of the requirements. In total it took 3-4 weeks before we had it running in production.
The main challenges making this Streak solution hard to implement compared to other streak solutions was in particular these requirements:
- it should be possible to change the streak by editing/creating workouts in the past.
- it should be possible to repair a previous broken week based on exercising the "missing" HIT from the next week.
- Timezones; we didn't really addressed this one, but it won't be straightforward. In order to map a date to the correct week, we would probably need to know the timezone the user had at a given time when retroactively editing an old workout. We don't expect a lot of our users to change timezones though.
"Timezones are like a box of chocolates. You never know what you're going to get."
There was some other gotchas on just representing the week number of the year (our WeekOfYear value object) that needed some iterations before it covered all edge cases too.
I'm considering writing another post that explains our solution or sharing a branch in the example repo with an implementation similar to ours. I don't think by any means that we have the best possible solution, but it works quite well so we plan to hold off any larger refactor on this feature until we see that our users love this feature as much as some of us do internally. There's always room for fresh perspectives and potentially even more refined solutions than the one we've presented.
If you're interested in the follow-up post or you have some suggestions, I'd appreciate it if you left a comment or subscribed to the site.