The whole design, in writing
Every step of the build above, written out: the problem each piece solves, the option that was taken and the ones that were not, the numbers, and how it fails in production.
The big idea
What is WhatsApp, really?
Strip away the brand and WhatsApp does one magical thing: get a message from your phone to anyone else's — instantly, reliably, anywhere on Earth — and tell you the moment they've read it.
That one sentence hides brutal questions. How do two phones that can't talk directly reach each other? What if the other person is offline? How do you do this for 2+ billion people at once — and prove delivery with those little ✓✓ ticks?
What the new pieces do
- Alicealice
- The sender. Types a message and expects it to reach Bob instantly — and to know the moment he's read it.
- Bobbob
- The receiver. Might be online, offline, or on another continent. The whole system exists to reach him.
Step 1 · The skeleton
Two phones, one server
Alice's phone and Bob's phone can't message each other directly — phones hide behind carrier networks, change IPs, and fall asleep. They need a middleman both can reach.
Two phones behind carrier networks, changing IPs, falling asleep — they can’t talk directly. How do they exchange messages?
Rejected. Peer-to-peer: the phones connect directly to each other.
Phones hide behind NAT/carrier networks, change IPs, and sleep — a direct connection rarely works and breaks constantly. You need a reliable middleman both can always reach.
The call. A server in the middle that stores then forwards each message.
Alice sends to the server, which holds a copy in a Message Store and forwards to Bob. Store-and-forward — holding each message until the recipient has it — is what lets messaging survive flaky networks and sleeping phones.
Rejected. Email the message to the recipient.
Email is slow, not real-time, and a different system. Chat needs an instant relay that tracks delivery — a server that stores and forwards.
Put a server in the middle. Alice sends her message to the server; it saves a copy in a Message Store and forwards it to Bob. The classic client → server → client relay — the spine of every chat app.
What the new pieces do
- Relay Serverbackend
- The single front door. Routes each message deeper into the system. Becomes an API Gateway + load balancer at scale.
- Message Storestore
- Durable storage holding a message only until it's delivered to every recipient — then it's gone. WhatsApp keeps no chat history.
Step 2 · Always-on
Reach Bob the instant it lands
The server is holding Alice's message — but Bob's phone isn't asking for it. Polling "any messages for me?" every second, for billions of phones, would melt the planet and still feel slow.
The server holds Alice’s message, but Bob’s phone isn’t asking for it. How does it arrive instantly?
Rejected. Bob’s phone polls "any messages for me?" every second.
Billions of phones polling every second is a self-inflicted DDoS that still feels laggy (you wait a poll interval). The server should push the instant a message arrives.
Rejected. Send Bob a push notification to open the app.
Push notifications wake a closed app, but in-chat delivery needs a live two-way pipe for messages, typing and receipts — not a nudge to reopen. Keep a persistent connection while online.
The call. A persistent WebSocket per phone; the server pushes on arrival.
An always-open socket flips the model — the server speaks first, delivering the moment a message lands, with no polling. One warm pipe carries messages, typing dots and receipts both ways.
Keep a persistent connection — a WebSocket — open between every phone and a Connection Server. Now the server can push the moment a message arrives. No refreshing, no polling; the pipe stays warm as long as you're online.
What the new pieces do
- Connection Serverrealtime
- Holds a permanent open socket to each online phone, so the server can push a message the instant it arrives — no refreshing.
Step 3 · The address book
Which server is Bob on?
At scale there isn't one connection server — there are thousands. Alice's socket lands on server #14; Bob's on server #882. Server #14 holds the message but has no idea where Bob is.
There are thousands of connection servers. Alice is on #14, Bob on #882. How does #14 deliver to Bob?
Rejected. Server #14 broadcasts the message to all connection servers.
Shouting to thousands of servers per message is hugely wasteful and doesn’t scale. You want to deliver to exactly one place — the server holding Bob’s socket.
The call. A Session Service maps user → connection server; look Bob up and route.
When Bob connects he registers his spot; to deliver, #14 asks "where’s Bob?", finds #882, and routes straight there. Service discovery turns "shout to everyone" into "deliver to exactly one place" — a fast in-memory registry (Redis).
Rejected. Pin every user to a fixed connection server forever.
Phones roam, reconnect and load-balance across servers constantly, so a fixed assignment breaks the moment Bob reconnects elsewhere. The registry must update on every connect/disconnect.
A Session Service keeps a live registry: user → connection server. When Bob connects he registers his spot. To deliver, server #14 asks "where's Bob?", finds server #882, and routes the message straight there.
What the new pieces do
- Session Serviceregistry
- A registry mapping each online user to the exact connection server holding their socket — so a message knows where to go.
Step 4 · When Bob's asleep
Deliver to someone offline
Bob's phone is off, or he's on the subway with no signal. There's no socket to push to. The message can't just vanish — and Alice shouldn't have to wait or retry.
Bob’s phone is off or has no signal — there’s no socket to push to. What happens to the message?
Rejected. Alice’s phone keeps retrying until Bob comes online.
Making the sender retry for hours/days drains Alice’s battery and blocks her on a sleeping recipient. The server should absorb the wait, not the sender.
Rejected. Drop the message and ask Alice to resend later.
Silently dropping messages is unacceptable — delivery must be reliable. The message has to be held safely until Bob reappears.
The call. Queue it in Bob’s per-user Inbox Queue; drain on reconnect, then delete.
If the registry shows no live connection, the message waits in Bob’s mailbox; the moment he reconnects the server drains the queue to his phone, confirms delivery, and deletes the stored copy. The queue decouples send from receive.
If the registry shows Bob has no live connection, drop the message into his Inbox Queue — a per-user mailbox. The moment Bob reconnects, the Connection Server drains his queue to the phone, confirms delivery, and the stored copy is deleted.
- 100B+messages / day
- 2B+users
- daysa queue can wait
What the new pieces do
- Inbox Queuequeue
- When the recipient is offline, the message waits here. On reconnect the queue drains to their device, then the copy is deleted.
Back of the envelope
- no live socket ⇒ Inbox Queue
- the server absorbs the wait, not the sender
- queue waits seconds…days
- drains on reconnect, then deletes
- 100B+ msgs/day, 2B+ users
- most delivered live, the rest queued
Step 5 · The famous ticks
✓ sent, ✓✓ delivered, ✓✓ read
Alice wants to know what happened to her message. Did it leave her phone? Reach Bob's? Did he actually read it? Silence breeds anxiety.
Every hop sends a tiny acknowledgement back. Reaches the server → one ✓. Delivered to Bob's device → two ✓✓. Bob opens the chat → his phone reports it and the ticks turn blue. A little state machine riding the same socket.
Step 6 · Group chat
One message, many phones
Alice posts in a 50-person group. We can't make her phone send 50 copies — that's slow and drains her battery. But everyone must get it, in order.
Alice posts in a 50-person group. How does everyone get it, in order, without her phone sending 50 copies?
The call. She sends one message; a Group Service fans it out to each member.
One message tagged with the group id; the Group Service looks up members and fans out a copy to each one’s connection (or Inbox Queue if offline). One send in, N deliveries out, all server-side — fan-out on write.
Rejected. Her phone sends a copy directly to all 50 members.
Client-side fan-out is slow and drains her battery, and her phone doesn’t know everyone’s connection server. Fan-out belongs on the server, which knows where each member is.
Rejected. Everyone polls a shared group inbox for new messages.
Polling a shared inbox reintroduces the lag and load you removed with push, and complicates per-member delivery/receipts. Push a fanned-out copy to each member instead.
She sends one message tagged with the group id. A Group Service looks up the members and fans out a copy to each one's connection — or their Inbox Queue if offline. One send in, N deliveries out, all handled server-side.
What the new pieces do
- Group Serviceservice
- Expands a group into its member list and fans the message out — one send becomes N deliveries.
Step 7 · Photos & video
Don't push a video down the socket
A 50 MB video can't ride the same lightweight pipe as "lol 😂". Pushing big blobs through connection servers would clog the real-time path for everyone.
A 50 MB video can’t ride the same lightweight pipe as "lol 😂". How do you send media?
Rejected. Stream the video through the connection server like a big message.
Pushing big blobs through the real-time connection servers clogs the messaging path for everyone. Text and media need different roads.
Rejected. Split the video into chunks and send them as many chat messages.
Cramming a video into the message pipeline as chunks still loads the real-time path and the message store with huge data. Media should bypass the chat pipeline entirely.
The call. Upload once to blob storage + CDN; the chat carries a pointer + thumbnail + key.
Media uploads once to blob storage fronted by a CDN near each viewer; the message carries only a small pointer, thumbnail and decryption key, and Bob fetches the blob from the nearest edge. Separate the data plane from the control plane.
Upload media once to blob storage, fronted by a CDN that caches it near each viewer. The chat message carries only a small pointer + thumbnail + decryption key. Bob's phone fetches the blob straight from the nearest CDN edge.
What the new pieces do
- Blob + CDNstore
- Photos and videos skip the message pipeline: uploaded once to blob storage, cached on a CDN near you, shared as a link + thumbnail.
Back of the envelope
- 50 MB video ≠ "lol 😂"
- different roads: CDN vs the socket
- upload once ⇒ blob + CDN
- cached near each viewer
- chat carries pointer + thumb + key
- tiny control message, big data elsewhere
Step 8 · Trust & scale
2 billion phones, zero peeking
Two hard problems at once: how do a handful of servers hold billions of live connections — and how do you promise that not even WhatsApp can read people's messages?
Connection servers are tuned to hold ~2 million sockets each, so a modest fleet covers the planet; Presence rides the same socket via heartbeats. And every message is end-to-end encrypted — the server only ever relays ciphertext it cannot decrypt.
- ~2Msockets / server
- 256-bitE2E keys
- 0msgs server can read
What the new pieces do
- Presenceservice
- Tracks who's online, last-seen and typing — using lightweight heartbeats over the same socket.
Back of the envelope
- ~2M sockets per server
- a modest fleet covers the planet
- E2E: keys live on devices only
- the server relays sealed ciphertext
- 0 messages the server can read
- privacy by design, not promise
You did it
You just designed WhatsApp.
Everything you assembled, in order
- Store-and-forward server — the relay spine between two phones.
- Persistent WebSockets so the server pushes messages instantly.
- A Session registry routes each message to the right connection server.
- Per-user Inbox Queues hold messages for offline recipients.
- Acknowledgements drive the ✓ / ✓✓ / blue-tick delivery receipts.
- A Group Service fans one message out to every member.
- Media via blob storage + CDN; only a tiny pointer rides the chat.
- ~2M sockets per server and end-to-end encryption at planet scale.
