Deep Dive
system design interviewdistributed systemsmessage queuesrealtime

Design a Chat System

WhatsApp-scale messaging, built end to end: persistent WebSocket gateways, a user→server registry, store-and-forward for offline devices, the one-tick / two-tick / blue-tick delivery state machine, message ordering, and group fan-out. Drive the delivery protocol yourself, then take it apart.

·14 min read
Hard

“Send a message to another user” sounds like a database insert. It isn’t. The recipient might be offline, on three devices, or behind a flaky mobile connection that drops mid-send. The message has to arrive exactly once, in order, and the sender expects to watch it progress — one tick when the server has it, two when it reaches the device, blue when it’s read. That little row of ticks is a distributed-systems problem in disguise.

Play with the part users actually see. Send messages, then flip the recipient offline and watch delivery become store-and-forward — nothing is lost, it just waits.

Recipientonline

No messages yet.

Server-stored0
Awaiting delivery0
Connectionlive (WebSocket)
✓ sent✓✓ delivered✓✓ read
What just happened
  • Recipient is online. Send a message.

What makes it hard

  • Connections are stateful. Unlike a stateless REST API, a gateway holds the socket. To deliver to user B, you must know which of thousands of gateways B is connected to — a lookup, on every message.
  • Recipients disappear. Persist first, deliver second. An offline user’s messages sit in a server-side inbox and a push notification wakes their app; delivery resumes on reconnect.
  • Order and dedup. A retried send must not double-post, and everyone must see a conversation’s messages in the same order — even though they were written from different servers.

The shape of it

sendlookup Bpushif offlineUser AWebSocketGateway 2holds A's socketChat servicepersist → routeSession registryuser → gatewayGateway 7holds B's socketUser B (online)✓✓ deliveredInbox + pushB offline → later
Send path: A's message rides A's WebSocket to its gateway, into the chat service, which PERSISTS it, then looks B up in the session registry. B online → push down B's gateway/socket. B offline → the message waits in the inbox and a push notification is sent; it's delivered on B's next connect.

The members-only build takes this all the way down: why gateways are stateful and how the session registry is kept consistent as connections churn; the idempotent send path (with code) that survives retries; message ordering with Snowflake ids; the storage engine choice (why Discord and Messenger reach for Cassandra/ScyllaDB, and how WhatsApp got away with storing almost nothing); presence & read receipts and why presence fan-out is the expensive part; group messaging and the fan-out-on-write vs fan-out-on-read decision for large groups; the reconnect thundering herd; and an interview challenge with a worked answer.

Members only

Keep reading with Premium

You've reached the members-only part of this deep-dive — the full implementation, the interactive ring simulator, and the step-by-step walkthrough. Unlock it with a membership.