AI InterviewSearch…Search problems, topics, or pages…Ctrl KGet started
Skip to content
SD notes

System Design · Note 16 of 22

Design a Chat App (WhatsApp)

Intermediate · 8 min read · updated 14 November 2026

Quick answer

How do you design a chat app like WhatsApp?

Keep a persistent WebSocket from every phone to a gateway, route each message through a chat service that numbers it per conversation, stores it and finds the recipient's gateway in a session registry. Deliver over the open socket, or keep it in an inbox and send a push notification when the recipient is offline. Receipts and presence travel as small messages on the same sockets.

On this page
  1. Requirements
  2. Capacity estimate
  3. API
  4. Data model
  5. High-level design
  6. Deep dive: receipts and ordering
  7. Deep dive: offline delivery and presence
  8. Bottlenecks and trade-offs
  9. Interview questions
  10. Common questions

A chat app is a real-time messaging system: millions of phones hold open connections, and every message must arrive once, in order, even when the receiver is offline.

Requirements

  • Functional: one-to-one and group messages; sent, delivered and read receipts; online / last seen; offline delivery; history on a new device.
  • Non-functional: low latency (well under a second); no message lost; per-conversation order; highly available.
  • Out of scope unless asked: voice calls, media processing, end-to-end encryption details.

Capacity estimate

Assumptions, stated out loud: 100 million daily users, 40 messages each a day, 200 bytes a message.

QuantityWorkingResult
Messages a day100 M × 404 billion
Average write rate4 B ÷ 86,400 s≈ 46,000 / s
Peak (assume 3×)46,000 × 3≈ 140,000 / s
Storage a day4 B × 200 B800 GB
Open connectionsassume 50 M online at once50 M sockets
  • Connections, not bandwidth, are the scaling problem: each gateway holds tens of thousands of sockets.

API

WebSocket  wss://chat.example/ws          (one per device, kept open)
→ send     {convId, clientMsgId, text}
← ack      {clientMsgId, seq, status: "sent"}
← message  {convId, seq, from, text}
→ receipt  {convId, seq, status: "delivered" | "read"}
GET /conversations/{id}/messages?afterSeq=40   (sync after reconnect)

Data model

TableKeyFields
messages(convId, seq)senderId, body, createdAt
conversationsconvIdmembers, lastSeq
inbox(userId, convId, seq)undelivered pointers
sessionsuserIdgatewayId (in memory, with TTL)
  • Partition messages by convId: one conversation's history is one range read. A wide-column store (Cassandra, HBase) suits this write-heavy, append-only shape.

High-level design

  • Gateways hold WebSockets and nothing else, so they scale out freely.
  • Chat service numbers, stores and routes each message.
  • Session registry maps user → gateway, so any gateway can reach any user.
A message from Alice to Bob over WebSockets

Each phone keeps one WebSocket open to a gateway, so the server can push a message the moment it arrives instead of waiting to be polled.

0:01 / 0:20Step 1 of 7
Transcript · 7 steps

Deep dive: receipts and ordering

  • The client sends a clientMsgId; a resend with the same id is ignored, so retries never duplicate a message.
  • Order comes from the server's seq, never from phone clocks.
Delivery receipts: sent, delivered, read

Alice sends m1 up her socket. Until the server answers, her phone shows it as sending, with a clock.

0:01 / 0:18Step 1 of 7
Transcript · 7 steps
Ordering by sequence number, not by arrival

The server numbered Alice's messages 1 to 5, but the network delivers them in the order 1, 3, 4, 2, 5. The receiver must not trust arrival order.

0:01 / 0:17Step 1 of 6
Transcript · 6 steps

Deep dive: offline delivery and presence

  • No live socket → store in the inbox, send a push via APNs / FCM. The push is a wake-up, not the delivery.
  • On reconnect: "everything after seq N", then ack.
Offline delivery: an inbox, a push, then a sync

Bob's phone is off. It last received message 40, and his WebSocket is gone from the session registry.

