System Design of WhatsApp: Messaging at Billion-User Scale

"Design WhatsApp" (or "design a chat system") is arguably the most frequently asked system design interview question — and one of the most instructive, because a messaging system stresses different muscles than a feed or a streaming service: long-lived connections, per-message delivery guarantees, ordering, and privacy. This walkthrough covers the architecture an interviewer expects you to reach, and the trade-offs they'll probe along the way.
If you're new to these interviews, start with our system design framework and the prerequisites; this post assumes those fundamentals.
Requirements
Functional:
- One-to-one messaging with delivery/read receipts (the double ticks)
- Group chats (hundreds of members, not millions)
- Online/last-seen presence and typing indicators
- Media sharing (images, video, documents)
- Offline delivery: messages sent while you're unreachable arrive when you return
Non-functional:
- Scale: billions of users, tens of billions of messages per day
- Latency: message delivery should feel instant (<100ms server-side)
- Delivery guarantee: at-least-once, with deduplication for exactly-once appearance
- Privacy: end-to-end encryption — servers must route what they cannot read
- Availability over consistency where they conflict: a chat app must keep accepting messages
State these explicitly in an interview — the E2E-encryption constraint in particular reshapes several later decisions, and mentioning it early signals you understand the domain.
Connections: the defining constraint
HTTP request/response doesn't fit chat: the server must push messages to recipients. The standard answer is a persistent WebSocket connection from each online device to a chat server.
The numbers make this interesting. With ~500M concurrent users and servers comfortably holding ~100k–1M idle connections each (mostly-idle connections are cheap; it's memory and file descriptors, not CPU), you need a fleet of hundreds to thousands of chat servers, plus:
- A connection registry — "user U's device is on chat server 42" — in a fast distributed store (think Redis-style, sharded by user ID). Every message routing decision reads this.
- Sticky routing at the gateway so reconnects land predictably, with the registry updated on every connect/disconnect.
- Heartbeats to detect dead connections and drive presence (below).
Message flow, step by step
Alice sends "hi" to Bob:
- Alice's device sends the message over her WebSocket to her chat server, with a client-generated ID for idempotency.
- The chat server persists the message durably to the message store and acks Alice (single tick). Persist-then-ack is the crux: acking before persisting risks silent loss; delivering without persisting risks loss on crash mid-flight.
- The server looks up Bob in the connection registry.
- Bob online: forward to Bob's chat server, which pushes down Bob's WebSocket. Bob's device acks; the ack propagates back to Alice (second tick).
- Bob offline: the message sits in Bob's per-user inbox (a message queue keyed by recipient). When Bob reconnects, his chat server drains the inbox in order. A push notification (via APNs/FCM) tells Bob's phone to wake up.
- Read receipts are just tiny system messages flowing the same path in reverse.
Ordering: guarantee order per conversation, not globally — a per-conversation monotonic sequence number assigned at write time is enough, and clients render by sequence, deduplicating on message ID. Global ordering is a classic over-engineering trap; say so.
The message store
Write-heavy (tens of billions/day), append-only, always queried by conversation: this is a textbook fit for a wide-column store (Cassandra/HBase family, or a sharded log) partitioned by conversation_id and clustered by sequence number. Hot reads ("recent messages in this chat") hit one partition in order.
Two properties worth volunteering:
- Retention is small. WhatsApp famously deletes messages from servers once delivered — the server-side store is a delivery buffer, not an archive. History lives on devices (and in client-side backups). This slashes storage and sidesteps a privacy liability, and it's only possible because of the delivery-receipt design.
- Media is separate. Blobs go to object storage behind a CDN: the sender uploads once, the message carries an encrypted pointer + key, recipients download on demand. Media never transits the chat servers.
Group messaging
Groups change the write path: one inbound message fans out to N recipients. For WhatsApp-sized groups (≤ ~1k members), fan-out at the server on write is right: the group service resolves membership, then enqueues one copy per recipient through exactly the one-to-one pipeline above (same inboxes, same receipts, same offline path — no special machinery).
Contrast this with Twitter-style fan-out to millions of followers, where write-time fan-out collapses and you shift to fan-out-on-read for celebrities. Groups capped in the hundreds are precisely what keeps chat fan-out tractable — a comparison interviewers reward.
Presence and typing indicators
Presence is high-volume, low-value data: nobody needs "last seen" to be perfectly fresh, and it must not compete with message delivery for resources.
- Heartbeats update a TTL'd entry in a presence store; missing heartbeats let it expire to "last seen at T".
- Fan out presence lazily — only to users currently viewing the conversation, throttled.
- Typing indicators are fire-and-forget ephemeral events: no persistence, no retries, drop on any pressure. Explicitly deprioritising them is a maturity signal in interviews.
End-to-end encryption
With the Signal protocol, encryption happens on devices; servers route ciphertext. What survives on the architecture:
- Key distribution: a key server hands out public "pre-key" bundles so senders can establish sessions with offline recipients.
- Multi-device: each device has its own keys; a message is encrypted per recipient device, multiplying the fan-out — this is why "how many devices per user?" is a real capacity question.
- What the server still sees: metadata — who messaged whom, when, message sizes. Delivery, receipts, ordering and abuse-rate-limiting all work on metadata alone; content search server-side becomes impossible, which is why search is a client-side feature.
You won't be asked to derive the cryptography; you will be asked what E2EE breaks. This list is the answer.
Putting it together
Client apps ⇄ gateways (sticky) ⇄ chat servers ⇄ {connection registry, per-user inboxes, message store, group service, key server, presence store}, with object storage + CDN for media and APNs/FCM for wake-ups. Every box shards by user or conversation ID; no single point sees more than its shard.
Failure modes to raise before the interviewer does: a chat server dies (clients reconnect elsewhere; registry updates; inboxes replay — nothing lost because of persist-then-ack); a registry shard is stale (delivery falls back to the inbox + push path); a region fails (users are homed to regions, cross-region messages flow through inter-region queues with the same at-least-once semantics).
How this plays in an interview
A strong 45-minute pass: requirements with numbers (5 min) → connection layer and registry (10) → one-to-one flow with persist-then-ack, receipts, offline (15) → message store choice (5) → groups via write fan-out (5) → E2EE implications and failure modes (5). Depth beats breadth: delivery semantics and ordering, argued crisply, outscore a diagram with twenty boxes.
Preparing for system design rounds? Work through our other deep dives — Uber, Twitter, and Netflix — or get matched with a coach who runs mock system design interviews.