Designing a Push Notification System at Billion-Device Scale (iOS & Android)

Designing a Push Notification System at Billion-Device Scale

Designing a Push Notification System at Billion-Device Scale

A ground-up, interview-ready architecture for reliably delivering push notifications to a billion iOS and Android devices — covering APNs and FCM internals, fan-out at scale, device-token lifecycle, retries, and the trade-offs real engineering teams live with.

01

Introduction & History

Every time your phone buzzes with a message, a price drop alert, a ride-arrival notice, or a friend’s comment on a photo, a small but extraordinarily complex piece of distributed-systems engineering just fired. Push notifications feel instantaneous and effortless to the end user, but underneath that single buzz is a pipeline that has to track billions of devices, know exactly which network path is currently valid for each one, respect two entirely different vendor ecosystems with different rules, retry failures without flooding anyone, and do all of this within a latency budget of a few seconds — at a scale of tens of billions of notifications a day for the largest platforms in the world.

Push notifications did not always exist. In the earliest smartphone era, apps that wanted to alert users had only one option: polling. An app would wake up periodically, open a network connection, ask the server “anything new for me?”, and go back to sleep. This was catastrophic for battery life — imagine every app on your phone independently waking the radio every few minutes. Apple recognized this in 2009 and introduced the Apple Push Notification service (APNs), a centralized, persistent connection that the operating system itself maintained with Apple’s servers. Instead of every app polling, the OS held one long-lived connection, and Apple multiplexed notifications for every app through it. Google followed with Cloud to Device Messaging (C2DM) in 2010, which evolved into Google Cloud Messaging (GCM) in 2012, and was eventually rebuilt and rebranded as Firebase Cloud Messaging (FCM) in 2018 — the current standard for Android push delivery.

2009

APNs launches

Apple introduces a centralized, OS-level persistent connection so individual apps no longer need to poll for updates.

2010–2012

C2DM → GCM

Google’s Cloud to Device Messaging matures into Google Cloud Messaging, bringing a similar centralized model to Android.

2018

FCM becomes the standard

Firebase Cloud Messaging rebuilds and rebrands GCM, adding Doze/App-Standby awareness, priority tiers, and a modern HTTP v1 API.

The core insight that both platforms converged on is the same: push delivery must be centralized at the OS/vendor level, not left to individual app developers to solve with their own polling or socket logic. Your backend never talks directly to a user’s phone. It talks to Apple or Google, and Apple or Google — who already maintain a persistent, battery-optimized connection to that device — deliver the final hop. This is the single most important architectural fact to internalize before designing anything else: you are building a system that talks to two gatekeeper platforms, not directly to a billion phones.

Real-life analogy

Imagine fifty independent couriers each keeping their own delivery van idling outside your house all day, just in case a package needs to arrive. Now imagine instead a single mailbox and one shared postal service that hands off every package to the right person inside. The OS-level push connection is that shared mailbox — one warm channel that every app rides on.

It’s worth pausing on why a single persistent connection per device, multiplexed across every installed app, is such a big architectural win. Before this model, if you had fifty apps on your phone and each maintained its own always-on socket to its own backend, you would have fifty separate radio wake-ups, fifty separate TCP/TLS handshakes to keep alive, and fifty independent points of battery drain — the radio is, by a wide margin, the most power-hungry component on a mobile device, and keeping it active is far more expensive than keeping the CPU idle. By centralizing this at the OS level, Apple and Google turned an O(n) battery cost (n = number of apps) into an O(1) cost: one connection, one radio wake pattern, shared by every app on the device. This single design decision is why push notifications are viable at all on battery-constrained hardware, and it’s the reason your system, no matter how sophisticated, must always terminate at APNs or FCM rather than trying to reinvent this transport layer.

The other historical thread worth knowing is that early push systems were unreliable and inconsistent across OEMs on Android specifically, because for years many Android manufacturers layered aggressive battery-saving customizations (task killers, background-restriction lists) on top of stock Android that could silently prevent FCM’s underlying persistent connection from staying alive. This fragmentation is part of why FCM’s current architecture leans so heavily on Doze-mode-aware priority levels and why understanding “high priority” versus “normal priority” is not a minor implementation detail — it directly determines whether the OS will wake the device’s network stack at all.

?
What the interviewer may ask
  • Why can’t apps just keep their own persistent socket open to every device instead of relying on APNs/FCM?
  • What problem did push notifications solve that polling could not?
  • Why did Google rebuild GCM into FCM instead of just extending GCM?
  • Why does a single shared OS-level connection save more battery than each app managing its own connection?
02

Architecture & Core Components

At a billion-device scale, the push system is really several cooperating subsystems, each independently scalable. Let’s name them before we go deeper.

CLIENT LAYER

Client-Facing Layer

API Gateway, authentication, rate limiting, and the public “send notification” and “register device” endpoints that internal services and clients call.

ORCHESTRATION

Notification Orchestration

Notification Composer, Targeting/Segmentation Service, Template Engine, and the Campaign Scheduler that decide what to send and to whom.

DELIVERY

Fan-Out & Delivery

The Fan-Out Service, Device Token Registry, per-platform Dispatch Workers, and Retry/DLQ pipeline that turn one logical notification into millions of individual delivery attempts.

GATEWAYS

Platform Gateways

APNs (Apple) and FCM (Google) — the external systems that own the final hop to the device. You do not control these; you integrate with them under their constraints.

Producers Backend Services Campaign Tools Internal Event Producers Edge API Gateway TLS + rate limit Auth + Event Bus mTLS / OAuth + Kafka Orchestration Notification Composer template + payload Template Engine localization + render cache Targeting Service audience segmentation Device Token Registry sharded, key-value Fan-Out & Dispatch Core Notification Queue Kafka, per-platform partitions Fan-Out Service explodes to per-device jobs Preference / Consent quiet hours, opt-outs Platform Router APNs vs FCM APNs Dispatch Workers HTTP/2 multiplexed FCM Dispatch Workers HTTP v1 REST Reliability & Observability Retry Orchestrator + DLQ exp backoff + jitter Metrics + Delivery Ledger acceptance vs delivery External Gateways & Devices Apple APNs HTTP/2 multiplexed Google FCM HTTP v1 REST iOS Devices OS-managed persistent connection Android Devices Doze / App Standby aware
Fig. 1 — Layered architecture: producers, edge, orchestration, fan-out & dispatch core, reliability/observability, and external gateways with target devices.

