Skip to content
HLD Learn/Capstone Case Studies
Browsing as a guest. Sign in to save your progress and earn XP as you complete chapters.

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 is part of Pro

See a real weak answer next to a real strong one for this exact topic.

Quiz is part of Pro

Test what you just read with a short quiz, and bank the XP.

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.