Communication
Design WhatsApp
01
Scope and guarantees
Scope and guarantees
- Send text and media messages one to one and in groups of up to 1,024 members.
- Messages wait on the server for recipients who are offline and arrive when they reconnect.
- Show three states per message: sent to server, delivered to a device, read.
- One account can be linked to a phone plus up to four companion devices, each of which receives every message.
- Typing indicators and an optional "last seen" time.
- Photos, voice notes, video and documents up to 2 GB.
- Content is end-to-end encrypted; the service never sees plaintext.
- Between two online users in the same region, median delivery under 150 ms and p99 under 500 ms.
- Once the server acknowledges a message, it is never lost, even if a data centre fails.
- Each device shows each message exactly once, in a consistent order within a chat.
- Sending keeps working during partial outages; presence and typing can degrade first.
- Battery and data use on phones must stay low, so the protocol is compact and keepalives are rare.
- Undelivered messages are kept for 30 days, then dropped.
Ask whether the server keeps chat history or only holds messages until delivery. A store-and-forward design that deletes after delivery is far smaller and fits end-to-end encryption naturally. A server-side history, like Slack or Telegram cloud chats, turns this into a search and storage problem. Also ask about multi-device support, because it multiplies every delivery by the number of linked devices.
02
Load estimates
Load estimates
Our assumptions: 900 million daily users, each sending about 75 messages a day. Thirty percent of messages go to groups, which average 12 members. Users have 1.3 active devices on average. At the evening peak, about a quarter of daily users hold an open connection at the same moment.
| Quantity | Working | Result |
|---|---|---|
| Messages sent per day | 900M x 75 | 67.5B |
| Send rate | 67.5B / 86,400 s, peak 2x | about 780,000 / s, peak 1.56M / s |
| Recipient copies per day | 70% x 1 + 30% x 11 other members | about 270B |
| Device deliveries per day | 270B x 1.3 devices | about 350B, 4M / s average |
| Receipts per day | one delivered plus one read per recipient copy, batched | roughly as many as deliveries |
| Open connections at peak | 900M x 25% | about 225M |
| Connection hosts | 225M / 400,000 sockets per host, plus 50% headroom | about 850 hosts |
| Encrypted envelope size | text plus headers and signatures | about 350 B |
| Offline backlog at any moment | 8% of deliveries wait about 5 h: 350B x 0.08 x 5/24 | about 5.8B envelopes, 2 TB |
| Media uploaded per day | 5% of sends x 220 KB average | about 740 TB, kept 30 days |
A well-tuned host with an event loop or a lightweight-process runtime can hold several hundred thousand mostly idle TCP connections. The binding limits are memory per connection (TLS state and buffers, a few tens of kilobytes) and how fast the host can accept reconnections after a failure, not CPU. Plan capacity around the reconnect case.
Groups and devices multiply traffic. One message to a 12-person group where everyone has 1.3 devices produces about 14 deliveries and up to 28 receipts. Sending is under a million per second; delivering is four million. Design for deliveries.
03
Protocol and API
Protocol and API
Chat traffic does not use REST. Each device holds one long-lived, TLS-encrypted connection and exchanges small binary frames in both directions. The frames below are shown as JSON for readability. Only media upload and account management use ordinary HTTPS.
04
High-level design
High-level design
flowchart LR
A["Sender phone"] --> LB["TCP load balancer"]
LB --> G1["Connection gateway A"]
G1 --> RT["Message router"]
RT --> GS[("Group membership")]
RT --> IB[("Per-device inbox store")]
RT --> REG[("Session registry")]
RT -->|"recipient online"| G2["Connection gateway B"]
G2 --> B["Recipient devices"]
RT -. "recipient offline" .-> PN["Push notifier"]
PN --> APNS["APNs or FCM"]
A -->|HTTPS| MED["Media service"]
MED --> BLOB[("Blob store")]Clients connect through a layer-4 load balancer to a connection gateway, a host whose only job is to hold sockets, terminate TLS and move frames. When a frame arrives, the gateway hands it to a stateless message router. The router expands the chat into a list of recipient devices, appends a copy of the envelope to each device's inbox, then acknowledges the sender. Only after that does it try to deliver.
Delivery needs to know where each recipient device is connected right now. That lives in the session registry, a replicated in-memory map from device id to gateway id, updated whenever a device connects or disconnects. If the device is online, the router forwards the frame to that gateway, which writes it down the socket. If it is offline, the envelope simply waits in the inbox, and the router asks the push notifier to wake the phone. When the device reconnects, its gateway drains the inbox from the device's last acknowledged sequence number.
Holds roughly 400,000 sockets per host. Keeps no message state that matters: if it dies, clients reconnect elsewhere and nothing is lost because the inbox holds the truth.
About 290M device entries, each a few dozen bytes, so it fits in memory on a sharded cluster. Entries carry a connection epoch so a stale record from a dead gateway can be detected.
Stateless and horizontally scaled. Deduplicates on sender and client_msg_id, expands groups, writes inbox copies and forwards to gateways. Partitioned by chat id so one chat's messages are handled in order.
A wide-column store keyed by device id with the inbox sequence as the clustering key. Writes are appends, reads are short range scans, and acknowledged entries are deleted, so the table stays small.
Group id to member list and each member's device list, cached heavily. Changes to membership are themselves messages in the group so every device sees them in order.
Stores public identity keys and one-time prekeys for every device, so a sender can start an encrypted session with a device that is offline. It never sees private keys.
05
Deep dive: finding the recipient and not losing anything
Deep dive: finding the recipient and not losing anything
First, when a message arrives on gateway 17, how do you know the recipient's phone is on gateway 412? Second, how do you guarantee it arrives exactly once on every device, given that phones drop off networks constantly? The registry answers the first, and the inbox plus acknowledgements answer the second.
The session registry
Gateways are stateful in one important way: a socket lives on exactly one host. So routing is a lookup, not a broadcast. When a device connects, its gateway writes device_id → (gateway_id, epoch) into the registry, and deletes it on clean disconnect. The router reads that entry for each recipient device and sends the frame to the right gateway over an internal RPC connection that is kept open between every router and every gateway.
The tricky part is stale entries. If a gateway crashes, its entries remain until something removes them, and messages forwarded to it are lost in transit. That is acceptable only because forwarding is an optimisation on top of the inbox, not the delivery mechanism. The message was already in the inbox before forwarding began, so whichever gateway the device reconnects to will drain it. The epoch number stops a late disconnect from an old connection overwriting a newer connect from the same device.
flowchart TD
S["Router receives msg"] --> D{"Seen this client_msg_id?"}
D -->|yes| ACK["Re-send server_ack, stop"]
D -->|no| W["Append to each device inbox"]
W --> ACK2["server_ack to sender"]
ACK2 --> L{"Registry: device online?"}
L -->|yes| F["Forward to its gateway"]
L -->|no| P["Send silent push"]
F --> R{"Device sends delivered receipt?"}
R -->|yes| DEL["Delete inbox entries up to seq"]
R -->|"no, timeout"| KEEP["Keep in inbox for next connect"]
P --> KEEP
KEEP --> RC["Device reconnects and drains from last seq"]
RC --> RAt-least-once delivery, exactly-once display
There is no way to deliver exactly once across an unreliable network. A phone can receive a frame and die before sending its receipt, and the server has no way to know which happened. So we deliver at least once and remove duplicates at both ends. The sender attaches a client message id and retries with the same id until it sees the server ack; the router keeps a short-lived dedup set of sender and id, so a retry produces no second copy. Each device inbox hands out a strictly increasing sequence number, and the device keeps the highest number it has stored. A redelivered frame with a lower or equal number is dropped on arrival.
Acknowledgements are cumulative. A device that drains 300 queued messages after a flight sends one receipt saying "delivered up to 558,514", and the server deletes that range in one operation. That keeps receipt traffic proportional to batches rather than messages, which matters when receipts are as numerous as deliveries.
Groups and end-to-end encryption
With end-to-end encryption, the server cannot encrypt anything on the sender's behalf. For one-to-one chats, the sender's phone encrypts the message separately for each of the recipient's devices and for the sender's own other devices. For groups that would mean dozens of encryptions per message, so groups use sender keys: each member shares a symmetric key with the group once, through pairwise encrypted messages, then encrypts each group message one time. The router fans out the same ciphertext to every member device. When someone leaves, every remaining member rotates their sender key so the departed member cannot read new messages.
Each chat is processed by one router partition at a time, so messages from the same sender enter the inbox in send order. Across senders, the order is the order the router saw them. Clients display by that server order, not by phone clocks, which can be minutes off. Everyone in a group sees the same order, which is what people actually notice.
06
Trade-offs and choices
Trade-offs and choices
The server can push instantly, and both directions share one socket with a compact binary framing. Keepalives every few minutes are enough to hold NAT mappings open. The cost is a stateful gateway tier and careful handling of reconnects.
Lowest latency and batteryWorks through any proxy, which helps on locked-down corporate networks. Each poll carries full HTTP headers and reconnects regularly, so it uses more data and adds a round trip to every server push.
Only when sockets are blockedThe server holds only the undelivered backlog, about 2 TB, instead of every message ever sent. It fits the privacy model, since the server never needs history. Backups and new-device history move between the user's own devices, encrypted.
Small, private, cheapGives instant history on any new device and server search, as cloud chat apps do. It also means storing tens of petabytes a year and holding data that the encryption model is meant to keep off the server.
Conflicts with E2EE goalsRetries are safe because ids and sequence numbers make them harmless. No distributed transaction between server and phone is needed, which is good, because one is not possible.
Achievable and correctRequires the device and server to agree atomically on whether a frame was received. With phones that vanish mid-handshake, any attempt reduces to at-least-once with extra round trips.
Not achievable over this networkPartition by device id, cluster by sequence. Appends and range deletes are exactly its strengths, it spreads 4 million writes per second across many nodes, and replicas in several zones keep acked messages safe.
Matches the access patternWorks and gives transactions you mostly do not need. Heavy delete churn creates bloat and vacuum pressure, and resharding a queue table at this write rate is painful.
Viable at smaller scale"Last seen" and typing signals are best effort and can be stale or dropped. They go over a separate, lossy path, so an overloaded presence system never slows messages.
Messages firstThe server ack waits for a quorum write in the inbox store. That costs a few milliseconds, and it is the line between "tick shown" and "message possibly lost".
Never ack before durable07
Failure scenarios
Failure scenarios
Every phone on that host notices within a keepalive interval and tries to reconnect. If they all retry at once, the load balancer sends a burst of TLS handshakes to the remaining hosts, and the CPU cost of those handshakes can knock over a second host, then a third.
Clients reconnect after a random delay with exponential backoff, TLS session resumption makes handshakes cheap, and each gateway caps how many new connections it accepts per second. Messages are safe in inboxes throughout, so slow reconnection costs latency, never data.Routers cannot tell where a slice of devices is connected. Live forwarding to those devices stops, though the devices still have healthy sockets.
Fall back to inbox plus a nudge: write the message to the inbox as always, then ask every gateway to check whether it holds that device, or simply wait for the device's periodic inbox poll, which runs every minute when the registry is degraded. Rebuild the shard by having gateways re-register their live connections.A 1,000-member community group gets a burst of 50 messages a minute during a match. Each message turns into about 1,300 device writes and as many receipts, all keyed to one chat partition.
Cap group size, batch inbox writes per destination node, and aggregate read receipts on the server for large groups so senders see counts, not per-member ticks. Very large broadcast use cases belong in a separate channels product with a pull model.The mobile OS push provider starts rejecting or delaying notifications during a holiday spike. Offline users do not wake up and messages sit unseen for longer.
Collapse pushes per device so only one wake-up is pending at a time, prioritise one-to-one chats over groups, and retry with backoff. The messages are already stored, so a delayed push delays them without losing them.A cross-ocean link fails. Users homed in the other region cannot receive messages routed through the broken path, and senders keep retrying.
Home each device's inbox in one region with an asynchronous replica in another. Messages to unreachable regions are queued at the sending side and replayed when the link returns; dedup ids make the replay safe.08
Mistakes interviewers look for
Mistakes interviewers look for
If the router acks first and crashes before writing the inbox, the sender sees a tick for a message that no longer exists anywhere.
With 850 gateway hosts, every message becomes 850 internal messages so that one of them can reach the right socket. Internal traffic would dwarf user traffic.
Phone clocks drift and can be changed by the user, so a reply can appear above the message it answers.
09
Interview advice
Interview advice
Count deliveries, not sends. Show how groups and multiple devices turn under a million sends a second into several million deliveries. It justifies the inbox design and the receipt batching.
Draw the session registry. Many candidates draw gateways and never say how a message finds the recipient's host. Making the registry explicit, with an epoch for stale entries, is a clear signal.
Make the inbox the source of truth. Then gateway crashes, registry faults and dropped pushes become latency problems, not data loss. Say this sentence out loud.
Explain the three ticks precisely. One tick means durable on the server, two means a device stored it, blue means the user opened the chat. Map each to the exact event in your design.
Say "at least once, with dedup" without hedging. Then show the two ids that make it safe: the client message id and the per-device inbox sequence.
Know enough about end-to-end encryption. You do not need the full protocol, but explain per-device encryption, prekeys for offline recipients and sender keys for groups, and how that shapes server fan-out.
Plan for the reconnect storm. The worst moment for this system is not peak traffic but a host or region coming back. Backoff with jitter and admission limits per gateway are the expected answers.
Keep presence separate. Treat last seen and typing as lossy and rate-limited. If they share a path with messages, an interviewer will ask what happens when presence overloads it.
10
How the design grows
How the design grows
A single process keeps a map of user to socket and writes each message to a database table before forwarding. It works because every connection is on the same machine, so no routing is needed.
One machine can no longer hold every socket. Split connection handling from message logic, add the session registry, and switch to per-device inboxes with acknowledgements and deletion.
The relational messages table cannot keep up with delete churn. Move inboxes to a wide-column store, partition routers by chat, batch receipts and add end-to-end encryption with a key directory.
Users are homed in regions near them, companion devices get their own identities and inboxes, and points of presence close to users terminate connections to cut latency on mobile networks.
Watch and read
Try next
Free to read · better with landed
Whiteboard this with landed
landed runs it as a live system design round on the whiteboard and grades your trade-offs.