2.1 Component Responsibilities

ComponentResponsibility
API GatewayTerminates TLS, enforces authentication (mTLS or OAuth service tokens), applies coarse-grained rate limiting, and routes requests to the Composer service.
Notification ComposerValidates payload, resolves templates, applies localization, and stamps a unique notification ID for idempotency and tracing.
Targeting / Segmentation ServiceResolves a logical audience (e.g., “all users in California who abandoned a cart”) into a concrete, bounded set of device tokens, in batches, without loading the whole set into memory.
Device Token RegistryThe source of truth mapping user/app installation to a current, valid platform-specific device token. This is the single most operationally important data store in the whole system.
Fan-Out ServiceExplodes one logical send into millions of individual per-device delivery jobs, and pushes them onto per-platform queues, applying backpressure so downstream gateways are never overwhelmed.
Platform Dispatch WorkersSpeak the actual APNs (HTTP/2) or FCM (HTTP v1) wire protocol, handle per-platform payload size limits, batching, and connection pooling.
Retry Orchestrator / DLQClassifies failures (transient vs. permanent), applies exponential backoff with jitter, and routes unrecoverable failures to a dead-letter queue for offline analysis instead of retrying forever.
Metrics & Delivery LedgerRecords per-notification delivery state (queued, sent, acked, failed, token-invalidated) for auditability, debugging, and SLA reporting.

2.2 Why Separate Fan-Out From Dispatch?

A subtle but important design decision is splitting “explode a logical send into millions of jobs” (Fan-Out) from “speak the wire protocol to a specific gateway” (Dispatch). These have very different scaling characteristics and failure modes. Fan-Out is CPU- and I/O-bound on reading the token registry and writing to queues — it scales with audience size. Dispatch is bound by external rate limits imposed by APNs and FCM — it scales with how fast the gateway will accept traffic, which is largely fixed regardless of how much internal compute you throw at it. If these were combined into one service, a burst in audience size (Fan-Out load) would directly compete for the same process resources as gateway-bound work (Dispatch load), making it much harder to reason about capacity and to apply targeted backpressure. Keeping them separate means you can scale Fan-Out horizontally to handle audience resolution bursts while Dispatch stays sized to exactly what APNs/FCM will tolerate — and a slow gateway never starves the ability to keep resolving and queueing new audiences.

2.3 The Device Token Registry as the System’s Center of Gravity

Every other component in this architecture is, in some sense, in service of keeping the token registry accurate and using it correctly. If it’s stale — holding tokens for uninstalled apps, expired app installations, or devices that rotated their token weeks ago — you pay for it in three ways: wasted dispatch capacity spent on doomed sends, inflated cost (both compute and any per-message vendor billing where applicable), and misleading acceptance-rate metrics that make the system look less healthy than it actually is, or worse, mask a real problem underneath the noise. This is why the design deliberately treats every gateway error response as an immediate, synchronous trigger to update the registry rather than a “clean it up later” batch job — token hygiene is a real-time concern, not a housekeeping afterthought.

?
What the interviewer may ask
  • Why is the Device Token Registry called out as the “most operationally important” store — what breaks if it’s stale?
  • Why separate the Fan-Out Service from the platform-specific Dispatch Workers instead of combining them?
  • How would you design the Targeting Service to avoid loading a billion-row audience into memory at once?
  • What’s the cost of stale tokens beyond wasted network calls?
03

Internal Working — APNs & FCM Deep Dive

Understanding push at scale means understanding the two gatekeepers in real detail, because your entire dispatch layer’s design is dictated by their constraints.

3.1 Apple Push Notification service (APNs)

APNs uses HTTP/2, which allows many concurrent streams over a single long-lived, mutually-authenticated TCP connection. Your dispatch worker authenticates either with a provider certificate or, more commonly today, with a JWT signed with a p8 authentication key tied to your Apple Developer Team ID. Each notification is sent as an HTTP/2 POST to a device-token-specific path, with the JSON payload in the body and metadata (priority, expiration, topic/bundle-id, push type) in HTTP headers.

  • Payload limit: 4 KB for standard notifications, 5 KB for VoIP push types — treat as configuration, not a hard-coded constant, since Apple has moved these over the years.
  • Connection model: multiplexed HTTP/2 — many notifications per connection, few connections needed even for high throughput.
  • Priority: APNs supports “immediate” delivery (priority 10, wakes the device) and “power-considerate” delivery (priority 5, batched by the OS to save battery).
  • Feedback: APNs responds synchronously per stream with an HTTP status. A 410 Gone or specific reason string like BadDeviceToken or Unregistered tells you the token is dead and must be purged from your registry immediately — this is your primary signal for token hygiene.

3.2 Firebase Cloud Messaging (FCM)

FCM uses an HTTP v1 REST API (the legacy XMPP/HTTP endpoints are deprecated) authenticated with OAuth2 tokens derived from a service account. Each request targets a registration token, a topic, or a condition (a boolean expression over topics).

  • Payload limit: around 4 KB for the data payload.
  • Message types: notification messages (OS renders them automatically, even if the app is killed) versus data messages (delivered to the app’s code, which decides what to show — essential for custom rendering, badge counts, or silent background sync).
  • Delivery priority: “high” wakes the device and delivers immediately; “normal” is batched, especially under Android’s Doze mode and App Standby power-saving states.
  • Feedback: a failed send returns an error code such as UNREGISTERED or INVALID_ARGUMENT. Like APNs, this is your signal to invalidate the stored token.

3.3 The Fundamental Platform Asymmetry

This is a favorite interview probe: the two platforms are not symmetric, and a naive design that treats them identically will misbehave in production.

