AI InterviewSearch…Search problems, topics, or pages…Ctrl KGet started
Skip to content
SD notes

System Design · Note 18 of 22

Design a Notification System

Intermediate · 7 min read · updated 14 November 2026

Quick answer

How do you design a notification system?

Services send events to a notification service, which checks the user's preferences, quiet hours and rate limits, renders a template and puts a job on a queue per channel. Workers for push, SMS and email call the providers (APNs/FCM, an SMS gateway, an email service), retry temporary failures with backoff, and use idempotency keys so a duplicate event never notifies twice.

On this page
  1. Requirements
  2. Capacity estimate
  3. API
  4. Data model
  5. High-level design
  6. Deep dive: retries and idempotency
  7. Deep dive: preferences, quiet hours, rate limits
  8. Templates
  9. Bottlenecks and trade-offs
  10. Interview questions
  11. Common questions

A notification system turns events ("order shipped", "new follower", "login code") into push notifications, SMS and emails — once each, on the right channel, at a sensible time.

Requirements

  • Functional: send push (iOS/Android), SMS and email; per-user preferences and opt-outs; templates; scheduled and bulk sends.
  • Non-functional: no lost notifications; no duplicates; urgent ones (login codes) within seconds; bulk campaigns may take minutes.
  • Soft real-time: delivery is asynchronous; the caller never waits for the provider.

Capacity estimate

Assumptions: 50 million users; per day 10 million push, 1 million SMS, 5 million email; peak 5× the average.

QuantityWorkingResult
Total a day10 M + 1 M + 5 M16 million
Average rate16 M ÷ 86,400 s≈ 185 / s
Peak rate185 × 5≈ 925 / s
Log storage16 M × 500 B8 GB a day
  • Bursts (a campaign to every user) matter more than the average: queues absorb them.

API

HTTP
Copy
POST /v1/notifications
{"userId": "u42", "type": "order_shipped",
 "data": {"orderId": "77"}, "idempotencyKey": "order-77:shipped"}
→ 202 Accepted {"id": "n1"}

Data model

TableKeyFields
devices(userId, token)platform, lastSeen
preferences(userId, type, channel)enabled, quiet hours, timezone
templates(type, channel, locale)subject, body with placeholders
notification_logiduserId, key, channel, status, attempts

High-level design

  • Callers send events; the notification service validates, applies preferences, renders the template.
  • One queue and worker pool per channel, each calling its provider.
One queue and one set of workers per channel

A "order shipped" event (n1) reaches the notification service, which reads the user's preferences and the rule for this type.

0:01 / 0:19Step 1 of 6
Transcript · 6 steps

Deep dive: retries and idempotency

  • Temporary errors (timeout, 5xx, 429): retry with exponential backoff + jitter, capped attempts.
  • Permanent errors (invalid token, bad address): no retry; dead-letter and clean up.
Retries: back off on temporary errors, give up on permanent ones

Two push notifications wait. The worker retries a temporary failure with exponential backoff and gives up after 4 attempts.

0:01 / 0:14Step 1 of 5
Transcript · 5 steps
  • Producers and queues deliver at least once, so duplicates will happen.
  • An idempotency key claimed with set-if-absent turns that into exactly one notification.
Idempotency keys: a duplicate event sends nothing

Each event carries an idempotency key, the order id plus what happened. The worker records a key the moment it claims one.

0:01 / 0:11Step 1 of 4
Transcript · 4 steps

Deep dive: preferences, quiet hours, rate limits

  • Gates run in order; the first refusal wins.
  • Opt-outs are per type and channel; transactional and security messages are treated differently from marketing.
  • Quiet hours use the user's time zone; hold, don't drop.
  • Rate limit marketing per user (see rate limiting).
Every notification passes the user's gates in order

A shipping update passes every gate and is sent. Transactional messages are still checked; only security codes usually skip quiet hours.

0:01 / 0:18Step 1 of 6
Transcript · 6 steps

Templates

  • Stored per type, channel and locale: Your order {orderId} has shipped.
  • Rendered at send time; wording changes need no deploy.
  • SMS templates stay short; email has subject + HTML + plain text.

Bottlenecks and trade-offs

  • Provider limits: APNs, FCM and SMS gateways throttle; workers must respect their rate limits.
  • Bulk sends compete with urgent ones: a priority queue keeps login codes ahead of campaigns.
  • SMS costs money per message; prefer push, fall back to SMS only when needed.
  • Analytics: log sent, delivered, opened per notification, off the hot path.

Interview questions

  • Why queues? To decouple callers from slow providers, absorb bursts and retry without blocking.
  • How do you prevent duplicates? Idempotency keys claimed atomically before sending.
  • What do you retry? Temporary errors with backoff and jitter; never permanent ones.
  • How do you respect users? Preferences, quiet hours in their time zone, and per-user rate limits.

