Build series · Notifications
Notifications.
I studied how Uber, Duolingo, LinkedIn and Netflix build their notification services, then designed RepGarden’s from what they do. Here are the five rules the big apps share, the three steps every RepGarden notification goes through, and how I decide which notifications reach the lock screen.
By Ahmed Gadir8 min read
What the big apps do
A notification is the only time an app gets to speak first. Everything else in an app waits for someone to remember it exists.
RepGarden is the workout app I’m building for Benji, a personal trainer, and his clients. Benji sets his clients’ training on a website, and the clients log it on their phones, where a vegetable patch grows from the sets they do. None of the app is built yet, and the series page has the story so far.
Before designing RepGarden’s notifications, I studied how the companies that send the most notifications build theirs. Uber, LinkedIn, Duolingo, Netflix, Slack and Instagram have all written about it on their engineering blogs. The apps are very different, and between them their posts describe the same five rules.
- Every notification is saved before it is sent. Uber saves each marketing push in an inbox for that user, then plans when to send it. [1] Duolingo stores each learner’s reminders in a database, and a separate process sends the reminders that are due. [2]
- Urgent notifications and scheduled notifications travel on separate paths. LinkedIn sends messages between members on a fast path and its daily digest on a slow one. [3] Netflix sorts notifications by priority into separate queues, so a problem in one queue does not hold up the others. [4]
- Each notification is checked again just before it is sent. LinkedIn drops a notification if the member has already acted on it, or if the notification has gone out of date. [3]
- The list inside the app is the record, and the push is an extra. For each notification, LinkedIn decides between showing it in the app only, and showing it in the app and pushing it to the phone as well. [3]
- The number of notifications is guarded. At Duolingo, the team could improve the timing and wording of notifications freely, but could not send more notifications without strong justification and the CEO’s approval. [5]
The apps that personal trainers use with their clients today are much simpler. Most of those apps send one reminder a day at a fixed time. Of the sixteen coaching platforms I looked at, none says in its help pages that it skips the reminder once the client has logged the workout.
Three steps for every notification
RepGarden follows the same five rules on a much smaller scale: one trainer and his clients.
Two things can start a RepGarden notification:
- Something happens. For example, Benji replies to a set that a client filmed.
- A scheduled time arrives. For example, a client asked to be reminded at 18:30 on training days, and it is now 18:30 on a day when that client has a session.
Whichever way a notification starts, it then goes through the same three steps.
Step one: write it down. Before RepGarden sends anything, the server saves a row in the database for the notification. When Benji saves a reply, the row for its notification is saved at the same moment, so a reply never exists without one. Each row has a key that can exist only once: this reply, or this session on this date.
The server could skip the row and just send the push. Saving the row first does three jobs that sending alone cannot do:
- The row makes a double send impossible. If the timer runs twice, or the server tries a failed send again, the second attempt finds the row already there. A bug report from one open-source project describes what happens otherwise: the project sent the push first and marked the row afterwards, so a single slow send could start two runs at once and send two pushes. [6]
- The row survives a crash. If the server stops halfway through sending, the row is still waiting when the server comes back.
- The rows are the list behind the bell. The list of notifications that a client opens inside the app is read from the same rows.
Step two: check it. Just before sending, the server asks two questions about the notification. Is the notification still true? For a reminder, the session must still be planned and the client must not have started it. For a reply, the client must not have read it already. And has the client turned this kind of notification off in the app’s settings? If the notification is no longer true, or the client has turned that kind off, the server sends nothing and records the reason on the row. The first question is the rule I would put above every other: never notify someone about something they have already done.
Step three: send it. RepGarden’s phone app is built with Expo, a set of tools for building phone apps, and Expo runs a free service that passes each push on to Apple and Google. [7] Every push that RepGarden sends carries an expiry: two hours for a session reminder, three days for a reply. A push with no expiry can wait up to four weeks for a phone that is switched off, [7] so Monday’s reminder could arrive on Thursday.
The next diagram follows one reply from Benji through the three steps.
A push, or the bell?
Not every notification is important enough for the lock screen. RepGarden puts each kind of notification in one of two places. A push appears on the client’s lock screen and interrupts the client. A bell item waits in a list inside the app, behind a bell icon, until the client next opens the app. A notification gets a push when it is about now. Either a person is waiting on the client, as with a reply from Benji, or it is time for the client to do something, as with a session reminder. A notification that the client can read at any time, such as a change to next week’s plan, goes behind the bell.
| Notification | A push | Behind the bell |
|---|---|---|
| Your trainer replied on a filmed set | Yes, every reply | Yes |
| Your week is in | Yes, once for each week | Yes |
| Your plan was updated | No | Yes |
| Your trainer asked for a clip | No | Yes |
| Session reminder | Yes | No, the home screen already shows the session |
| Missed-session nudge, the morning after | Yes | No, the home screen shows the missed day |
| The weekly harvest, on Sunday evening | Yes | Yes |
The bell matters more than it looks. In one benchmark of 10,000 apps, 56% of iPhone users had notifications switched on, [8] which leaves nearly half of iPhone users who never see a push. For those clients, the bell is how they hear from their trainer.
Plan changes go behind the bell because of a complaint I found on the ideas forum of another coaching product. On that forum, a request for the product to stop telling clients about every small change to a plan had 295 votes. [9] In RepGarden, if Benji edits a plan five times in an evening, the client gets five quiet lines in the app and nothing on the lock screen.
What Expo does, and what I build
Expo asks the phone for permission, gives each phone an address that pushes are sent to, and delivers to iPhones and Android phones through one service. RepGarden’s server does everything else: it decides which client is told what and when, makes sure no notification is sent twice, and keeps the bell’s list and the switches in settings.
One detail in the server’s list of phones prevents a serious mistake. A phone keeps the same address whichever client signs in on it. If the server does not move the address when a second client signs in, the second client receives the first client’s notifications. RepGarden therefore ties each address to one account at a time.
More advanced features, left for later
The big apps do more than the three steps, and RepGarden’s first design did too. I cut these features from the first version, because none of them matters until the three steps work:
- Quiet hours hold a late reply until the morning. Phones already have Do Not Disturb, and each client chooses their own reminder time.
- A daily limit caps the automatic notifications. RepGarden’s automatic notifications rarely share a day anyway: reminders go out on training days, the missed-session nudge on free days, and the harvest on Sunday evening.
- Bundling sends several replies that arrive within a few minutes as one push.
- Easing off sends fewer reminders to a client who ignores them. In the first version, RepGarden always reminds a client unless that client switches reminders off.
- A receipts check asks Expo, a few minutes after a send, whether Apple or Google accepted the push.
- A measurement of whether reminders work holds back one reminder in ten at random and compares the results.
Each feature can be added later without changing the three steps. I don’t know yet which feature RepGarden will need first. My guess is the receipts check, because a reply that never arrives is the failure I care about most.
Where things stand
The design on this page was decided on 15 November. None of it is built. I have not yet chosen what RepGarden’s server will run on, and the design does not depend on that choice. I still need to ask Benji whether he wants an email when clips are waiting for him.
The next thing to design is a single screen. An iPhone shows its “allow notifications?” question once, ever. [10] The screen that RepGarden shows just before that question tells a client what RepGarden will notify them about and asks what time to remind them. That screen gets one chance.
Sources
Uber Engineering — How Uber Optimizes the Timing of Push Notifications using ML and Linear Programming. November 2022. Every push is stored in a per-user inbox, the whole inbox is planned again when a new one arrives, and a last check runs at the moment of sending.
Duolingo — Solving database contention with optimistic locking. December 2025. A post about a bug, which describes the reminder service along the way: each learner’s reminders are stored as a row, and a separate process sends the ones that are due.
LinkedIn Engineering — Air Traffic Controller: Member-First Notifications at LinkedIn. March 2018. One service decides on every notification, and its stages are listed in order.
Netflix Technology Blog — Rapid Event Notification System at Netflix. February 2022. Priority queues, a check that drops events that have gone stale, and a stored copy that devices fetch when a push doesn’t reach them.
Jorge Mazal — How Duolingo reignited user growth. Lenny’s Newsletter, February 2023. A first-hand account by Duolingo’s former chief product officer, with the rule about notification volume.
RAR1741/hub — issue 289. September 2026. A bug report from an open-source project whose reminder job read the unsent rows, sent them and then marked them sent, with nothing to stop two runs overlapping.
Expo — Send notifications with the Expo Push Service. The message format, including the expiry and its four-week default, and what Expo’s responses do and don’t confirm.
Batch — Push notification and mobile CRM benchmark 2025. Messages sent through 10,000 apps between July 2024 and July 2025. Batch sells notification software, so read it as a vendor’s figures: 56% of iPhone users and 67% of Android users opted in.
ABC Trainerize ideas forum — a request not to inform clients of every simple change. Open since 2014, with 295 votes when I read it.
Apple — Asking permission to use notifications. The first request shows the prompt and records the answer; later requests don’t prompt again.