DimensionAPNs (iOS)FCM (Android)
TransportHTTP/2, per-connection multiplexingHTTP v1 REST, connection-per-request (pooled)
AuthJWT (p8 key) or certificateOAuth2 via service account
Silent / background push“background” push type, throttled heavily by iOS, no delivery guaranteeData messages deliver more reliably to app code, subject to Doze
Token volatilityChanges on app reinstall, OS restore, or periodicallyChanges on app reinstall, token rotation, or Instance ID reset
Topic-based fan-outNot natively supported — you manage grouping yourselfNative “topics” (pub/sub) supported server-side, up to platform limits

3.4 Connection Pooling and Throughput Engineering

Because APNs multiplexes many notifications over few HTTP/2 connections, your dispatch workers should maintain a small, warm pool of long-lived, authenticated connections per provider credential rather than opening a new connection per notification — connection setup (TCP handshake, TLS negotiation, HTTP/2 settings exchange) is orders of magnitude more expensive than sending an already-established stream. The practical pattern is: a fixed-size connection pool per worker process, health-checked and replaced on error, with in-flight stream concurrency capped below APNs’ per-connection stream limits so you never trigger a forced connection reset. FCM, being a stateless REST API, doesn’t multiplex in the same sense, but the same principle applies to HTTP connection reuse (keep-alive) and OAuth2 token caching — refreshing an OAuth token on every single request would add unnecessary latency and load on Google’s auth infrastructure at billion-device volumes.

3.5 Handling Silent / Background Pushes Correctly

Both platforms offer a “silent” push type intended to wake the app in the background without showing a visible alert — useful for syncing data ahead of the user opening the app. It’s critical to understand that this category has explicitly weaker guarantees than user-visible pushes: iOS in particular aggressively throttles background pushes based on the app’s recent usage patterns, battery level, and Low Power Mode state, and reserves the right to defer or drop them entirely. A common design mistake is architecting a feature (e.g., “always have fresh content ready when the user opens the app”) around an assumption that silent push is reliable. The safe pattern is to treat silent push as a best-effort optimization layered on top of a pull-based fallback (the app still checks for fresh data on foreground), never as the sole delivery mechanism for anything the product depends on.

?
What the interviewer may ask
  • Why does APNs use HTTP/2 while older push systems used raw persistent binary sockets — what does multiplexing buy you?
  • What is the practical difference between an FCM “notification” message and a “data” message, and why does that distinction matter for reliability?
  • Your delivery success rate on Android is high but low on iOS during background sends — what’s the likely cause?
  • Why should dispatch workers maintain a pool of warm connections rather than opening one per notification?
  • Why is it dangerous to build a core product feature that assumes silent push always arrives?
04

Data Flow & Message Lifecycle

Trace a single notification from trigger to phone screen. This end-to-end lifecycle view is exactly what interviewers want you to whiteboard.

Producer Composer Targeting Token Registry Fan-Out Worker APNs / FCM Device trigger (event + template) resolve audience fetch tokens (paginated) batch of tokens + platform audience batches enqueue delivery jobs dispatch per-platform deliver payload (HTTP/2 or REST) ack / error code update delivery ledger final hop delivered / stored On permanent error → invalidate token in registry (real-time) invalidate token On transient error → retry with exponential backoff + jitter requeue with delay Retries capped → DLQ + alert (never retry forever)
Fig. 2 — End-to-end lifecycle from trigger through composition, targeting, fan-out, dispatch, gateway ack, and final hop — plus error and retry paths.

4.1 Lifecycle Stages

  1. Trigger: an internal event (order shipped, friend request, price alert) or a scheduled campaign initiates the send.
  2. Composition: template resolution, localization, personalization tokens filled in, payload built and validated against platform size limits.
  3. Targeting: the logical audience is resolved into a token stream, paginated to avoid unbounded memory use — this is where segmentation queries, consent checks, and quiet-hours logic run.
  4. Enqueue: jobs land on a durable queue (typically Kafka), partitioned by platform and possibly by shard of device ID for parallelism.
  5. Dispatch: platform-specific workers pull batches, open/reuse connections, and send.
  6. Acknowledgment: the gateway responds per-message (APNs) or per-batch (FCM), and the worker updates a delivery ledger.
  7. Retry or Dead-letter: transient failures (rate limiting, timeouts) go back onto a delayed retry queue with exponential backoff; permanent failures (bad token) trigger token invalidation.
  8. Final hop: the OS-level push service delivers to the device if online, or stores the notification (subject to platform-specific TTL) for delivery when the device reconnects.
?
What the interviewer may ask
  • At which stage would you enforce a user’s “do not disturb” / quiet hours preference, and why there rather than earlier or later?
  • How do you guarantee idempotency if the Fan-Out Service crashes mid-batch and reprocesses the same Kafka partition?
  • What happens to a notification if the device is offline for a week — does it ever get delivered, and who decides?
05

Advantages, Disadvantages & Trade-offs

Advantages of this architecture

  • Decoupled fan-out means the Composer and Targeting layers scale independently from platform dispatch.
  • Queue-based buffering absorbs traffic spikes (breaking news, flash sales) without overwhelming APNs/FCM.
  • Centralized token registry gives a single place to enforce hygiene, consent, and privacy rules.
  • Per-platform workers isolate blast radius — an APNs outage doesn’t stall Android delivery.

Disadvantages & costs

  • Operational complexity: many moving parts, each independently scaled, monitored, and on-called.
  • Eventual delivery, not guaranteed delivery — neither APNs nor FCM promise exactly-once, real-time delivery.
  • Token churn creates constant background load: purging, re-registering, and reconciling registries.
  • Cost scales with message volume, retry amplification, and the storage/compute for a billion-row token store.

5.1 Key Trade-offs

Trade-offOption AOption BWhat to consider
Delivery guarantee At-least-once (retry aggressively) Best-effort (fire and forget) At-least-once risks duplicate notifications; best-effort risks silent loss. Most systems choose at-least-once with client-side dedup by notification ID.
Fan-out granularity Per-device jobs Platform-native topics/conditions Per-device gives fine control (personalization, targeting) but costs more compute; topics are cheap but coarse and iOS lacks native topic support.
Consistency of token registry Strong consistency Eventual consistency A billion-row store under constant token churn favors eventual consistency with a short staleness window; strong consistency doesn’t scale linearly here.
Priority handling Always high priority Priority tiers Always-high burns user battery and gets throttled by OS heuristics over time; tiered priority preserves your “high priority” credibility with the OS.
Push is eventual, best-effort, and outside your control after the gateway handoff. Design SLAs and dashboards around what you can honestly measure — not what your users hope for.
?
What the interviewer may ask
  • Why can’t a push notification system offer exactly-once delivery end-to-end?
  • If you had to cut scope under time pressure, would you sacrifice delivery guarantees or targeting precision first, and why?
