System Design Case Studies
Designing a Notification System
A notification system fans a single event out across push, email, and SMS channels, each with different delivery guarantees and failure modes, without spamming a user across channels for the same event.
- Requirements to size the design: which channels (push, email, SMS, in-app), delivery guarantee (best-effort, retried, is normal), and expected fan-out volume for a single triggering event
- Producers publish an event to a queue rather than calling notification providers directly — decoupling means a slow or down provider does not block the service that triggered the notification
- Per-channel workers consume from the queue and call the relevant third-party provider — each channel has its own throughput limits and retry/backoff policy
- User preferences (which channels, quiet hours, opt-outs) must be checked before sending — a compliance requirement as much as a UX one
- Idempotency matters twice over: the same event should not be processed twice by a retried worker, and the same logical notification should not be re-sent if an earlier attempt actually succeeded but its acknowledgment was lost
- Third-party providers rate-limit and can be temporarily down — the pipeline needs its own backoff and dead-letter queue, or a provider outage backs up and starts timing out producers
| Channel | Delivery guarantee | Latency | Typical use |
|---|---|---|---|
| Push | best-effort | seconds | time-sensitive, in-app relevant |
| best-effort, retried | seconds to minutes | non-urgent, detailed content | |
| SMS | best-effort, higher cost per message | seconds | urgent, high open-rate needs |
void process(NotificationEvent event) {
if (!dedupStore.markIfAbsent(event.idempotencyKey())) {
return; // already processed — a retry landed here again
}
if (preferences.allows(event.userId(), event.channel())) {
provider.send(event);
}
}