Frontend Labs
⋯

35. Notification Platform

Business context. As in §60.2: separate transactional notifications (OTP, receipts: critical, low latency) from engagement/marketing notifications (DAU uplift, but over-notifying causes permanent opt-outs).

Requirements. Multi-channel (push, SMS, email, in-app); templates and localization; user preferences and quiet hours; dedupe; scheduling; analytics (delivered, opened). Volume: 1B notifications/day, with marketing blasts of 100M in under an hour. Transactional p99 < 5 s end to end.

Design.

Producers (services, campaign tool) ─▶ Notification API (validate, idempotency key)
     ─▶ priority topics: [critical] [transactional] [marketing]   ← separate queues, quotas, workers
     ─▶ Processor: preferences & opt-outs ─▶ frequency caps/ranking (marketing only) ─▶ template render
     ─▶ Channel routers ─▶ provider adapters (APNs, FCM, SMS vendors A/B, email ESP) with per-provider
                           rate limits, circuit breakers, and failover between SMS vendors
     ─▶ Delivery status/webhooks ─▶ event log ─▶ analytics, retry, invalid-token cleanup

Deep dives.

  • Priority isolation: a marketing blast must never delay OTPs. Use separate queues, worker pools, and provider quotas (bulkheads, §14).
  • Dedupe: an idempotency key per (user, notification_type, entity) with a TTL.
  • Provider limits and cost: SMS costs real money per message (cents, and more in some countries). Route by cost and deliverability, fall back to a secondary vendor, and prefer push or in-app when possible.
  • Frequency capping and ranking: a budget per user per day across all teams that send notifications. Without a central budget, every team optimizes its own metric and the user uninstalls.
  • Scheduling and time zones: "send at 9am local" means bucketing users by time zone and spreading the load.

Strategic lens. This is a platform that many teams depend on. Governance (who can send what, approval for large campaigns), self-serve templates, and per-team quotas and cost attribution matter as much as throughput.

🏗️ Flow: one notification from event to device, with all the checks.

 "order_shipped" event ─▶ Notification API (idempotency key = user:order:type)
   ─▶ [transactional] queue ─▶ Processor:
        prefs(user): email ✓ push ✓ sms ✗ ; quiet hours? (user TZ) → defer push to 08:00 local
        dedupe window (Redis SETNX key TTL 24h) ✓
        render template (locale de-DE, fallback en) ─▶ channel fan-out
   ─▶ Push router ─▶ APNs (token valid?) ─▶ 410 Unregistered → delete token
   ─▶ Email router ─▶ ESP A (circuit open?) → ESP B
   ─▶ delivery events (sent/delivered/opened/bounced) ─▶ Kafka ─▶ analytics + suppression lists

🌍 How it's built in production.

  • LinkedIn "Air Traffic Controller" (ATC): a centralized service that decides whether, when, and through which channel a member gets a notification, applying frequency caps and relevance across all teams. It exists to prevent over-notification, a business outcome problem (§60.2).
  • Uber RAMEN: the in-app real-time channel, separate from OS push. Use in-app channels for active users and OS push for inactive ones.
  • Amazon SNS/SES, Twilio, SendGrid: most companies buy the last mile and build the policy layer (preferences, budgets, templates). That's a typical build-vs-buy answer.

⚠️ Corner cases.

  • Notification about deleted content: the comment was deleted before the delayed push went out. Re-validate at send time.
  • Time zones and DST for quiet hours and "9 a.m. local" campaigns. Store each user's IANA time zone, not a UTC offset.
  • SMS deliverability by country: some countries require sender registration, and some routes silently drop messages. Fall back to voice OTP or another provider, and monitor delivery rate per country.
  • Marketing to opted-out users is a legal violation (CAN-SPAM, GDPR, TCPA). Suppression lists must be checked at send time, with a strongly consistent read.
  • Device token churn: reinstalls and new phones. Handle provider feedback (APNs 410, FCM UNREGISTERED) and prune tokens.
  • Cross-channel duplicates: the user gets a push and an email for the same event while active in the app. Suppress based on in-app presence, with a short delay before external channels.

Generated from real-life-system-design-guide.md · 8 Oct 2026 16:36