Design problems
Messaging & collaborationHardSDE-2 40 min

Design WhatsApp

Asked at
AmazonGoogleMetaMicrosoftPhonePeShareChat

The interviewer asks

“Design a messaging app like WhatsApp: one-to-one and group chat, delivery and read receipts (ticks), online/last-seen, media sharing, and end-to-end encryption, for over a billion users on mobile networks of very mixed quality.”

What a strong answer looks like

A strong answer is built around a persistent-connection layer and store-and-forward delivery. Each phone keeps one long-lived connection to a chat gateway; a session registry maps user (and device) to the gateway holding it. Sending a message means persisting it to the recipient's per-device inbox, acknowledging the sender (one tick), routing it to the recipient's gateway if online (two ticks after the device acks), or sending a push notification if offline. Messages are end-to-end encrypted with the Signal protocol, so the server only stores and forwards ciphertext and deletes it after delivery. Ordering is per conversation using sequence numbers, retries are made safe with client message IDs, groups use fan-out per member device with sender keys, and media goes to blob storage with only an encrypted reference in the message.

Concepts to mention

Persistent connections to stateless gatewaysSession registry mapping device to gatewayStore-and-forward inbox per devicePersist before ack (one tick)Client message IDs for idempotent retriesPer-conversation ordering with sequence numbersDelivery and read receiptsPush notifications for offline devicesSignal protocol: X3DH, Double Ratchet, sender keysMedia in blob storage with keys inside the messagePresence via subscriptions, not broadcastGroup fan-out on write with size caps

1 Requirements

Functional

  • One-to-one text messages with sent / delivered / read ticks
  • Group chats up to 1,024 members
  • Offline delivery: messages wait on the server until the device comes online
  • Media: images, video, voice notes, documents
  • Online status and last seen, typing indicator
  • End-to-end encryption; multi-device (phone + up to 4 linked devices)

Non-functional

  • 2B users, ~500M concurrently connected at peak
  • ~100B messages/day; peaks 3-5x on New Year's Eve and Diwali
  • Delivery latency p99 under 500 ms when both users are online in the same region
  • No message loss once the sender sees one tick; no duplicates shown to users
  • Per-conversation ordering; works on 2G/3G and flaky networks with small battery cost
  • Server must not be able to read message content

Out of scope (say it out loud)

  • Voice and video calls (separate WebRTC design)
  • Payments (UPI in WhatsApp) and business APIs
  • Status / stories
  • Spam and abuse detection details

2 Estimates

Message QPS~1.2M/s average, ~5M/s peak100B ÷ 86,400 ≈ 1.16M/s; New Year midnight spike ×4
Deliveries (after group fan-out)~3M/s averageAssume average delivery fan-out ~2.5 devices per message (groups + linked devices)
Concurrent connections~500MA gateway holds ~1M idle connections with Erlang/epoll tuning, more typically plan for ~500k; 500M ÷ 500k ≈ 1,000 gateways, ×1.5 headroom
Transient message storage~10 TB/day in, small at rest100B × ~100 bytes ciphertext + metadata ≈ 10 TB; most delivered in seconds and deleted; undelivered kept up to 30 days
Media~5 PB/day uploadedAssume ~5B media items/day × ~1 MB average after client compression
Receipts~2x message volumeEach delivered message generates a delivered ack and usually a read ack, which are themselves small messages

3 API

EndpointWhat it does
WS/chat (persistent, binary protocol)Single long-lived connection carrying send, deliver, ack, receipt, presence and typing frames
WS framesend {client_msg_id, conv_id, to, ciphertext_per_device}Send a message; server replies ack {client_msg_id, server_seq} = one tick
WS framedeliver / ack {msg_id}Server pushes message to device; device acks so the server can mark delivered and delete it
WS framereceipt {msg_id, type: delivered|read}Ticks sent back to the original sender, also end-to-end encrypted metadata where possible
GET/v1/keys/{user_id}Fetch identity key, signed pre-key and a one-time pre-key bundle for each device to start an encrypted session
POST/v1/mediaUpload encrypted media blob; returns media_id/URL included inside the encrypted message
POST/v1/groups/{group_id}/membersAdd or remove group members (admin only)

4 Data model

inbox (pending messages)Cassandra / HBase-style wide-column store
device_id (partition)server_seq (clustering)msg_idfromconv_idciphertextcreated_atTTL 30 days

Store-and-forward queue per recipient device; rows deleted after the device acks. Write-heavy and append-only, which suits LSM-tree stores.

session registryRedis cluster (or in-memory distributed registry)
device_id → gateway_idconnected_at

Updated on connect/disconnect with a TTL refreshed by heartbeats so stale entries expire.

users and devicesMySQL/Postgres sharded by user_id
user_idphone (hashed lookup)devices[]push_tokensprivacy settings

Relational, moderate volume; phone-to-user lookup for contact discovery.

public keysSharded key-value store
device_id → identity keysigned pre-keyone-time pre-keys list