06

Performance & Scalability at Billion-Device Scale

Let’s ground this in real numbers. Assume a billion registered devices, split roughly 60/40 between Android and iOS, and a platform that sends an average of 5 notifications per device per day across all campaigns and transactional triggers.

1 Bregistered devices
5 B / daynotifications sent
~58k / ssustained average
0.5–1 M / speak burst absorption
i
Back-of-envelope math

1,000,000,000 devices × 5 notifications/day = 5,000,000,000 notifications/day. 5,000,000,000 / 86,400 s ≈ ~58,000 notifications/second sustained average. Peak traffic (breaking news, flash sale, midnight campaign burst) can be 10–20× average, meaning your dispatch layer must comfortably absorb 500,000–1,000,000+ notifications/second in short bursts.

6.1 Where the Bottlenecks Actually Are

  • Targeting query fan-out: resolving “all users matching X” against a billion-row store must be paginated and index-backed, never a single unbounded scan.
  • Gateway connection limits: APNs and FCM both enforce their own rate limits and connection caps per provider identity — your worker fleet must respect these via token-bucket rate limiting per gateway credential, and shard credentials/apps to parallelize beyond a single limit.
  • Queue partitioning: Kafka partitions should be keyed to spread load evenly (e.g., by hash of device ID) while preserving enough ordering guarantees where needed (e.g., don’t reorder a “message deleted” push after “message received”).
  • Retry storms: naive immediate retries during a gateway outage can turn a transient blip into a self-inflicted DDoS on your own queue and worker fleet — exponential backoff with jitter is mandatory, not optional.

6.2 Scaling Techniques

HORIZONTAL

Stateless worker scaling

Dispatch workers behind an autoscaler keyed on queue lag, not just CPU, so scaling reacts to the actual backlog rather than incidental load.

BATCHING

Multicast & multiplexing

FCM supports multicast-style batch sends; APNs benefits from HTTP/2 multiplexing over pooled connections rather than one-connection-per-message.

SHARDING

Registry partitioning

Partition by hash of user or device ID across many database shards so no single shard becomes a hot spot during a large campaign targeting a popular segment.

WARM CONN

Pre-warm connection pools

Maintain a pool of already-authenticated HTTP/2 connections to APNs and OAuth-refreshed sessions to FCM so a traffic spike doesn’t pay connection-setup latency.

BACKPRESSURE

Rate-limit-aware pacing

The Fan-Out Service slows its enqueue rate when downstream dispatch queues grow past a threshold, rather than blindly pushing every job immediately.

DEDUPE

Coalesce near-duplicates

“Three people liked your photo” is one aggregated notification, not three sends — cuts dispatch volume and improves user experience simultaneously.

6.3 Capacity Planning Worked Example

Suppose product wants to send a single campaign notification to 200 million iOS devices within a 5-minute window (a common “big moment” launch pattern). That’s 200,000,000 / 300 seconds ≈ 666,000 notifications/second sustained for that window, entirely against a single gateway. Since a realistic per-connection throughput on APNs (accounting for stream concurrency limits and network round trips) might be on the order of a few thousand notifications per second per warm connection pool, you need to horizontally scale dispatch workers, each maintaining their own connection pools, to collectively reach that throughput — while making sure your provider credential’s overall rate allowance from Apple isn’t exceeded. This is exactly why the Fan-Out layer needs configurable pacing: product teams should be able to specify “deliver this over 5 minutes” versus “deliver this over 60 minutes,” and the system translates that into an enqueue rate that respects gateway ceilings, rather than dumping the entire audience onto the queue instantly and hoping downstream keeps up.

6.4 Read/Write Amplification From Retries

It’s worth explicitly modeling how retries multiply load. If your permanent-failure rate is 2% (dead tokens) and your transient-failure rate is 5% (timeouts, momentary rate limiting), and each transient failure is retried up to 3 times with backoff, your effective dispatch volume isn’t 100% of the audience — it’s closer to 100% + (5% × up to 3 retries) ≈ 115% of the logical audience size in worst-case gateway conditions. At a billion-device scale this is a meaningfully different capacity number than the naive “one send per device” estimate, and it’s a common source of under-provisioning surprises during incident postmortems.

?
What the interviewer may ask
  • How would you handle a single campaign trying to notify 200 million devices in under 5 minutes without tripping APNs/FCM rate limits?
  • Walk through what happens end-to-end if FCM starts returning 503s for 10 minutes during a big send.
  • How do you avoid a “thundering herd” of retries after a gateway comes back online?
  • How does retry amplification change your capacity planning numbers versus a naive one-send-per-device estimate?

6.5 CAP Theorem and the Token Registry

The device token registry is a good concrete example for reasoning about CAP theorem trade-offs in an interview. During a network partition between data-center regions, you must choose: reject writes to preserve consistency (CP), or accept writes on both sides and reconcile later, accepting temporary inconsistency (AP). For this specific workload, AP is almost always the right call. A device token being briefly stale after a partition heals is a low-severity problem — worst case, a handful of notifications go to an outdated token and bounce with a clean error that triggers re-registration on the next app launch. Rejecting writes during a partition, on the other hand, would mean actively refusing to register fresh tokens from devices that just installed the app, a much worse and more visible failure. This asymmetry — where the cost of temporary inconsistency is low but the cost of unavailability is high — is exactly the signal that should push you toward an AP-leaning, eventually-consistent design for this particular data store, even though other parts of the same platform (e.g., a payments ledger) might correctly make the opposite choice.

6.6 Cost Optimization at This Scale

