Notification System Design Interview Guide: Fan-Out, Preferences, Retries, and Delivery Guarantees
Quick Overview
Design a multi-channel notification system with durable intent, recipient fan-out, preference enforcement, idempotent retries, and honest delivery-state tracking.
A strong notification system design interview answer starts with one durable intent, then separates policy, fan-out, channel delivery, and receipt tracking. Accept the intent idempotently; resolve the recipient's latest preferences and quiet hours; render a versioned template; create per-recipient, per-channel jobs; and process them through isolated queues and provider adapters. Use at-least-once processing with idempotent workers, capped retries, expiry, and dead-letter review.
Notification system: a service that converts product events into user-facing messages across push, email, SMS, and in-app channels while enforcing recipient preferences, timing rules, templates, rate limits, retry policy, and observable delivery state.
This guide is for software engineers preparing for a system design round. Use the PracHub system design interview questions to practice explaining the architecture under different scale and reliability constraints. PracHub question-bank records are practice material, not predictions of an exact interview.

Notification system design: the quick answer
Build the system around a durable NotificationIntent, not a synchronous call to an email or push provider. A transactional outbox or idempotent API persists the intent. An orchestrator loads current policy, resolves an immutable template version, calculates next_eligible_at, and expands the intent into delivery jobs. Push, email, SMS, and in-app workers consume separate queues so one provider outage cannot block every channel.
Workers persist each attempt before calling an external provider, classify the result, and retry only transient failures with exponential backoff and jitter. Provider callbacks enter an idempotent webhook pipeline and append delivery events.
The core guarantee is effectively-once user impact within a defined scope, not universal exactly-once delivery. Google Cloud documents that Pub/Sub exactly-once applies only to pull subscriptions within a region, while publish-side retries may still create separately identified messages. External push, SMS, and email systems add further uncertainty after your queue boundary. Google Cloud Pub/Sub exactly-once documentation
Clarify requirements and define “delivered”
Ask whether the product sends transactional alerts, social updates, security notices, marketing campaigns, or all four. Clarify channels, latency, scheduling, priority, localization, compliance, category preferences, and whether an in-app history remains when external channels are disabled.
Define delivery states before drawing boxes. A useful model is:
accepted internally → durably queued → provider accepted → provider/carrier delivered → displayed → read
Not every channel reports every transition. Firebase Cloud Messaging says a returned message ID means the request was accepted for delivery, not that the device received it. Apple Push Notification service is best effort and may store, coalesce, throttle, or drop notifications. An email provider's “delivered” event can mean that the recipient server accepted the message, not that it reached the inbox or was opened. Firebase message-lifespan guidance and Apple remote-notification guidance
State non-goals too. Queue internals, a general job scheduler, and an enterprise incident-escalation product can be supporting components without becoming the center of this answer.
Design the API and durable data model
A minimal send API might accept type, recipient or audience, variables, priority, schedule, expiry, and a client-supplied idempotency key. Return an internal notification ID after the intent is durable; do not wait for every provider.
Model the lifecycle explicitly:
NotificationIntent: source event, audience, priority, schedule, expiry, and idempotency key.Preference: category, channel state, quiet hours, timezone, locale, and version.TemplateVersion: channel, locale, schema, content, and immutable version.DeliveryJob: recipient, channel, deduplication key, status, and next attempt.DeliveryAttempt: provider ID, attempt number, error class, and timestamps.DeliveryEvent: provider event ID, status, occurred time, and received time.Suppression: unsubscribe, invalid endpoint, permission denial, reason, and effective time.
Use a unique business key such as source_event + recipient + notification_type + channel. Keep its deduplication record at least as long as the event can be retried or replayed; choose the window from the notification's expiry semantics.
Build the intent-to-receipt architecture
Producers write an intent through the API or a transactional outbox. The orchestrator reads policy, chooses channels, resolves template versions, and dispatches now or hands the job to a scheduling service. Google Cloud explicitly warns that a subscription retry policy is not an intentional-delay mechanism. Pub/Sub subscription retry policy
The fan-out stage writes auditable delivery jobs. Separate queues, workers, concurrency limits, and provider adapters by channel so a push outage cannot starve password-reset email. Apply provider, tenant, and recipient rate limits before each call.
Provider callbacks are repeatable input. Verify signatures, deduplicate provider event IDs, and reconcile late or out-of-order events. A status projection can change from sent to delivered, while immutable history retains both facts.
Choose fan-out-on-write, fan-out-on-read, or a hybrid
There are three different fan-outs: one product event to several consumers, one audience to many recipients, and one recipient to several channels. Naming them prevents an interview diagram from hiding the expensive step.
| Strategy | Best fit | Main trade-off |
|---|---|---|
| Fan-out-on-write | Urgent or personalized external delivery | Per-recipient auditability and retry isolation, but many writes and campaign spikes |
| Fan-out-on-read | Large, low-priority in-app announcements | Cheap shared storage, but it cannot proactively send SMS, email, or push by itself |
| Hybrid | Mixed transactional and broadcast workload | Materialize urgent channels; retain or incrementally expand shared low-priority audiences |
Amazon Simple Notification Service describes event fan-out as parallel delivery from one topic to multiple endpoints. That is a useful broker pattern, but audience expansion still needs product policy, recipient eligibility, and quota control. Amazon SNS overview
For a multi-million-recipient campaign, use admission control and progressive expansion. Partition by a stable recipient key, checkpoint progress, and cap job creation so a failed shard can resume independently.
Enforce preferences, quiet hours, and templates
Resolve preferences in a dedicated policy layer rather than encoding every user's mutable choices as broker subscriptions. Broker filtering can remove messages by attributes, but a notification policy also needs category precedence, legal suppression, device permission, quiet hours, frequency caps, locale, priority overrides, and fallback channels. Amazon SNS message filtering
Evaluate policy during orchestration and again before dispatch so a scheduled message respects a later unsubscribe. Use the recipient's IANA timezone, handle daylight-saving boundaries, and record the applied policy version.
Templates should be immutable, versioned, and channel-specific. Resolve variables before enqueueing; a missing field is a permanent content error. Apple documents server-rendered localization and client localization keys, so template strategy can differ by channel. Apple notification payload guidance
Server preferences also cannot override device settings. Android users can deny or revoke notification permission, so device authorization and token health belong in delivery eligibility and suppression handling. Android notification-permission documentation

