Skip to content
HLD Learn/Capstone Case Studies

Worked Example: Designing a Chat System (like WhatsApp)

8 min read

You'll learn to

  • -Apply the design loop to a stateful, real-time system
  • -See why a persistent connection breaks the statelessness assumption from Module 2, and how to work around it

The URL shortener was almost entirely stateless request/response. A chat system breaks that assumption immediately: it needs a persistent, long-lived connection per user, which directly conflicts with the "any server can handle any request" principle from Module 2. This chapter is about resolving that tension.

Step 1: Clarify Requirements

  • -Functional: 1:1 and group messaging, delivery status (sent/delivered/read), online presence, message history that syncs across a user's devices.
  • -Out of scope: media/file transfer internals (assume object storage handles that, covered in Module 11), voice/video calls.
  • -Non-functional: messages must arrive in the right order within a conversation, delivery should feel near-instant when both users are online, and no message should be silently lost if a user is briefly offline.

Step 2: Estimate Scale

Back-of-Envelope Math (assume 500M daily active users)
~230K/sec
Average messages/sec (500M users × 40 msgs/day ÷ 86,400s)
~1M/sec
Peak messages/sec (assume ~4-5x average)
~20 TB/day
Raw message storage (20B messages/day × ~1KB incl. metadata)
Millions
Concurrent open WebSocket connections at any moment

That last row is the number that makes this system architecturally different from anything earlier in the course: it is not just about handling requests, it is about holding open millions of simultaneous, stateful connections.

Step 3: High-Level Design

Sender
Gateway A
Message Service
Presence Registry
Message Store
Gateway B
Recipient

Sender and recipient may be connected to different gateway servers: a presence registry tells the message service where to route each message.

SenderGateway A- persistent WebSocketGateway AMessage Service- send messageMessage ServicePresence Registry- where is recipient?Message ServiceMessage Store- persist messageMessage ServiceGateway B- route to recipient's gatewayGateway BRecipient- push over WebSocket

The message store is deliberately a wide-column NoSQL database, not a relational one: the access pattern is almost always "give me messages for conversation X, ordered by time," a simple, massive-write-volume pattern that plays directly to a wide-column store's strengths, exactly as the Databases 101 module framed the choice.

Step 4: Deep Dive - The Stateful Connection Problem

A user's WebSocket connection is pinned to one specific gateway server, which is unavoidable, since a TCP connection cannot be load-balanced mid-flight to a different machine. This means any OTHER server that needs to deliver a message to that user must first discover which gateway currently holds their connection. The fix: a presence registry (a fast key-value store, mapping user_id -> gateway_server_id) that every gateway updates on connect/disconnect, and that the message service consults before routing every message.

  • -User comes online: their gateway writes {user_id: gateway_id} to the presence registry.
  • -A message arrives for them: the message service looks up their current gateway, then forwards the message there specifically.
  • -User goes offline or the gateway crashes: the registry entry is removed (or expires via TTL), and the message service instead writes the message to a durable per-user inbox for delivery on reconnect.

Ordering and Delivery Guarantees

Two different servers' clocks can disagree, so a raw timestamp is not a safe way to order messages within a conversation. Instead, assign a per-conversation monotonic sequence number at write time: every client can then reliably reconstruct "what came after what," independent of clock skew. For delivery, the system guarantees at-least-once (the same guarantee the Async Systems module named as the practical default for queues); a client may occasionally receive a duplicate after a reconnect, and de-duplicates using the message's unique ID, exactly the idempotency pattern from Module 12.

Step 5: Trade-offs & Failure Modes

  • -Group chat fan-out: for a group of N members, the message service pushes to N recipients' gateways per message. Unlike a social feed, N here is small (tens to low hundreds), so simple fan-out-on-write is fine: no celebrity problem at this scale (the next chapter is the case where that stops being true).
  • -Consistency: "delivered" and "read" receipts propagate asynchronously and can briefly disagree across a user's own devices, which is acceptable, since nobody expects perfectly synchronized read receipts within milliseconds.
  • -Failure mode: a gateway server crashes. Every connection it held drops. Clients detect the dropped socket, reconnect (likely to a different gateway via the load balancer), re-register their presence, and request "everything since sequence number N" for each conversation to fill the gap: the sequence number from the deep-dive is what makes this resync possible at all.
Interview Signal

Why can't you just load-balance WebSocket connections the same way you load-balance stateless HTTP requests?

Weak Answer

"WebSockets are just a different protocol, so you need a different load balancer."

Strong Answer

"A WebSocket is a single long-lived TCP connection: once established, it is physically pinned to one specific server process holding that socket. A stateless HTTP load balancer picks a server per-request; here, the choice is made once, at connect time, and every server for the life of that connection must be reachable through that same pinned path. That is exactly why a separate presence registry is needed: it is the piece that lets the rest of the stateless system find a fundamentally stateful connection."

Check Yourself1 / 3

Why does a chat system need a "presence registry" at all?

Ready to Build This?

Level 11: Chat System (WhatsApp) in the HLD Lab is exactly this system - go build the design you just walked through.

ScaleDojo Logo
Initializing ScaleDojo