At billion-device volumes, both compute and any vendor-side costs compound quickly, so a few disciplined habits pay for themselves. Deduplicating near-identical notifications before fan-out (e.g., collapsing “three people liked your photo” into a single aggregated notification instead of three separate sends) directly cuts dispatch volume. Right-sizing retry policy — capping total retry attempts and retry windows based on the actual half-life of most transient failures rather than retrying indefinitely — avoids paying for work that has a vanishingly small chance of ever succeeding. And regularly auditing the token registry to prune tokens for uninstalled apps (detectable from a pattern of repeated permanent-failure responses) keeps both storage costs and wasted dispatch attempts in check as the registry inevitably accumulates churn over years of operation.

07

High Availability & Reliability

Push delivery sits on the critical path for time-sensitive product experiences (2FA codes, fraud alerts, delivery updates), so the internal pipeline — everything up to the gateway handoff — needs to be designed as if it were a tier-0 system, even though the final hop is inherently best-effort once it leaves your control.

7.1 Reliability Techniques

  • Multi-AZ / multi-region deployment of the queue, workers, and token registry so a single data center failure doesn’t stall delivery.
  • Durable queueing (replicated Kafka topics) so an in-flight notification survives a worker crash — nothing lives only in a process’s memory.
  • Circuit breakers around each gateway integration: if APNs error rates spike, the circuit opens, jobs queue up instead of failing hard, and dispatch resumes once the gateway recovers.
  • Dead-letter queues for notifications that exhaust retries, with alerting and periodic reprocessing rather than silent loss.
  • Idempotency keys on every notification so retries — even across a full pipeline restart — never double-notify a user for the same logical event.

7.2 Failure Mode Table

FailureImpactMitigation
Token registry shard downTargeting stalls for affected user shardReplica promotion, read from standby, degrade to cached targeting where possible
APNs / FCM outageiOS or Android delivery halts platform-wideQueue backlog safely, circuit breaker, exponential backoff resume, no data loss
Worker fleet crash-loopDispatch throughput collapsesAutoscaling + health checks + fast rollback of bad deploys
Kafka partition skewUneven load, hot workersRebalance partitioning key, add partitions ahead of growth

7.3 Defining Your Actual SLA

Because the final hop is outside your control, a mature team writes its SLA in terms of what it can actually own: for example, “99.9% of accepted notifications will be handed off to the appropriate gateway within 30 seconds of trigger, for 99.95% of the time our own infrastructure is healthy.” This is a meaningfully different — and honest — commitment than “99.9% of notifications will be delivered to the device,” which no push system on earth can truthfully promise, since it depends on device power state, network connectivity, OS-level user settings, and vendor infrastructure that you don’t operate. Being explicit about this boundary in design docs and stakeholder conversations avoids a very common and painful mismatch between what product/leadership assumes the system guarantees and what it’s actually capable of guaranteeing.

?
What the interviewer may ask
  • Since you can’t control APNs/FCM uptime, what’s the boundary of what you’re actually responsible for guaranteeing?
  • How would you design idempotency so that replaying a Kafka partition after a crash never results in a duplicate push to the user?
  • How would you word an SLA for this system so it’s honest about what’s inside versus outside your control?

7.4 Disaster Recovery and Backup

Beyond day-to-day availability, plan explicitly for full regional loss. The token registry should be replicated cross-region with a documented, tested failover procedure — not just a theoretical capability. Because the registry is effectively irreplaceable (a lost token cannot be regenerated by your system; it can only be re-obtained the next time the affected device’s app happens to launch and re-register), backups and cross-region replication for this store deserve materially more rigor than for data that can be recomputed or re-derived, such as cached template renders or metrics rollups. Regularly exercising a full regional failover — actually cutting traffic over, not just simulating it on paper — is the only reliable way to know your disaster recovery plan works when you eventually need it for real, since untested runbooks are a well-known source of surprises during actual incidents.

7.5 Consensus and Coordination Concerns

Most of this system is deliberately designed to avoid needing strong distributed consensus, since consensus protocols (leader election, distributed locking) add latency and complexity that this workload’s tolerance for eventual consistency doesn’t require. The one place consensus-like coordination legitimately shows up is in Kafka consumer group partition assignment, where the broker coordinates which worker instance owns which partition — but this is handled by the underlying queue infrastructure, not something the application layer needs to reimplement. A useful interview instinct here is recognizing when a distributed system genuinely needs strong consensus (leader election for a singleton scheduler, for example) versus when eventual consistency and idempotency are sufficient and cheaper — this system leans heavily on the latter, and that’s a deliberate, defensible design choice rather than a shortcut.

08

Security

Push infrastructure is a high-value target: it can be used to spam users, exfiltrate targeting data, or — worst case — send convincing phishing-style notifications that impersonate the platform.

SECRETS

Credential protection

APNs p8 keys and FCM service account JSON files are long-lived secrets — store them in a managed secrets vault, rotate them, and scope access tightly to the dispatch workers that need them.

mTLS

Service-to-service auth

mTLS or signed service tokens between Composer, Targeting, and Fan-Out so a compromised internal service can’t directly enqueue arbitrary notifications.

SANITIZE

Payload validation

Prevent injection of malicious deep links or spoofed sender identity inside a notification payload; validate schema and escape any user-supplied strings.

CONSENT

Consent & privacy enforcement

The Targeting Service must respect opt-outs, regional consent regimes (GDPR-style marketing consent), and quiet-hours preferences as hard constraints, not best-effort.

LIMITS

Per-sender rate limiting

Prevents a single misbehaving internal team or compromised campaign tool from spamming users at scale.

LEAST PRIV

Token registry access

Read access to raw device tokens should be tightly restricted, since a leaked token set is effectively a list of addressable devices.

8.1 Defense in Depth for Campaign Tooling

A frequently underestimated attack surface is not external attackers but internal tooling misuse — a marketing team member with campaign-creation access accidentally targeting “all users” instead of a 10,000-person test segment. The mitigation isn’t purely technical; it’s a combination of guardrails: mandatory audience-size confirmation dialogs above a configurable threshold, staged rollout requirements for any campaign above a certain reach, and automatic circuit-breaking if a single campaign’s send rate exceeds a sane multiple of that team’s historical baseline. Treat “accidental mass notification” as seriously as you’d treat a security incident, because from the user’s perspective, an unwanted 3am notification blast is indistinguishable from a compromise.