Make retries safe with idempotency and deduplication
At-least-once queues can redeliver a message, so consumers must tolerate duplicates. Amazon SQS documents this behavior for standard queues. Handle duplication at three boundaries:
- API retry: deduplicate the request using the client idempotency key.
- Worker redelivery: claim or upsert the delivery job using its business key before the provider call.
- Callback retry: deduplicate provider events by provider and event ID.
Classify failures as permanent, transient, or ambiguous. Invalid tokens, malformed addresses, opt-outs, and template-schema errors should suppress or fail without repeated sends. Rate limits, network failures, and many server errors can use capped exponential backoff with jitter, expiry, and a retry budget. AWS SNS, for example, documents retry treatment for HTTP status classes and jitter, but each provider's contract must be checked independently. Amazon SNS delivery retries
The hardest case is a timeout after the provider accepted the request but before your worker received the response. A retry can duplicate the notification; provider failover can do the same. Reuse a provider idempotency key when available. Otherwise record the outcome as uncertain and choose between possible loss and possible duplication based on notification type.
After capped retries, move the job to a dead-letter queue with reason and replay controls. Put the DLQ beside the failing channel or subscription, not in one global pile. Amazon SNS dead-letter queue guidance
Track delivery without overstating guarantees
Persist every state transition with both occurred_at and received_at. Channel adapters normalize provider-specific statuses into a small internal vocabulary while retaining the original event for investigation. Twilio, for example, exposes several outbound message states, but their availability depends on channel and configuration. Twilio outbound-message status documentation
Track intent acceptance, queue age, suppression, attempts, retry age, DLQ depth, callback lag, provider latency, and status transitions. Alert on business symptoms, not only CPU or queue size.
Reconciliation compares old in-flight attempts with provider receipts or query APIs. It should repair the status projection, not fabricate certainty. “Provider accepted” remains different from “displayed,” and “displayed” remains different from “read.”
Estimate capacity and control broadcast hotspots
Use transparent interview assumptions rather than pretending to know the company's traffic. One worked example is:
| Input | Assumption or result |
|---|---|
| Daily active users | 10 million |
| Logical notifications | 3 per user per day = 30 million per day, 347 per second average |
| Peak logical rate | 10× average = about 3,500 per second |
| Selected channels | 1.4 per intent = about 4,900 channel jobs per second at peak |
| Retry overhead | 2% transient failures with two extra attempts = 4% more attempts, about 5,100 per second peak |
A separate campaign test often matters more than the average. Expanding five million recipients over ten minutes requires about 8,300 recipient jobs per second before channel multiplication. That leads naturally to progressive fan-out, partitioning, per-provider quotas, backpressure, and pausing low-priority campaigns when transactional traffic needs capacity.
Order only a narrow stream, such as one conversation or user. Global ordering adds coordination and head-of-line blocking. Exactly-once features do not remove the need for application-level business keys.
A 45-minute interview walkthrough
- Minutes 0-5: clarify notification types, channels, scale, priority, scheduling, preferences, and the meaning of delivered.
- Minutes 5-10: define the API, lifecycle states, and non-goals.
- Minutes 10-20: draw intent storage, orchestration, policy, templates, scheduling, fan-out, channel queues, and adapters.
- Minutes 20-28: deep-dive on fan-out strategy, campaign hotspots, and per-channel isolation.
- Minutes 28-36: explain idempotency, ambiguous timeouts, retry budgets, expiry, and DLQs.
- Minutes 36-42: cover callbacks, reconciliation, delivery-state semantics, metrics, and privacy.
- Minutes 42-45: summarize the guarantees and the largest remaining trade-off.
Avoid saying “Kafka guarantees exactly once, so users receive one notification.” The broker, worker, external provider, device, and human are different boundaries. A stronger answer states the guarantee at each boundary and explains where deduplication can only reduce—not eliminate—duplicates.
Practice with notification system design questions from PracHub
These verified PracHub question-bank records train related skills. They are practice material, not predictions of your exact assessment or interview.
| PracHub question | Practice focus | Why it helps |
|---|---|---|
| Design a follower push-notification system | Audience fan-out and preference filtering | Tests the expensive recipient-expansion path and overload controls. |
| Design an alert notification system | Multi-channel routing and suppression | Exercises quiet hours, deduplication, provider failures, and audit history. |
| Design a scalable notification system | Templates, personalization, and backpressure | Connects product policy to high-volume delivery. |
| Design at-least-once notification delivery | Idempotency, retries, and DLQs | Forces a precise delivery guarantee instead of an impossible promise. |
| Design high-throughput event subscription system | Filtering, tenant isolation, and rate limits | Deepens fan-out and webhook delivery trade-offs. |
Frequently asked questions
Can a notification system guarantee exactly-once delivery?
Not universally from producer to human. A broker may provide exactly-once behavior within a narrow boundary, while publish retries, provider timeouts, devices, and callbacks create other risks. Define the scope, use idempotency and deduplication, and promise effectively-once impact only where supported.
When should notification fan-out happen on write versus read?
Fan-out on write fits urgent, personalized external messages because it creates independent jobs and audit records. Fan-out on read fits large, low-priority in-app announcements that remain shared until read. A hybrid usually handles mixed workloads best.
How should quiet hours and channel preferences be enforced?
Resolve category, channel, locale, timezone, priority, suppression, and device authorization in a policy layer. Calculate the next eligible time with daylight-saving-aware data, then recheck preferences before dispatch so a later unsubscribe wins.
How do you retry provider failures without sending duplicates?
Retry transient failures with capped exponential backoff, jitter, expiry, and a stable delivery key. Do not retry permanent failures. A timeout after possible provider acceptance is ambiguous: reuse provider idempotency when available, or choose explicitly between duplicate risk and loss.
What does “delivered” mean for push, SMS, and email?
It depends on the channel. Internal acceptance, provider acceptance, carrier or device delivery, display, and read are separate states. Store the most precise supported status, retain the provider event, and never treat acceptance as proof that a person saw the message.
Final takeaway
The best notification system design interview answer states where work is durable, where fan-out occurs, which policy applied, how duplicates are contained, when retries stop, and what each state proves. It isolates channels, treats provider timeouts as ambiguous, and controls broadcast expansion.
Practice the walkthrough aloud, then use the PracHub system design question library to test whether you can adapt the same principles to follower notifications, alerts, at-least-once delivery, and high-throughput subscriptions.
Sources and Further Reading
- Amazon SNS overview and fan-out model
- Amazon SNS message filtering
- Amazon SQS at-least-once delivery
- Amazon SNS message delivery retries
- Amazon SNS dead-letter queues
- Google Cloud Pub/Sub subscription overview
- Google Cloud Pub/Sub exactly-once delivery
- Google Cloud Pub/Sub ordered delivery
- Google Cloud Pub/Sub retry policy
- Google Cloud Pub/Sub dead-letter topics
- Firebase Cloud Messaging message lifespan
- Apple: Setting up a remote notification server
- Apple: Generating a remote notification
- Android notification runtime permission
- Twilio outbound message statuses
Research note: This guide was checked on August 31, 2026. Provider documentation is living documentation, and exact capabilities can vary by channel, account, region, and client version.
Comments (0)