Next: Design File Storage · Before this: Design a News Feed

Common questions

Why use a separate queue for each notification channel?

Channels fail and slow down independently: an SMS gateway outage should not delay push notifications. A queue per channel lets each set of workers scale, retry and be rate-limited on its own, and absorbs bursts such as a sale announcement sent to millions.

How do you avoid sending the same notification twice?

Give every event an idempotency key, such as the order id plus the event type. The worker claims the key with an atomic set-if-absent in a store like Redis before sending; a duplicate event finds the key taken and is skipped.

How should a notification system handle provider failures?

Retry temporary failures, such as timeouts, 5xx answers and 429s, with exponential backoff and random jitter, up to a maximum number of attempts. Permanent failures such as an invalid device token are not retried; they go to a dead-letter queue, and the bad token is removed.

What are quiet hours and rate limits in notifications?

Quiet hours hold non-urgent notifications during the user's night, in their own time zone, and deliver them in the morning. Rate limits cap how many notifications, usually marketing ones, a user receives per hour or day, so they are not overwhelmed.

What is a notification template?

A stored message with placeholders, such as Your order {orderId} has shipped, kept per channel and per language. The service fills in the values at send time, so product teams change wording without code changes and every channel stays consistent.

Design a Notification System: Push, SMS, Email

Design a notification system for push, SMS and email: queues per channel, retries with backoff, idempotency keys, user preferences, rate limits and templates.

  • Subject: System Design
  • Level: Intermediate
  • Reading time: 7 min
  • Updated: 2026-11-14

How do you design a notification system?

Services send events to a notification service, which checks the user's preferences, quiet hours and rate limits, renders a template and puts a job on a queue per channel. Workers for push, SMS and email call the providers (APNs/FCM, an SMS gateway, an email service), retry temporary failures with backoff, and use idempotency keys so a duplicate event never notifies twice.

A notification system turns events ("order shipped", "new follower", "login code") into push notifications, SMS and emails — once each, on the right channel, at a sensible time.

Requirements

  • Functional: send push (iOS/Android), SMS and email; per-user preferences and opt-outs; templates; scheduled and bulk sends.
  • Non-functional: no lost notifications; no duplicates; urgent ones (login codes) within seconds; bulk campaigns may take minutes.
  • Soft real-time: delivery is asynchronous; the caller never waits for the provider.

Capacity estimate

Assumptions: 50 million users; per day 10 million push, 1 million SMS, 5 million email; peak 5× the average.

QuantityWorkingResult
Total a day10 M + 1 M + 5 M16 million
Average rate16 M ÷ 86,400 s≈ 185 / s
Peak rate185 × 5≈ 925 / s
Log storage16 M × 500 B8 GB a day
  • Bursts (a campaign to every user) matter more than the average: queues absorb them.

API

POST /v1/notifications
{"userId": "u42", "type": "order_shipped",
 "data": {"orderId": "77"}, "idempotencyKey": "order-77:shipped"}
→ 202 Accepted {"id": "n1"}

Data model

TableKeyFields
devices(userId, token)platform, lastSeen
preferences(userId, type, channel)enabled, quiet hours, timezone
templates(type, channel, locale)subject, body with placeholders
notification_logiduserId, key, channel, status, attempts

High-level design

  • Callers send events; the notification service validates, applies preferences, renders the template.
  • One queue and worker pool per channel, each calling its provider.
Notification svcpicks the channelPush queueAPNs / FCM2 deliveredn1n4SMS queueSMS gateway1 deliveredn2Email queueEmail provider1 deliveredn3
One queue and one set of workers per channel.
  1. A "order shipped" event (n1) reaches the notification service, which reads the user's preferences and the rule for this type.
  2. n1 (order shipped) goes on the Push queue. Each channel has its own queue and workers, so a slow one never blocks the rest.
  3. n2 (login code) goes on the SMS queue, while n1 reaches APNs / FCM. Each channel has its own queue and workers, so a slow one never blocks the rest.
  4. n3 (receipt) goes on the Email queue, while n2 reaches SMS gateway. Each channel has its own queue and workers, so a slow one never blocks the rest.
  5. n4 (new follower) goes on the Push queue, while n3 reaches Email provider. Each channel has its own queue and workers, so a slow one never blocks the rest.
  6. n4 goes out through APNs / FCM. Push 2, SMS 1, email 1: each channel scaled and retried on its own.

Deep dive: retries and idempotency

  • Temporary errors (timeout, 5xx, 429): retry with exponential backoff + jitter, capped attempts.
  • Permanent errors (invalid token, bad address): no retry; dead-letter and clean up.
