Playbook / General system design / Design a notification system

Design a notification system

Expected question

"Design a notification system. How do you deliver push, email, SMS, and in-app notifications reliably with user preferences and rate limits?"

Variant forms

Interviewers often ask the same design with different framing — recognize the archetype:

  • "Design Uber-style trip notifications across push, SMS, and email with fallbacks."
  • "How do you send 1B push notifications for a marketing campaign without burning deliverability?"
  • "Design user preference center: quiet hours, channel opt-in, topic subscriptions."
  • "Our notifications duplicated — architect idempotency and deduplication keys."
  • "Design priority: transactional alerts bypass marketing throttles."
  • "How do you template and localize notifications for 40 languages?"
  • "Design delivery analytics: sent, delivered, opened, failed — with retry policies."

Where this actually gets asked

Top-tier classic system-design question at Amazon, Meta, and most big-tech loops: multi-channel notifications (push, email, SMS, in-app) with fan-out, preferences, and reliability. Not AI-specific; still appears in Staff loops as an operational-depth probe.

Requirements

Functional

  • Accept notification events from producers (order shipped, mention, security alert).
  • Deliver via user-preferred channels with templates.
  • Honor quiet hours, per-category opt-outs, and frequency caps.
  • Support digests (batch) and realtime (immediate) priorities.

Non-functional

  • At-least-once delivery with idempotent providers; no silent drops for security alerts.
  • Fan-out to millions of recipients for one event (celebrity / flash sale) without melting producers.
  • Per-channel retries, DLQ, and observability.
  • P99 enqueue latency low; end-to-end delivery SLO varies by priority.

Core entities

  • Notification request: idempotency_key, user_id(s) or segment, template_id, priority, payload.
  • Preference: user_id, channel, category, enabled, quiet_hours.
  • Delivery attempt: channel, provider_message_id, status, attempt_n.
  • Template: localized body, required variables, channel constraints.

API / interface

POST /v1/notifications
Idempotency-Key: <uuid>
{ "user_id":"u_...", "category":"security", "template":"login_new_device",
  "data":{"device":"iPhone"}, "priority":"high" }
→ 202 { "notification_id":"n_..." }

POST /v1/notifications/bulk
{ "segment_id":"seg_...", "template":"sale_start", "priority":"normal" }
→ 202 { "campaign_id":"cmp_..." }

PUT /v1/users/{id}/preferences
{ "email":{"marketing":false,"security":true}, "push":{"mentions":true}, "quiet_hours":"22-07" }
→ 200

GET /v1/notifications/{id}
→ { "status":"delivered|pending|failed", "attempts":[...] }

Staff+ callout: security category ignores marketing opt-outs; encode that in policy, not hope.

Data Flow

Producer enqueues → preference filter → channel router → provider adapters → receipt/webhook updates status.

Rendering architecture diagram…

High-level design

Maps to functional requirements from step 1 — the component architecture that makes the API and data flow real.

Rendering architecture diagram…

Deep dives below target non-functional requirements (latency, scale, failure, cost, security).

Deep dive 1: fan-out at scale

For 1:N campaigns, do not expand millions of rows synchronously in the producer. Use segment materialization + chunked workers, or write-fanout to per-user inboxes for small N and read-fanout for large N (same celebrity problem as feeds). Cap QPS to each provider; smooth with token buckets.

Deep dive 2: reliability semantics

Idempotency keys at ingest; provider-level dedupe. Retries with exponential backoff + jitter; DLQ after N. Critical alerts: fail closed on preference store outage (still attempt security email); marketing: fail open to skip is acceptable. Track provider SLA breaches separately.

Deep dive 3: preference and abuse

Frequency caps prevent notification fatigue and SMS cost blowups. Quiet hours defer normal priority but not security. Rate-limit producers that spam a single user.

Deep dive 4: delivery SLOs and provider circuit breaking

Track queued → sent → delivered → bounced per notification; set SLOs by priority (security: minutes; marketing: hours). On provider degradation, circuit-break — stop global retry storms, use capped backoff, and only cross-channel fallback where policy allows. In 45 minutes, cover fan-out + preferences + idempotency + one provider-failure story.

What's expected at each level

  • Mid-level: one queue → email API.
  • Senior: multi-channel + preferences + retries.
  • Staff+: fan-out strategy, idempotency, priority vs quiet hours, DLQ/ops.
  • Principal: provider cost economics, abuse, and cross-region delivery SLOs.

Follow-up questions to expect

  • "How do you digests work?" (Windowed aggregation worker per user/category.)
  • "User disables push mid-campaign?" (Preference check at send time, not only enqueue.)