Real-Time Chat, Actually Explained

WhatsApp moves on the order of 2 billion messages a day. Sending one message to one online recipient is a WebSocket push — that part's easy. The interesting part is that the recipient might be offline, might have three devices, and might be in a 500-person group, and a well-designed system handles all three with one mechanism, not three bolted-together special cases. Run the three simulators below and watch it hold.

Rahul Bisht·
Jul 8, 2026
·System Design·
1 min read
Interactive Guide

Three live simulators — watch delivery, ordering, and group fanout actually work

01 · Ground Zero

What Even Is a Real-Time Chat System?

A real-time chat system's whole job is simple to say: get a message from one device to another, fast, and don't lose it. That's the entire promise — send, deliver, don't drop it.

Everything below this point is about honoring that promise once the recipient might be offline, might have three devices open at once, and might be one of five hundred people in the same conversation — the three complications a demo never shows you.

02 · Scope It First

What Are We Actually Building?

Before picking WebSocket vs. polling, it's worth being explicit about what this system actually owes every message — the same way you'd scope it out loud in an interview before drawing a single box.

It has to

  • Send a message in a 1:1 or group conversation
  • Deliver it immediately if the recipient is online, on reconnect if not
  • Support delivery and read receipts
  • Keep messages in sync across a user's multiple devices

It's explicitly not on the hook for

  • Voice/video calling — a genuinely different system, media transport not message delivery
  • The end-to-end encryption key-exchange itself — content is assumed to arrive already encrypted
  • Media transfer — a separate blob-storage path; the message just carries a reference
03 · Size It

Sizing the Problem

Numbers here are explicit back-of-envelope estimates, not measured production values — but they're what determines that this design is a connection-tracking problem more than a raw-throughput one.

Traffic

On the order of 2 billion messages/day — roughly 23,000/second average, several times higher at peak (regional evenings).

Concurrent connections

With ~500M daily active users and a large fraction connected at once, the platform needs on the order of 100-200 million concurrent WebSocket connections.

Memory just to hold connections

At maybe 10KB per open connection (buffers, TLS state, routing metadata), that's 1-2TB of memory before a single message is processed.

The constraint that actually rules the design

Sharded across a couple thousand gateway nodes if each comfortably holds ~50,000 connections — that single number is why a layer tracking which node holds which user's connection stops being optional.
04 · The Whole Picture

The Shape of the System

Before drilling into any one piece, here's the system end-to-end: a sender's message goes to a gateway, then a message service durably logs it and looks up presence to decide whether to push it live or leave it for reconnect-sync. The API contract and data model below are what every later section refines — nothing past this point replaces this picture, it only explains why each piece of it exists.

send
durable write
lookup
push if online
sync if offline
Sender
WS Gateway
Message Service
Presence Service
Conversation Logdurable, ordered
Recipient Device

The durable write happens on every send; push and sync are two ways of catching a device up — hover a node

client_message_id, generated by the sending device, is what makes retries safe: if a send is retried after a flaky connection, the server recognizes the duplicate against recently-seen IDs for that conversation and returns the original sequence number instead of creating a second message — the same idempotency-key pattern that shows up anywhere a caller can't be sure its last request landed.

// WS: client maintains a persistent connection for push delivery
POST /v1/messages
{ conversation_id, client_message_id, content }
→ { server_sequence, status: "accepted" }
GET /v1/conversations/{id}/sync?after_sequence={n}
→ { messages: [...] } // used by a device on reconnect

User

An account, which can have several registered Devices at once.

Device

One logged-in client (phone, web, desktop), each tracking its own sync position.

Conversation

A 1:1 or group thread, with a participant list.

Message / DeliveryReceipt

Content, sender, sequence number; delivery status tracked per message, per recipient device.
owns
participates in
contains
tracked per device
User
Device
Conversation
Message
DeliveryReceipt

Message is append-only, ordered by server sequence — never a client timestamp

Every device tracks its own “last synced sequence number” per conversation rather than the server tracking “has this message been shown to this device” as a mutable flag — that single cursor is what makes reconnect-and-sync, multi-device, and offline delivery all the same mechanism instead of three separate ones.

05 · Deep Dive: The First Fork

Poll, or Push?

Naive
Clients poll the server every few seconds, asking whether there are new messages.
Breaks
Delivery latency is bounded by the poll interval, and most polls return nothing — meaning the server does constant, mostly wasted work at hundreds of millions of users. Fails the 'instant' requirement immediately.
Fix
A persistent WebSocket connection, with the server pushing a message down the recipient's open connection the moment it arrives, is the only version that gets close to instant.
Tradeoff
That single choice is why 'which node holds this user's connection right now' becomes the architecture's central question — the subject of the next section.
06 · Deep Dive: The Load-Bearing Fork

Push-Only, or Durable-Log-First?

Push alone raises the real question underneath it: no single server can hold every user's connection, so which node does a given user's connection live on right now — and what happens if they're not connected to any node at all?

The answer that holds up: add a presence layer mapping a user/device to a gateway node, if any, and make every message write to a durable, ordered per-conversation log regardless of whether the recipient is online. Online push becomes an optimization on top of a system that would still be correct if every push failed — not the primary mechanism with offline bolted on, matching the architecture shown earlier.

07 · Deep Dive: The Payoff

Watch Delivery, Offline Catch-Up, and Multi-Device Actually Work

