7 mistakes in notification system design (I've made every one of them)
Sending everything, no severity, duplicates under stress, messages at 3 a.m. — the mistakes that make recipients mute your app, and how to think about fixing each one.
Notification systems are very easy to make work and very hard to make people want. I've been building them for years, for clients and for myself, and I've made every mistake below — a couple of them twice.
1. Sending everything that happens
The symptom: every event in the backend fires a notification. Order placed, order updated, order paid, order packed, order shipped. One customer gets five messages per order; a staff member gets five times the number of orders per day.
The consequence isn't just annoyance. The recipient mutes the app, and then misses the one message in a hundred that actually needed them.
The question to ask before every send: if the recipient never sees this, what happens? If the answer is "nothing", don't send it. Put it on a log page or a dashboard instead.
2. No severity
"Server down" and "new user signed up" arrive as the same message, same sound, same look. The recipient has to open every one to find out which can wait.
You want at least three levels, and they should differ before the message is opened — in the title, a colour, or a prefix visible on the lock screen. If your channel supports cards or colours, this is what they're for, not decoration.
3. Duplicates when the system is under stress
This one hurts the most because it happens exactly when you're busiest.
A real example: a worker is sending to 2,000 customers, gets through 800, and you deploy new code. The worker restarts and begins again from the first customer. 800 people get the message twice. The other flavour is unbounded retry: one network hiccup, ten copies sent.
The fix has two parts and you need both: remember who has already been sent to at the recipient level, not the job level, and retries need a cap and spacing (exponential backoff). This is long enough for its own article: why sending notifications needs a queue.
4. The message doesn't say what to do
"New order" — and? Which app do I open, which page, what do I press?
A good message answers three things by itself: what happened (order #1042 from Somchai, 450 THB), what's needed (confirm within 15 minutes), and where to do it (an "Accept order" button that finishes the job right there). If the channel supports buttons, use them. If it doesn't, at least link to the exact page, not the system's home page.
5. Sending at 3 a.m.
The system doesn't know the recipient is asleep. A cron job that runs at midnight sends the daily summary at 00:05. A report meant for the morning arrives as the recipient is going to bed.
Be explicit about what must wake someone (system down, money missing) versus what can wait until morning (daily totals, a new review). Hold the second kind and deliver it in a batch at a fixed time, say 08:30. Recipients will thank you, and they won't mute the app.
6. One channel, no fallback
Everything goes to LINE. The day LINE has an outage, or your quota runs out, or a customer blocks the OA, everything goes quiet — and you don't know it's quiet.
For anything important, have at least one second channel, and know when the primary one failed rather than tossing the message over the wall and hoping. Per-recipient, per-channel delivery results should come back as data you can look at.
7. Not recording what was sent, and when
A customer calls: "I never got the notification." The team can't say whether it was sent, at what time, to which channel, or whether it succeeded or failed and why.
A send log isn't a luxury. It's what lets you answer that question in ten seconds instead of combing server logs for half an hour. Record at minimum: who, which channel, when, the result, and the actual content sent (because the template may have changed since).
What I chose to build into Ting
To be upfront: numbers 3, 6 and 7 are the ones I never wanted to solve again on every project, so they became shared infrastructure in Ting — a queue that tracks each recipient, capped retries, per-channel results visible in the console, and a log that keeps the real content sent.
Numbers 1, 2, 4 and 5 can't be fixed by any tool. They're decisions the person designing the system makes about what deserves to be sent and what doesn't — and that's always the harder part than the code.