Design a basic chat application like WhatsApp that supports one-to-one and small group messaging, online/last-seen status, and sent/delivered/read receipts. Focus on how messages reach users in real time, what happens when the recipient is offline, and how message order is preserved.
Show hints
HTTP request–response alone is a poor fit for server-initiated delivery.
Show the explanation
Model answer.
Real-time connection
- Each online client keeps a persistent WebSocket connection to a chat server (a gateway). This lets the server push messages instantly instead of the client polling.
- Many chat servers sit behind a load balancer. A session registry (e.g. Redis) maps
userId → chat serverso any server knows where a recipient is connected.
Sending a message (A → B)
- A sends
{conversationId, clientMessageId, text}over its WebSocket. - A's chat server assigns a per-conversation sequence number, persists the message, and acknowledges to A (sent ✓).
- It looks up B in the session registry. If online, it forwards the message to B's chat server (directly or via a message broker), which pushes it to B.
- B's client acknowledges; the server records delivered and notifies A. When B opens the chat, a read event flows back similarly.
Offline recipients
- If B is offline, the message stays stored as undelivered, and a push notification is sent via APNs/FCM.
- When B reconnects, the client sends the last sequence number it has for each conversation and the server sends everything newer — this sync-from-cursor approach also recovers from dropped connections.
Ordering and duplicates
- Order is guaranteed within a conversation using the server-assigned sequence numbers (a global order is unnecessary and expensive). Clients sort by sequence, not by device clock.
- Clients retry unacknowledged sends with the same
clientMessageId; the server deduplicates on it, so retries do not create duplicate messages.
Storage
- Messages are write-heavy and read by conversation, so a wide-column store such as Cassandra (partition key = conversationId, clustered by sequence number) fits; profiles and group membership can stay relational.
Groups
- Store a group message once and fan it out to each member's connection or push queue; fine for small groups.
Presence
- Clients send heartbeats; missing them for, say, 30–60 seconds marks the user offline and sets "last seen". Push presence changes only to contacts currently viewing that user.
Follow-ups: end-to-end encryption (server stores only ciphertext), media via object storage links, multi-device sync.
Original UniCircle question. Last reviewed 28 Oct 2026.