0:01 / 0:16Step 1 of 6
Transcript · 6 steps
  • Presence is a heartbeat plus a key with a time-to-live — no database writes per beat.
Presence: heartbeats keep a key alive until they stop

Bob's app sends a heartbeat every 30 seconds. Each one sets a presence key that expires 45 seconds later unless refreshed.

0:01 / 0:14Step 1 of 5
Transcript · 5 steps

Bottlenecks and trade-offs

  • Gateway failure drops its sockets; phones reconnect elsewhere and sync by seq, so nothing is lost.
  • Hot groups: fan-out per member grows with group size — cap it, or use pull for very large channels.
  • Ordering across conversations is not needed; one counter per conversation avoids a global bottleneck.
  • Pass routing traffic through a message queue when the chat service and gateways must scale separately.

Interview questions

  • WebSockets or long polling? WebSockets: one open, two-way connection per device; long polling is the fallback for networks that block them.
  • How do you guarantee order? A per-conversation sequence number assigned by the server; clients display by it and hold gaps.
  • How do you avoid duplicates on retry? A client-generated message id, deduplicated by the server.
  • Where is a message while the friend is offline? In their inbox in durable storage; the push only wakes the phone.

Next: Design a News Feed · Before this: System Design Interview Framework

Common questions

Why do chat apps use WebSockets instead of HTTP polling?

A WebSocket stays open in both directions, so the server can push a message the instant it arrives. Polling either delays messages until the next poll or floods the servers with empty requests from millions of idle phones.

How does WhatsApp keep messages in order?

The server gives every message the next sequence number of its conversation. Phones sort and display by that number, not by arrival time or the sender's clock, and hold a message whose predecessor has not arrived yet.

How do delivery and read receipts work?

They are small messages that travel back over the same sockets. The server acks a stored message (sent), the recipient's phone acks receipt (delivered) and acks again when the chat is opened (read); the status only ever moves forward.

What happens when the recipient is offline?

The message is stored in the recipient's inbox of undelivered messages and a push notification is sent through APNs or FCM. When the phone reconnects it asks for everything after the last sequence number it holds and acks what it receives.

How is online or last seen status implemented?

The app sends a heartbeat every few seconds, which refreshes a presence key with a time-to-live in a fast store such as Redis. When heartbeats stop, the key expires and contacts see last seen; a friend usually reads it when opening the chat rather than having every change broadcast.

How do group chats work in this design?

The chat service fans a group message out to every member: online members get it over their sockets and offline members get it in their inboxes. Small groups fan out this way; a cap on group size keeps the cost of one message bounded.

Design a Chat App Like WhatsApp: System Design

Design a chat app like WhatsApp: WebSockets, presence, message ordering with sequence numbers, sent/delivered/read receipts and offline delivery, animated.

  • Subject: System Design
  • Level: Intermediate
  • Reading time: 8 min
  • Updated: 2026-11-14

How do you design a chat app like WhatsApp?

Keep a persistent WebSocket from every phone to a gateway, route each message through a chat service that numbers it per conversation, stores it and finds the recipient's gateway in a session registry. Deliver over the open socket, or keep it in an inbox and send a push notification when the recipient is offline. Receipts and presence travel as small messages on the same sockets.

A chat app is a real-time messaging system: millions of phones hold open connections, and every message must arrive once, in order, even when the receiver is offline.

Requirements

  • Functional: one-to-one and group messages; sent, delivered and read receipts; online / last seen; offline delivery; history on a new device.
  • Non-functional: low latency (well under a second); no message lost; per-conversation order; highly available.
  • Out of scope unless asked: voice calls, media processing, end-to-end encryption details.

Capacity estimate

Assumptions, stated out loud: 100 million daily users, 40 messages each a day, 200 bytes a message.

QuantityWorkingResult
Messages a day100 M × 404 billion
Average write rate4 B ÷ 86,400 s≈ 46,000 / s
Peak (assume 3×)46,000 × 3≈ 140,000 / s
Storage a day4 B × 200 B800 GB
Open connectionsassume 50 M online at once50 M sockets
  • Connections, not bandwidth, are the scaling problem: each gateway holds tens of thousands of sockets.