?
What the interviewer may ask
  • What’s the blast radius if an attacker gets access to your APNs signing key?
  • How would you prevent an internal service bug from accidentally spamming every user in a region at 3am?
  • What non-technical guardrails would you add to campaign tooling to prevent human error at this scale?
09

Monitoring, Logging & Metrics

Because the final hop is opaque (you hand off to Apple/Google and can’t observe the device directly), your observability has to be unusually rigorous about what it can measure precisely.

9.1 Key Metrics

  • Enqueue-to-dispatch latency — time from a notification entering the queue to leaving your dispatch worker.
  • Gateway acceptance rate — percentage of sends accepted by APNs/FCM (a 200/success at the API level, distinct from actual device delivery).
  • Token invalidation rate — spikes here often reveal a bad app release, a mass reinstall event, or a registry sync bug.
  • Retry rate and DLQ volume — a leading indicator of gateway degradation before it becomes user-visible.
  • Queue lag — the gap between produced and consumed offsets, the earliest signal of a scaling problem.
  • End-to-end p50/p95/p99 latency from trigger to gateway acceptance (device-side delivery confirmation is generally not available, so this is your practical ceiling of visibility).

9.2 Logging & Tracing

Every notification should carry a trace ID stamped at composition time and propagated through targeting, fan-out, and dispatch, so a single notification’s journey can be reconstructed on demand — essential for debugging “why didn’t user X get notified” support escalations, which are common and time-sensitive at this scale.

?
What the interviewer may ask
  • Given that you can’t observe true device-side delivery, how do you build confidence that your system is actually working?
  • A support ticket says “I never got my 2FA push” — walk through how you’d trace that with your logging design.
10

Deployment & Cloud Architecture

This system is naturally suited to a cloud-native, multi-region deployment.

  • Stateless dispatch workers run as containerized services on an orchestrator (e.g., Kubernetes), autoscaled by queue lag and CPU.
  • Regional deployment close to major user populations reduces latency for the internal hops, though the final APNs/FCM handoff is inherently global and vendor-controlled.
  • Blue-green or canary rollouts for the dispatch workers, since a bad deploy that mishandles payload formatting can silently degrade delivery across an entire platform.
  • Infrastructure as code for queue topics, autoscaling policies, and secrets provisioning, so environment drift doesn’t silently break rate-limit configuration.
  • Feature flags around new notification types or targeting logic, allowing gradual rollout and fast kill-switch capability given how visible a bad notification blast is to users.
ADR-01 Dispatch worker deploys go through canary + feature-flag gates before wide rollout ACCEPTED
Context

A bad payload-formatting change or misconfigured rate limit shipped to 100% of dispatch workers at once could silently degrade delivery for hours across an entire platform, or trigger a mass-notification incident visible to every user simultaneously.

Decision

Every dispatch-worker change ships behind a feature flag, canaries first on a small percentage of traffic per platform, and gates wider rollout on acceptance rate, retry rate, and DLQ volume staying within baseline — with an automatic kill switch and rollback path if any of them regress.

Consequences

Slower rollout cadence for the dispatch layer specifically, in exchange for effectively eliminating self-inflicted mass-delivery incidents from deploys. Composer, Targeting, and Fan-Out keep their normal cadence.

?
What the interviewer may ask
  • How would you canary a change to your payload-building logic without risking a mass-notification incident?
  • Why might you deploy dispatch workers regionally even though APNs/FCM are globally managed services?
11

Databases, Caching & Load Balancing

11.1 Device Token Registry Design

This store needs to comfortably hold a billion-plus rows (accounting for multiple devices per user and historical churn) and support:

  • Fast point lookups by user ID (for transactional, per-user sends).
  • Efficient range / segment scans for campaign targeting (paginated, index-backed).
  • High write throughput for constant token refresh / invalidation churn.

A common real-world approach is a horizontally sharded key-value or wide-column store (the class of system that Cassandra, DynamoDB, or Bigtable represent) partitioned by user ID, with secondary indexes or a separate search-optimized index (e.g., a segment/attribute index) feeding the Targeting Service for campaign-style queries. A single relational database rarely scales cleanly to this row count and write volume without significant sharding work of its own.

11.2 Caching

  • Hot-token cache: an in-memory cache (e.g., a distributed cache layer) in front of the registry absorbs read load for frequently-notified users (highly active accounts, popular creators with large follower fan-out).
  • Template and rendering cache: compiled, localized templates are cached so the Composer doesn’t re-render identical content per recipient.
  • Rate-limit counters: per-gateway-credential token buckets are naturally implemented as cache entries with short TTLs and atomic increment operations.

11.3 Load Balancing

Layer-7 load balancers distribute inbound API traffic across Composer/Gateway instances; internally, Kafka’s partitioning acts as the load-balancing mechanism between Fan-Out and Dispatch — consumer groups scale horizontally simply by adding more worker instances that claim partitions.

?
What the interviewer may ask
  • Why is a single relational database usually the wrong choice for the device token registry at this scale?
  • How would you design the schema/partitioning to avoid a hot shard when a celebrity account with 50 million followers triggers a fan-out?
12

APIs & Microservices Design

The system is naturally decomposed into independently deployable services, each with a narrow contract.

API

Composer API

Accepts a notification request (template ID + audience descriptor + payload data), returns a notification ID immediately (async processing).

API

Device Registration API

Called by client apps on install/token-refresh to upsert the current device token — must be idempotent and cheap, since it’s called constantly.

API

Preference API

Exposes user consent, quiet hours, and channel preferences to both end users (settings screens) and internal services (targeting enforcement).

API

Delivery Status API

Internal/debugging API for querying the delivery ledger by notification ID or user ID.

These services communicate asynchronously wherever possible (via the event bus/queue) rather than synchronous request chains, so a slowdown in one stage doesn’t cascade into a timeout storm across the whole pipeline — a classic microservices resilience pattern.

?
What the interviewer may ask
  • Why should the Composer API return immediately rather than blocking until delivery completes?
  • Why is the Device Registration API one of the highest-traffic, most latency-sensitive endpoints in the whole system?
