System design

Design a Chat App (WhatsApp Web)

← Back to System design

Difficulty: 🔴 Hard · Est. time: 1h · Tags: #realtime #websocket #offline #optimistic-ui

Asked at: Meta, Slack, Discord, Uber · Related: News Feed · Google Docs · Interview Patterns


1. The Question

Design the frontend for a real-time chat app (WhatsApp Web / Messenger): conversation list, message thread, send/receive in real time, delivery/read receipts, typing indicators, and offline support.

2. Requirements

Functional

Non-functional

3. High-Level Design

┌─────────────┐  WebSocket (bidirectional)  ┌──────────┐
│  Chat Core  │ ◀─────────────────────────▶ │  Gateway │
│  - socket   │      messages, receipts,     └──────────┘
│  - outbox   │      typing, presence
│  - stores   │
└──────┬──────┘
       │ selectors

[Conversation list]  [Message thread (virtualized)]  [Composer]
  • Transport: WebSocket for bidirectional, low-latency messaging. Fall back to SSE/long-polling if WS is blocked.
  • Local stores: normalized messages + conversations; an outbox for pending sends.
  • Persistence: IndexedDB for offline history + queued messages.

4. Deep Dives & Trade-offs

Transport choiceWebSocket (server can push; low overhead). SSE is server→client only (no send channel); long-polling is a fallback. Discuss reconnection with exponential backoff + jitter and resubscribe-on-reconnect.

Optimistic send + reconciliation → render the message immediately with a temp client id and "sending" state. On server ack, replace temp id with the real id and mark "sent". On failure, show retry. This is why you key messages by a client-generated id, not the server id.

Ordering & dedupe → messages can arrive out of order or twice (reconnect replays). Order by server timestamp/sequence; dedupe by id. Detect gaps (missing sequence) and backfill via history fetch.

Offline → queue sends in the outbox (IndexedDB); on navigator.onLine/reconnect, flush in order. Show per-message state (queued → sent → delivered → read).

Receipts & typing → separate lightweight events. Typing indicators are throttled and ephemeral (don't persist). Read receipts update on viewport visibility (IntersectionObserver).

Long threads → virtualize the message list; load history in pages scrolling up while preserving scroll position (anchor to the first visible message). Evict far-offscreen messages from the DOM.

Consistency → treat the server as source of truth; local optimistic state is provisional until acked.

5. What Interviewers Probe

  • WebSocket vs SSE vs polling — when each?
  • How do you guarantee ordering and no duplicates across reconnects?
  • Optimistic UI: temp ids, ack reconciliation, failure/retry.
  • Offline queue + sync strategy.
  • Reconnection/backoff and resubscribe.
  • Virtualizing a thread while paginating history upward.
  • Read receipts via visibility.

6. Curated Resources

ESC

Type to search across every question bank.

navigate open 3,610 questions indexed