API

WebSocket  wss://chat.example/ws          (one per device, kept open)
→ send     {convId, clientMsgId, text}
← ack      {clientMsgId, seq, status: "sent"}
← message  {convId, seq, from, text}
→ receipt  {convId, seq, status: "delivered" | "read"}
GET /conversations/{id}/messages?afterSeq=40   (sync after reconnect)

Data model

TableKeyFields
messages(convId, seq)senderId, body, createdAt
conversationsconvIdmembers, lastSeq
inbox(userId, convId, seq)undelivered pointers
sessionsuserIdgatewayId (in memory, with TTL)
  • Partition messages by convId: one conversation's history is one range read. A wide-column store (Cassandra, HBase) suits this write-heavy, append-only shape.

High-level design

  • Gateways hold WebSockets and nothing else, so they scale out freely.
  • Chat service numbers, stores and routes each message.
  • Session registry maps user → gateway, so any gateway can reach any user.
AliceGateway 1Chat servicenext seq 43Gateway 2BobMessages#42 storedSessionsbob → Gateway 2dashed = open WebSocket42
A message from Alice to Bob over WebSockets.
  1. Each phone keeps one WebSocket open to a gateway, so the server can push a message the moment it arrives instead of waiting to be polled.
  2. Alice's message goes up her open socket to Gateway 1. Gateways only hold connections; they keep no chat state of their own.
  3. The chat service gives it the conversation's next sequence number, 42. One counter per conversation is what lets every phone agree on the order.
  4. Message 42 is written to the messages store before anything else, then Alice is told "sent". From here it survives any crash.
  5. To deliver it, the service asks the session registry where Bob is connected: Gateway 2. Any gateway can reach any user through this lookup.
  6. The message is handed to Gateway 2, which holds Bob's socket.
  7. Gateway 2 pushes message 42 down Bob's open WebSocket. No polling was involved anywhere on the path.

Deep dive: receipts and ordering

  • The client sends a clientMsgId; a resend with the same id is ignored, so retries never duplicate a message.
  • Order comes from the server's seq, never from phone clocks.
AliceServerBobsend m1ack: storedm1delivered m1deliveredread m1readAlice sees: read
Delivery receipts: sent, delivered, read.
  1. Alice sends m1 up her socket. Until the server answers, her phone shows it as sending, with a clock.
  2. The server stores m1 and acks. Alice now sees one tick, sent: the server has it, even if Bob's phone is off.
  3. The server pushes m1 to Bob's phone over his WebSocket.
  4. Bob's app acks receipt as soon as the message is on the device, before he opens the chat.
  5. The server relays it and Alice's tick becomes two: delivered. A receipt is just another small message on the same sockets.
  6. Bob opens the chat; his app sends a read receipt for the newest message on screen, which covers every earlier one too.
  7. Alice sees read. The status only ever moves forward, so a late or duplicate receipt can never turn read back into delivered.
Arriving from the networkHeld: waiting for a gapShown in the chat, in ordernext expected: 612345
Ordering by sequence number, not by arrival.
  1. The server numbered Alice's messages 1 to 5, but the network delivers them in the order 1, 3, 4, 2, 5. The receiver must not trust arrival order.
  2. Message 1 is the next expected number, so it is shown straight away.
  3. Message 3 arrives but 2 has not, so 3 is held rather than shown out of order. A gap that lasts too long triggers a resync from the server.
  4. Message 4 arrives but 2 has not, so 4 is held rather than shown out of order. A gap that lasts too long triggers a resync from the server.
  5. Message 2 fills the gap, so it is shown and the held 3 and 4 follow it at once. The chat reads 1, 2, 3, 4.
  6. Message 5 is next in line and is shown. The chat now holds all 5, in the order they were sent.