One-time pre-keys are consumed atomically; client tops up when low.

groupsSharded SQL or Cassandra
group_idmembers (user_id, role)versioncreated_at

Member list cached in the group fan-out service; version used to detect stale caches.

mediaBlob storage (S3-like) + CDN
media_id → encrypted blobsizeexpiry

Server stores only ciphertext; decryption key travels inside the E2E encrypted message.

5 Architecture

CLIENTSEDGESERVICESASYNCDATAEXTERNALpersistent connsend framepersist firstwhere is device?group msgdeliver to gatewaypush framedevice offlineheartbeatspre-key bundleencrypted mediaSender phoneRecipient devicesL4 load balancerChat gatewaysKey serviceGroup fan-out serviceMessage routerPresence servicePush queueMedia blob store + CDNInbox (Cassandra)Session registry(Redis)FCM / APNs
ClientEdge / gatewayService / workerQueue / streamCacheDatabase / storageAI / modelThird partyrequestasync (event / queue)

Hover a box to highlight its connections.

6 Request flows

Send a 1:1 message (recipient online)

  1. 1Sender's app encrypts the message once per recipient device (and per own linked device) using the Signal session and sends a send frame with client_msg_id
  2. 2Gateway forwards to the message router, which assigns a server_seq for the recipient device inbox and writes the ciphertext to Cassandra
  3. 3Only after the write succeeds, the router acks the sender: one grey tick
  4. 4Router looks up the recipient device in the session registry and forwards the message to that gateway
  5. 5Gateway pushes it down the recipient's connection; the device stores it locally and sends ack
  6. 6Router deletes it from the inbox and sends a delivered receipt to the sender: two grey ticks
  7. 7When the user opens the chat, the device sends a read receipt: two blue ticks (if read receipts are enabled)

Recipient offline

  1. 1Router finds no live session (or the gateway reports the connection gone)
  2. 2Message stays in the inbox; the router enqueues a push notification with no content (or encrypted preview) to FCM/APNs
  3. 3When the phone reconnects, the gateway asks the router for all inbox rows with server_seq greater than the device's last acked seq
  4. 4Messages are delivered in order in batches; each batch ack deletes them and triggers delivered receipts to senders

Send to a group of 500

  1. 1Sender uses a sender key (Signal 'sender keys'): it encrypts the message once with its group chain key; the sender key was earlier shared with each member device over pairwise sessions
  2. 2Server receives one ciphertext plus the group_id; the group service expands membership to ~500 users × devices
  3. 3Router writes one inbox entry per member device (or one shared message row plus per-device pointers) and delivers to online devices
  4. 4Delivery receipts are aggregated per message so the sender sees 'delivered to all' without 500 separate notifications

7 Deep dives

Connections, routing and the session registry

Mobile phones cannot be reached by inbound connections, so each device opens one long-lived TCP/TLS connection (WhatsApp historically used a compact XMPP-derived binary protocol; WebSocket or MQTT also work) to a gateway chosen by the load balancer. Gateways are only connection holders: they authenticate, keep heartbeats, and forward frames. Each connect writes device_id → gateway_id into a session registry with a TTL refreshed by heartbeats, so a crashed gateway's entries expire. The router, given a recipient device, reads the registry and sends the frame to that gateway over an internal RPC or message bus. A gateway can hold hundreds of thousands to about a million mostly idle connections; WhatsApp famously ran around 2M connections per Erlang server. Heartbeat interval is a battery-vs-staleness trade-off: send small pings every few minutes, and adapt to the network (NAT timeouts on Indian mobile carriers can be short). Because connections drop constantly on mobile, the design must assume any delivery can fail mid-way, which is why delivery is ack-based from the inbox rather than fire-and-forget.

Delivery guarantees, ordering and dedup