13

Design Patterns & Anti-Patterns

13.1 Patterns That Work

FAN-OUT

Queue-based fan-out

Decouples “decide to send” from “actually deliver,” enabling independent scaling and graceful backpressure.

CIRCUIT

Circuit breaker per gateway

Isolates APNs/FCM instability from your internal pipeline health.

IDEMPOTENCY

Idempotency keys everywhere

Makes at-least-once delivery safe under retries and crash recovery.

RATE LIMIT

Token bucket per credential

Respects each gateway’s published limits without manual throttling guesswork.

DLQ

Dead-letter with reprocessing

Turns silent failure into visible, actionable backlog with periodic replay.

TRACING

End-to-end trace IDs

A single ID stamped at composition and propagated through every hop so any user’s notification can be reconstructed on demand.

13.2 Anti-Patterns to Avoid

!
Anti-pattern: synchronous fan-out on the request thread

Having the Composer API block while iterating over millions of tokens and calling APNs/FCM inline turns a single API call into a multi-minute operation and defeats the entire point of async architecture.

!
Anti-pattern: treating gateway acceptance as delivery

A 200 response from APNs/FCM means “we accepted your request,” not “the user saw a notification.” Conflating these gives false confidence in dashboards.

!
Anti-pattern: unbounded immediate retries

Retrying failed sends instantly and repeatedly during an outage amplifies load exactly when the system is most fragile.

!
Anti-pattern: never pruning invalid tokens

A registry that never expires stale tokens accumulates dead weight, silently inflates campaign costs, and skews delivery-rate metrics.

?
What the interviewer may ask
  • What’s wrong with treating “APNs returned 200” as proof the user was notified?
  • Describe a real incident shape that unbounded retry-on-failure could cause during a gateway outage.
14

Best Practices & Common Mistakes

Do

  • Invalidate device tokens immediately on receiving a permanent-failure error code from APNs/FCM — don’t wait for a nightly batch job.
  • Separate transactional notifications (2FA, fraud alerts) from marketing/campaign traffic at the queue level, so a huge campaign burst never delays a time-critical transactional push.
  • Enforce quiet hours and consent as a hard filter in the Targeting Service, not as an optional client-side setting.
  • Load-test against simulated gateway rate limits and failure injection before every major campaign feature launch.

Common mistakes

  • Under-provisioning the token registry’s write capacity — token refresh churn from a billion devices is a constant, high-volume background write load that’s easy to underestimate during capacity planning.
  • Forgetting that iOS silent/background push has no delivery guarantee at all and building critical business logic (e.g., background data sync) that assumes it always arrives.
  • Not separating “notification accepted by gateway” from “notification delivered” in dashboards, leading teams to chase phantom reliability problems or, worse, miss real ones.

14.1 Operational Runbook Habits Worth Building Early

Teams that operate these systems well tend to converge on a small set of operational habits long before they’re forced to by an incident. They build a dashboard that shows acceptance rate, retry rate, and DLQ volume broken out per platform and per gateway credential, because a problem isolated to one app’s credential set looks very different from a platform-wide outage and calls for a different response. They set explicit alert thresholds on token-invalidation rate, since a sudden spike is one of the earliest signals of either a bad client release or a registry synchronization bug. And they rehearse — through game days or fire drills — what happens when a gateway is degraded but not fully down, since partial degradation (elevated latency, intermittent 5xx responses) is a much more common real-world failure than a clean, total outage, and is exactly the scenario where naive retry logic does the most damage.

?
What the interviewer may ask
  • Why should transactional and marketing notifications never share the same queue/priority lane?
  • What capacity-planning mistake most commonly bites teams building their first billion-scale token registry?
  • Why is partial gateway degradation often more dangerous operationally than a full outage?

14.2 Concurrency Considerations in Dispatch Workers

Within a single dispatch worker process, you’re typically running many concurrent send operations against a shared, bounded connection pool. This is a classic bounded-concurrency problem: too few concurrent in-flight requests underutilizes available gateway throughput, while too many risks exhausting connection pool slots, tripping gateway-side rate limits, or starving other work on the same process (like metrics reporting or health checks). The common solution is a semaphore or worker-pool pattern that caps concurrent in-flight sends per connection to a tuned value, combined with backpressure signals fed from the queue consumer — if the connection pool is saturated, the worker should slow its rate of pulling new jobs off the queue rather than buffering an unbounded number of pending sends in local memory, which risks an out-of-memory crash under sustained backpressure and would lose whatever work was queued in-process at the moment of the crash.

15

Real-World Industry Examples

MESSAGING

WhatsApp / Meta Messenger

Operate push infrastructure that fans out to APNs and FCM at massive scale for message delivery notifications, layering their own internal delivery-receipt system on top since gateway acceptance alone isn’t sufficient for a messaging product’s reliability bar.

MOBILITY

Uber

Relies on push for time-critical, high-priority transactional events (driver arriving, trip status changes) — a textbook case for strict separation of transactional versus promotional notification lanes.

STREAMING

Netflix

Uses push for personalized content recommendations and new-episode alerts — large-scale, segment-driven campaign targeting rather than purely transactional, per-user triggers.

E-COMMERCE

Amazon

Sends order and delivery status pushes at enormous volume, illustrating the need for robust retry/backoff design so peak shopping events (e.g., major sales days) don’t overwhelm the dispatch layer.

TRAVEL

Airbnb

Combines transactional (booking confirmations) and engagement (price-drop, new-listing) notifications — a real-world case for the Preference API and consent-aware targeting discussed earlier.

A pattern common across all of these companies is worth naming explicitly: the largest, most reliability-conscious platforms rarely trust APNs/FCM acceptance as their only signal of success. They layer an application-level acknowledgment on top — for a messaging app, this might be the recipient’s client explicitly confirming receipt over its own connection once the app is foregrounded or the socket reconnects; for a delivery-tracking app, it might be reconciling push delivery against whether the user actually opened the app and viewed the updated status shortly after. This “trust but verify” pattern is a direct consequence of the acceptance-versus-delivery distinction covered earlier, and it’s a strong signal in an interview that a candidate understands the system isn’t finished at the gateway handoff — it’s finished when the product outcome the notification was meant to drive actually happens.