This is the part that's hard to picture from a diagram alone. A recipient can have several devices, and each one is either online right now or it isn't — that's the whole story. Toggle devices on or offline, send a message, and watch what happens to each one.

Try it

Toggle devices online/offline, then send. Every device gets the message from the durable log — online ones get it pushed live, offline ones catch up when you reconnect them.

Sender
Durable Logconversation log
Phone
Laptop
Tablet

Every device gets the same treatment: the log write happens once, online devices get it pushed live, offline ones pick it up whenever they reconnect by asking for everything after their own last-synced sequence number. A device offline for two weeks catches up exactly like one offline for two seconds — multi-device sync isn't a separate feature, it falls out of the same mechanism offline delivery already needed.

08 · Deep Dive: The Ordering Fork

Client Clock, or Server Sequence?

Naive
Order messages by the sender's device timestamp — it's already sitting right there on every message.
Breaks
Clocks skew, and two senders can produce the identical millisecond — or worse, a reply can carry an earlier timestamp than the message it's replying to, which the simulator below will show you directly.
Fix
The server assigns a monotonically increasing sequence number per conversation at write time, and that sequence number, never the timestamp, is the source of truth for both ordering and each device's sync cursor.
Try it

Alice's clock runs ~2.5s fast; Bob's runs ~1.2s slow — realistic device clock skew, not an edge case. Same six messages, same arrival order at the server, two different sort keys.

Press “Run exchange” to send the conversation.

09 · Deep Dive: The Group Fork

Copy the Message 500 Times, or Once?

Naive
Fan-out-on-write: copy the message into all 500 of the group's individual inboxes at send time — the same move a social feed makes to fan a celebrity's post out to followers.
Breaks
Wrong specifically because chat has no per-recipient customization: everyone in the group sees the identical message in the identical order, so 500 copies of the same bytes buy nothing a feed's personalization would justify.
Fix
A single shared, append-only log per conversation plus a per-device sync cursor handles a 500-person group exactly the same way it handles a 1:1 — fan-out only happens at delivery time (looking up presence for however many of those 500 are online), never at write time.
Tradeoff
Fan-out-on-read means storage stays flat regardless of group size, at the cost of every device doing its own read/cursor-advance instead of having a pre-copied inbox waiting — the right trade when there's no personalization to justify the copies.
Try it
one copy written into every member's inbox
+26 more inboxes
Copies written: 0Storage for this message: ~0KBestimate, 2KB/copy
10 · Operate It: When It Breaks

Where Speed Degrades but Correctness Doesn't

WS gateway node crash

Impact: Every connection held by that node dropsResponse: Clients reconnect with backoff, land on a different gateway node, re-register presence; missed messages are caught up via the sync endpoint using the last known sequence number

Conversation log store failure

Impact: Risk of message loss, which is unacceptableResponse: The log is replicated across multiple nodes/AZs before a write is acknowledged to the sender — durability comes before the online-push optimization, not after

Presence service outage

Impact: Can't route to a specific gateway node for online deliveryResponse: Falls back to push-notification delivery for everyone — slower, no live push, but not lossy, since the conversation log write still happens independently

Push provider (APNs/FCM) outage

Impact: Backgrounded/offline devices don't get wokenResponse: Failed pushes retry with backoff; since messages are already durably logged, a device that reconnects on its own still syncs correctly even if every push attempt failed

The recurring theme: nothing on this list causes message loss, because the durable log write happens independently of every delivery mechanism above it — each failure degrades speed, not correctness.

11 · Operate It: Watching It

Watching Delivery, and What Encryption Takes Off the Table

Message delivery latency, online path (P99)

Why it matters: The whole point of the push path is feeling instantAlert on: Sustained P99 above ~300ms

Presence lookup miss rate

Why it matters: A high miss rate forces messages onto the slower push-notification path unnecessarilyAlert on: Sharp rise vs. baseline

Conversation log write durability lag

Why it matters: A message isn't safe until it's durably logged, regardless of deliveryAlert on: Any replication lag beyond a few seconds

Undelivered backlog, per device

Why it matters: A growing backlog on one device usually means a broken push integration for that platformAlert on: Backlog age exceeding a few minutes for online-looking devices

If message content is end-to-end encrypted, the server is deliberately blind to it — which means features like server-side search or content moderation on message text simply aren't available, and any product requirement for them has to be solved client-side or explicitly traded away. Retention policy matters even for opaque, encrypted content: how long undelivered messages are held for an offline device, and how long delivery metadata is retained, are privacy decisions the architecture has to make room for, not afterthoughts.

12 · Judging an Answer

Sorting Mid from Senior from Staff

Mid-level

Lands on persistent WebSocket connections with server-side push for the online case, and can be prompted into handling the offline case as a fallback.

Senior

Independently proposes a presence layer and a durable per-conversation log as the unifying mechanism for online delivery, offline delivery, and reconnect-sync — rather than three separate features; gets ordering and de-duplication right without prompting.

Staff+

Reasons about the group-fanout trade-off (write-time vs. read-time) explicitly and picks read-time for the right reason, in direct contrast to how a social feed would answer the same question; designs multi-device sync as a natural consequence of the per-device cursor model; raises what end-to-end encryption forecloses (server-side search, moderation) as a real product constraint.
13 · Go Deeper

Further Reading

Related posts

View all →