The promise to the user is: once you see one tick, the message will arrive exactly once, in order. 'Exactly once' over a network is really at-least-once delivery plus idempotent handling. The sender attaches a client_msg_id (UUID); if its send times out and it retries, the router finds the existing record and returns the same ack instead of writing twice. The server persists before acking, so a crash after the ack cannot lose the message. Towards the recipient, the router may deliver a message twice (for example if the device's ack was lost); the device dedups by msg_id. Ordering is per recipient device inbox: server_seq is monotonic per inbox (assigned by the partition owner or using a per-inbox counter), and the device applies messages in seq order and asks for gaps. Global ordering across conversations is not needed. Within a conversation, two people typing at once can produce interleavings that differ slightly between devices; using the sender's timestamp plus server_seq as a tie-break is a standard, acceptable answer. Deletion after ack keeps server storage tiny, which is also a privacy feature.

End-to-end encryption with the Signal protocol

With E2E encryption the server is a blind relay. Each device has a long-term identity key pair, a signed pre-key, and a batch of one-time pre-keys, whose public parts are uploaded to the key service. To start a session, the sender fetches the recipient device's pre-key bundle and runs X3DH to derive a shared secret without the recipient being online. Each message then uses the Double Ratchet, which gives forward secrecy (old keys cannot be derived from new ones) and post-compromise recovery. For groups, each member creates a sender key and distributes it to every other member device via pairwise sessions; group messages are then encrypted once, which makes server fan-out cheap. Consequences for system design: the server cannot do search, spam filtering on content or server-side message history; multi-device means encrypting per device, so a user with 5 devices costs 5 ciphertexts; adding a device requires a key-change notice ('security code changed'); and backups must be encrypted separately (by a user key or password-protected backup keys). Media is encrypted on the device with a random key; the blob goes to storage and the key and hash travel inside the encrypted message.

Presence and last seen without melting the system

Presence looks small but is a fan-out trap: if every status change were pushed to every contact, a user with 500 contacts would create 500 events each time they open the app, and with hundreds of millions of users it explodes. Use pull-on-demand plus subscriptions limited to open chats. When you open a chat with someone, your device subscribes to their presence; the presence service keeps online state from gateway heartbeats in a sharded in-memory store with a TTL and notifies only current subscribers. Last-seen timestamp is written on disconnect and read lazily. Typing indicators are ephemeral frames routed directly to the other party's gateway with no persistence; if lost, nobody cares. Privacy settings (hide last seen from everyone or non-contacts) are checked in the presence service before returning anything. Showing that presence and typing are best-effort and transient while messages are durable is a key signal of judgement.

8 Trade-offs

Store-and-forward with deletion vs permanent server history

WhatsApp keeps messages on the server only until delivered; history lives on devices plus encrypted backups. That keeps storage small and supports E2E privacy, but new devices cannot simply download history from the server. Slack and Telegram cloud chats choose server history for search and multi-device sync at the cost of the server seeing content.

Fan-out on write for groups

Writing one inbox entry per member device makes reads trivial and works because groups are capped at ~1,024. For huge broadcast channels you would switch to fan-out on read: store once per channel and let devices pull.

Custom binary protocol vs plain WebSocket+JSON

A compact binary protocol saves bytes and battery on 2G/3G, which matters for Indian users with low-end phones and expensive data. WebSocket with protobuf is a good modern equivalent; JSON is fine for an MVP but wasteful at this scale.

Cassandra for inboxes vs a relational DB

Inbox traffic is append-then-delete at millions of writes per second, partitioned by device. An LSM-based wide-column store handles this well; the cost is tombstones from deletions, which needs tuned compaction and short gc_grace.

9 Bottlenecks

New Year's Eve midnight spike

Messages per second jump several-fold within minutes, mostly greetings with media. Pre-scale gateways and routers, let queues absorb the burst, prioritise text over media downloads, and shed non-critical work like presence updates and typing indicators first.

Large groups and viral forwards

One forward into many 1,000-member groups creates huge fan-out. Cap group size, rate-limit forwards (WhatsApp limits forwarding to 5 chats, and 'highly forwarded' messages to 1 chat at a time), and deduplicate media blobs by hash so the same video is stored once.

Inbox tombstones

Billions of deletes per day create tombstones that slow reads in LSM stores. Use time-bucketed partitions or TTL-based expiry, and compaction strategies suited to queue-like data.

10 Failure handling

Gateway crash

Its devices reconnect elsewhere within seconds and resync from their last acked server_seq. Registry entries for the dead gateway expire via TTL, and any in-flight messages remain in the inbox because they were persisted before delivery.

Router fails after persisting but before acking the sender

The sender times out and retries with the same client_msg_id; the router finds the existing record and acks. No duplicate.

Data centre outage

Users are homed to a region, with inbox data replicated to a second region. On failover, devices reconnect to the other region; a small window of unreplicated messages may be retried from senders' unacked outboxes, which is why clients keep unacked messages until they get one tick.

11 What impresses

  • Explaining exactly what each tick means in terms of server state
  • Saying 'at-least-once plus dedup' instead of claiming exactly-once
  • Knowing how E2E encryption changes the design: no server search, per-device ciphertexts, sender keys for groups
  • Treating presence and typing as ephemeral and not persisting them
  • Designing for flaky mobile networks: resume from last acked seq, small binary frames
  • Deleting messages after delivery to keep storage and privacy risk low

12 Mistakes

  • Using HTTP polling for message delivery
  • Acking the sender before the message is durably stored
  • Requiring global ordering of all messages
  • Broadcasting presence changes to all contacts
  • Sending media bytes through the chat connection
  • Saying 'the server encrypts messages' when asked about E2E encryption

13 Follow-ups

Q1How do linked devices (WhatsApp Web) work with E2E encryption?
Q2How would you implement 'delete for everyone'?
Q3How do you sync chat history to a new phone?
Q4How would you scale to broadcast channels with 10M followers?
Q5How do you detect spam if you cannot read messages?
Q6How would you comply with data-localisation rules in India?

Saved on this device. Create a free account to keep it on every device.

Practise this with Sam

A real mock interview, out loud, with an honest scorecard. Free to start.

Start free