Push queueWorkerAPNs / FCManswered 200Dead-letter queuetry 3t = 3sn1n2
Retries: back off on temporary errors, give up on permanent ones.
  1. Two push notifications wait. The worker retries a temporary failure with exponential backoff and gives up after 4 attempts.
  2. n1 gets 503, a temporary failure. It goes back on the queue to retry in 1s; real systems add random jitter so retries do not arrive together.
  3. n2 gets 400: the device token is invalid, a permanent error. Retrying cannot help, so it goes straight to the dead-letter queue.
  4. Attempt 2 of n1 at 1s gets 503 again, so the wait doubles: retry in 2s.
  5. At 3s attempt 3 of n1 gets 200 and it is sent. The waits doubled, 1s then 2s, so a struggling provider was not flooded.
  • Producers and queues deliver at least once, so duplicates will happen.
  • An idempotency key claimed with set-if-absent turns that into exactly one notification.
Order serviceWorkerSent keyskeys: 2User's phonereceived: 2SET NXe2e1e2key order-78:shipped
Idempotency keys: a duplicate event sends nothing.
  1. Each event carries an idempotency key, the order id plus what happened. The worker records a key the moment it claims one.
  2. The key order-77:shipped is new, so "set if absent" succeeds and the notification is sent. The user gets "Order 77 shipped".
  3. The order service timed out and sent the same event again. Its key is already stored, so the worker skips it: no second "Order 77 shipped".
  4. Event e2 has a new key, order-78:shipped, so it is sent: 2 notifications for 2 real events.

Deep dive: preferences, quiet hours, rate limits

  • Gates run in order; the first refusal wins.
  • Opt-outs are per type and channel; transactional and security messages are treated differently from marketing.
  • Quiet hours use the user's time zone; hold, don't drop.
  • Rate limit marketing per user (see rate limiting).
EventOpt-inQuiet hrsRate limitResult10:00 Shipped · pushokokoksent10:05 Sale · SMSstop––dropped10:10 Sale · pushokokoksent10:20 Flash deal · pushokokoksent10:40 Price drop · pushokokstopdropped23:15 Delivered · pushokhold–held to 07:00
Every notification passes the user's gates in order.
  1. A shipping update passes every gate and is sent. Transactional messages are still checked; only security codes usually skip quiet hours.
  2. The user turned off marketing by SMS, so this sale is dropped at the first gate. Preferences are checked before anything costs money.
  3. The same sale by push is allowed. It is the first marketing message this hour (limit 2), so it is sent.
  4. A flash deal 10 minutes later is the second marketing message in the hour: still within the limit of 2, sent.
  5. A third marketing message within the hour hits the limit of 2 and is dropped. The limit protects users from notification fatigue.
  6. At 23:15 the user is in quiet hours (22:00 to 07:00), so the delivery update is held and sent at 07:00 instead of waking them.

Templates

  • Stored per type, channel and locale: Your order {orderId} has shipped.
  • Rendered at send time; wording changes need no deploy.
  • SMS templates stay short; email has subject + HTML + plain text.

Bottlenecks and trade-offs

  • Provider limits: APNs, FCM and SMS gateways throttle; workers must respect their rate limits.
  • Bulk sends compete with urgent ones: a priority queue keeps login codes ahead of campaigns.
  • SMS costs money per message; prefer push, fall back to SMS only when needed.
  • Analytics: log sent, delivered, opened per notification, off the hot path.

Interview questions

  • Why queues? To decouple callers from slow providers, absorb bursts and retry without blocking.
  • How do you prevent duplicates? Idempotency keys claimed atomically before sending.
  • What do you retry? Temporary errors with backoff and jitter; never permanent ones.
  • How do you respect users? Preferences, quiet hours in their time zone, and per-user rate limits.

Next: Design File Storage · Before this: Design a News Feed

Common questions

Why use a separate queue for each notification channel?

Channels fail and slow down independently: an SMS gateway outage should not delay push notifications. A queue per channel lets each set of workers scale, retry and be rate-limited on its own, and absorbs bursts such as a sale announcement sent to millions.

How do you avoid sending the same notification twice?

Give every event an idempotency key, such as the order id plus the event type. The worker claims the key with an atomic set-if-absent in a store like Redis before sending; a duplicate event finds the key taken and is skipped.

How should a notification system handle provider failures?

Retry temporary failures, such as timeouts, 5xx answers and 429s, with exponential backoff and random jitter, up to a maximum number of attempts. Permanent failures such as an invalid device token are not retried; they go to a dead-letter queue, and the bad token is removed.

What are quiet hours and rate limits in notifications?

Quiet hours hold non-urgent notifications during the user's night, in their own time zone, and deliver them in the morning. Rate limits cap how many notifications, usually marketing ones, a user receives per hour or day, so they are not overwhelmed.

What is a notification template?

A stored message with placeholders, such as Your order {orderId} has shipped, kept per channel and per language. The service fills in the values at send time, so product teams change wording without code changes and every channel stays consistent.

← Design a News Feed · Design a File Storage Service (Dropbox) →