i
Verification note

Specific product behaviors, feature names, and operational details are illustrative and drawn from general, publicly discussed industry patterns rather than any single company’s disclosed internal architecture; exact implementations vary by provider and should be verified independently for any real design or business decision.

?
What the interviewer may ask
  • Why might a messaging app like WhatsApp build its own delivery-receipt layer on top of APNs/FCM instead of trusting gateway acceptance alone?
  • How would Uber’s driver-arrival notification differ architecturally from Netflix’s new-episode alert in terms of priority and targeting design?
  • Why do the most reliability-conscious companies add an application-level acknowledgment layer on top of gateway acceptance?
16

Frequently Asked Questions

Q1

Can I guarantee a notification will always reach the device?

No. Both APNs and FCM are best-effort at the final hop — a device that’s powered off, offline for an extended period, or has notifications disabled at the OS level will not receive it, regardless of how reliable your internal pipeline is. Design for “reliable up to the gateway handoff,” not end-to-end guaranteed delivery.

Q2

Should I use FCM’s topic-based messaging instead of per-device fan-out?

Topics are efficient for coarse, non-personalized broadcasts (e.g., “all users subscribed to Sports news”), but they don’t support per-user personalization, fine-grained targeting, or iOS-native equivalents — most production systems use per-device fan-out as the general mechanism and topics only for specific broad-broadcast use cases.

Q3

How do I handle a device token that changes while a notification is in flight?

Treat token lookup as “resolve at dispatch time, not at enqueue time” where possible, and always process a token-invalidation error from the gateway as an immediate signal to update the registry — the very next send for that user will then use the fresh token.

Q4

What’s the difference between notification acceptance rate and delivery rate, and why does it matter?

Acceptance rate measures whether APNs/FCM accepted your API call; delivery rate (actual device receipt) is generally not observable by your backend at all. Conflating the two creates a false sense of reliability — track acceptance rigorously, but be explicit in dashboards and SLAs that it is not the same as confirmed delivery.

Q5

How do you prevent notification spam when many features want to notify the same user simultaneously?

A centralized Preference/Frequency-capping service that all producers must go through, enforcing per-user, per-channel send caps and prioritization rules, rather than letting each feature team fire independently.

Q6

How large should Kafka topics/partitions be sized for this kind of system?

Partition count should be driven by your target consumer parallelism (the number of dispatch workers you want reading concurrently) and by keeping per-partition throughput within comfortable broker limits, not by audience size alone. A common approach is to key partitions by a hash of device ID so load spreads evenly, and to over-provision partition count modestly up front, since increasing partitions later can disturb existing ordering guarantees for keyed messages.

Q7

Do iOS and Android need entirely separate codebases for the dispatch layer?

Not entirely — the Fan-Out Service, Targeting Service, and Retry Orchestrator can remain platform-agnostic, operating on an abstract “delivery job” concept. Only the innermost Dispatch Worker layer needs platform-specific wire-protocol code, which keeps the majority of the system’s logic shared and easier to maintain and reason about.

Q8

How do you test this system without spamming real users?

Maintain a pool of sandboxed test devices registered against APNs’ and FCM’s respective sandbox/testing environments, and run load tests against synthetic token sets with mocked gateway responses to validate throughput and backpressure behavior without ever touching production traffic or real user devices.

17

Summary & Key Takeaways

A billion-device push notification system is not one big problem — it’s a chain of well-scoped smaller problems, each with a known engineering pattern.

GATEKEEPERS

Two platforms, not a billion phones

You never talk directly to a device — you talk to APNs and FCM, two asymmetric gatekeeper platforms with different transports, auth models, payload limits, and failure semantics.

CENTER OF GRAVITY

Registry is the operational heart

The Device Token Registry is the operational heart of the system; keeping it fresh via immediate invalidation on gateway errors is non-negotiable at scale.

ASYNC

Queue-based fan-out, always

Fan-out must be asynchronous and queue-based, never synchronous on the API request thread, to survive billion-device audiences and traffic spikes.

RELIABILITY

Break the retry storm

Circuit breakers, exponential backoff with jitter, idempotency, and DLQs protect both your system and the external gateways from cascading failure during outages.

Key takeaways

  • Two gatekeepers, not a billion endpoints. Design the entire dispatch layer around APNs’ and FCM’s asymmetric protocols, auth, and priorities.
  • The token registry is your center of gravity. Real-time invalidation on gateway errors is a hard requirement, not a housekeeping nice-to-have.
  • Fan-out is asynchronous, always. Queue-based fan-out with backpressure is the only way to survive billion-device audiences and social-moment bursts.
  • Reliability engineering is non-optional. Circuit breakers, exponential backoff with jitter, idempotency keys, and DLQs protect the system and the gateways alike.
  • Acceptance is not delivery. Build observability and SLAs around what you can honestly measure — not what a naive dashboard might imply.
  • Separate transactional from promotional lanes. A 2FA push must never wait behind a marketing campaign, ever.
  • Scale is a data-partitioning and backpressure problem. Shard the registry, partition the queues, and respect each gateway’s rate limits — the compute is secondary.
  • Be honest about the boundary. Your responsibility ends at gateway handoff. Say so in the SLA, the runbook, and the design review.

If you take one mental model away from this whole design, let it be this: a billion-device push notification system is not one big problem, it’s a chain of well-scoped smaller problems — resolve an audience without blowing up memory, queue work durably, respect two very different external rate limits, retry safely without amplifying an outage, and keep a massive token store honest in near real time. Each of those sub-problems has a known, well-understood engineering pattern. The skill being tested, in an interview or in production, is recognizing which pattern fits which sub-problem, and being honest about the boundary of what your system can actually promise once a notification leaves your infrastructure and enters Apple’s or Google’s.

i
Interview takeaway

Strong candidates walk into this question with two things: a mental separation between “my pipeline” and “the gatekeeper platforms,” and a clear-eyed understanding of what push can and cannot guarantee. Everything else — sharding, queues, rate-limit strategies — hangs off those two foundations.

Leave a Reply

Your email address will not be published. Required fields are marked *