Deep dive: offline delivery and presence

  • No live socket → store in the inbox, send a push via APNs / FCM. The push is a wake-up, not the delivery.
  • On reconnect: "everything after seq N", then ack.
Chat serviceBob's inbox0 undeliveredPush service3 sentBobhas up to #43414243
Offline delivery: an inbox, a push, then a sync.
  1. Bob's phone is off. It last received message 40, and his WebSocket is gone from the session registry.
  2. Message 41 finds no live socket for Bob, so it is kept in his inbox of undelivered messages and a push notification wakes his phone.
  3. Message 42 joins the inbox too (2 waiting). Push notifications are best effort; the inbox is what guarantees delivery.
  4. Message 43 waits as well: 3 in the inbox, kept in sequence order, 3 pushes sent.
  5. Bob comes back online and opens a new WebSocket, asking for everything after #40, the last number his phone holds.
  6. The server sends 41, 42, 43 in order; Bob's app acks #43 and the inbox is cleared. Nothing was lost while he was away.
  • Presence is a heartbeat plus a key with a time-to-live — no database writes per beat.
0s30s60s90s120snowpresence:bob expiredAlice sees: last seen at 60s
Presence: heartbeats keep a key alive until they stop.
  1. Bob's app sends a heartbeat every 30 seconds. Each one sets a presence key that expires 45 seconds later unless refreshed.
  2. Heartbeat at 30s, before the key ran out: its time-to-live is reset to 45s and Bob stays online.
  3. Another heartbeat at 60s. While beats come every 30s, the 45s time-to-live never runs out.
  4. No heartbeat at 90s: Bob closed the app. The key still has 15s to live, so a single late beat does not flicker him offline.
  5. At 105s the key expires and Bob shows "last seen at 60s". Presence is cheap state with a timeout, not something to store forever.

Bottlenecks and trade-offs

  • Gateway failure drops its sockets; phones reconnect elsewhere and sync by seq, so nothing is lost.
  • Hot groups: fan-out per member grows with group size — cap it, or use pull for very large channels.
  • Ordering across conversations is not needed; one counter per conversation avoids a global bottleneck.
  • Pass routing traffic through a message queue when the chat service and gateways must scale separately.

Interview questions

  • WebSockets or long polling? WebSockets: one open, two-way connection per device; long polling is the fallback for networks that block them.
  • How do you guarantee order? A per-conversation sequence number assigned by the server; clients display by it and hold gaps.
  • How do you avoid duplicates on retry? A client-generated message id, deduplicated by the server.
  • Where is a message while the friend is offline? In their inbox in durable storage; the push only wakes the phone.

Next: Design a News Feed · Before this: System Design Interview Framework

Common questions

Why do chat apps use WebSockets instead of HTTP polling?

A WebSocket stays open in both directions, so the server can push a message the instant it arrives. Polling either delays messages until the next poll or floods the servers with empty requests from millions of idle phones.

How does WhatsApp keep messages in order?

The server gives every message the next sequence number of its conversation. Phones sort and display by that number, not by arrival time or the sender's clock, and hold a message whose predecessor has not arrived yet.

How do delivery and read receipts work?

They are small messages that travel back over the same sockets. The server acks a stored message (sent), the recipient's phone acks receipt (delivered) and acks again when the chat is opened (read); the status only ever moves forward.

What happens when the recipient is offline?

The message is stored in the recipient's inbox of undelivered messages and a push notification is sent through APNs or FCM. When the phone reconnects it asks for everything after the last sequence number it holds and acks what it receives.

How is online or last seen status implemented?

The app sends a heartbeat every few seconds, which refreshes a presence key with a time-to-live in a fast store such as Redis. When heartbeats stop, the key expires and contacts see last seen; a friend usually reads it when opening the chat rather than having every change broadcast.

How do group chats work in this design?

The chat service fans a group message out to every member: online members get it over their sockets and offline members get it in their inboxes. Small groups fan out this way; a cap on group size keeps the cost of one message bounded.

← How to Answer a System Design Interview · Design a News Feed →