System Design

Design WhatsApp & Chat System

WhatsApp-scale messaging — WebSockets, delivery guarantees, group chat fan-out, and presence at 500M+ users.

Interview tip Cover online presence, per-channel message ordering, at-least-once delivery + client idempotency, and fan-out on write vs read for groups.

① Functional requirements

  • Send/receive text messages in 1:1 and group conversations
  • Message history: paginated fetch on login and scroll-up
  • Delivery status: sent → delivered → read receipts
  • Online/offline presence and "last seen" (privacy configurable)
  • Push notification when recipient offline
  • Media attachments (images, files) via separate upload flow
  • Group chat: create, add/remove members, admin roles
Out of scope (state in interview)
  • Voice/video calls (signaling only mention)
  • Full E2E encryption implementation details
  • Message editing/deletion sync (brief mention)
  • Federation across providers

② Non-functional requirements

  • Message delivery latency < 500ms p99 for online users
  • 99.9% availability; messages never lost (durable)
  • Support 50B messages/day (~580K msg/sec avg)
  • Per-conversation ordering guarantee
  • End-to-end encryption (mention as optional advanced feature)

③ Back-of-the-envelope scale

Assumptions
  • 500M DAU, 50B messages/day → ~580K writes/sec avg, ~2M/sec peak
  • Avg message 200 bytes text; 10% with 500KB media metadata
  • Storage: 50B × 200B × 365 × 5yr ≈ 1.8 PB text (replication 3× → 5+ PB)
  • WebSocket connections: 100M concurrent (20% DAU online)
  • Group fan-out: avg 10 recipients × 580K = 5.8M inbox writes/sec
  • Presence updates: 100M users × heartbeat/30s ≈ 3.3M updates/sec
Group fan-out dominates write amplification. Hybrid: fan-out on write for groups < 100 members; fan-out on read for larger channels. Presence uses Redis with TTL — approximate is OK.

④ High-level architecture

Chat System Architecture
Mobile / Web Clients
WebSocket Gateway (sticky sessions)
REST API (history, groups)
Chat Service (route, seq IDs)
Presence Service (Redis)
Message Queue (Kafka)
Group Fan-out Workers
Message Store (Cassandra)
Push Service (APNs/FCM)
Media Store (S3 + CDN)
Send msgAssign seq IDPersist + fan-outPush to online clients
Layer 1 — WebSocket Gateway (connection state)
Layer 2 — Chat Service (routing, seq IDs)
Layer 3 — Message Store + Fan-out Queue
Layer 4 — Push / Media / Presence
WebSocket gateway is stateful — sticky load balancing by connection ID. Chat service is stateless. Each conversation has monotonic sequence number assigned by chat service (or per-sender for 1:1).

⑤ Data flow & execution path

End-to-end execution flow
① Client② API / LB③ Core services④ Cache + DB⑤ Message queue⑥ Async workers
Sync path: validate → authorize → read/write primary store
Async path: publish domain events → consumers (email, analytics, search index)
Read-heavy path: CDN / edge cache → regional cache → DB replica
Failure path: retry with backoff, DLQ, idempotent handlers
In interviews, trace one user action through this diagram. State what is synchronous (user waits) vs asynchronous (background), and where you enforce idempotency.

⑥ API & interfaces

Endpoint / flowPurposeNotes
WS: send_messageReal-time send{convId, clientMsgId, body, type} → ack with serverMsgId + seq
WS: message_deliveryServer push{serverMsgId, seq, sender, body, timestamp}
GET /v1/conversations/{id}/messages?before_seq=HistoryPaginated 50 msgs; cursor on seq number
POST /v1/conversationsCreate 1:1 or groupReturns convId; dedupe 1:1 by sorted user pair
POST /v1/media/uploadAttachmentPresigned S3 URL; attach mediaId to message
GET /v1/users/{id}/presenceOnline statusReturns online|offline|last_seen (privacy filtered)

⑦ Data model & storage

messages (Cassandra, partition by conv_id): conv_id, seq (clustering), sender_id, body, media_ref, ts

user_inbox (fan-out): user_id, conv_id, last_seq, unread_count

user_devices: user_id, device_id, push_token, ws_gateway
StoreWhatWhy
Cassandra / HBaseMessages by conversationWide-column; partition conv_id; seq ordering native
RedisPresence + unread countsTTL heartbeat keys; pub/sub for online events
KafkaFan-out + push eventsDurable buffer; replay on consumer failure
S3Media blobsPresigned upload; CDN for download

⑧ Deep dive — core components

Message ordering & idempotency

Per-conversation monotonic seq assigned by chat service (single partition per conv or lightweight lock). Clients send clientMsgId UUID — server dedupes on (sender_id, clientMsgId) to handle retries. Display order = seq, not client timestamp (clock skew).
Delivery: at-least-once over WebSocket. Client acks serverMsgId; server retries unacked for 30s. Idempotent handler on client prevents duplicate display.

Group fan-out strategy

Fan-out on write: For each message, write to each member's inbox table. Fast reads (just read inbox). Write cost = O(group_size). Good for groups < 100.
Fan-out on read: Store message once per conversation; on read, merge conversations user belongs to. Good for large channels. Hybrid: write fan-out for small groups, read fan-out for > 256 members.

WebSocket connection management

100M concurrent connections → ~50K connections per gateway node (2K nodes). Sticky sessions via consistent hash on user_id. Gateway maintains user_id → socket map in memory. On disconnect, presence TTL expires in 60s.
Cross-gateway delivery: chat service publishes to Kafka topic partitioned by user_id; target gateway consumes and pushes to local socket. Alternative: Redis pub/sub per gateway for lower latency.

Offline sync & push

Offline user: message written to inbox; push service sends FCM/APNs with payload {convId, preview}. On reconnect, client calls GET messages?after_seq=last_known to catch up.
New device: full inbox sync from user_inbox table, then per-conversation history paginated. Compress with gzip; delta sync using seq cursors.

⑨ Trade-offs & alternatives

DecisionOption AOption BPick when
Group deliveryFan-out on writeFan-out on readWrite for small groups; read for broadcast channels
OrderingGlobal seq per convLamport timestampsSeq simpler; Lamport for multi-master geo
PresenceHeartbeat + Redis TTLExact connection trackingTTL approximate; exact costly at scale
StoragePer-user inbox copyPer-conversation onlyInbox copy for fast read; conv-only saves writes
ProtocolWebSocketLong pollingWebSocket for real-time; polling fallback only

⑩ 45-minute interview script

  1. 0–5 min: Clarify 1:1 vs group, receipts, presence, offline behavior
  2. 5–12 min: Scale — messages/day, concurrent connections, fan-out math
  3. 12–22 min: Architecture diagram — gateway, chat service, store, push
  4. 22–32 min: Deep dive ordering, seq IDs, idempotency
  5. 32–38 min: Group fan-out hybrid strategy
  6. 38–42 min: Presence, WebSocket scaling, cross-gateway routing
  7. 42–45 min: Offline sync on new device

⑪ Likely follow-up questions

QuestionShort answer
Exactly-once delivery possible?Practically no over unreliable network; at-least-once + idempotent client is standard
How to handle 10K-member group?Fan-out on read only; store single message; members pull on open
Message deleted for everyone?Tombstone message with deleted flag; push delete event; clients purge locally
End-to-end encryption impact?Server cannot read body; push shows "new message"; key exchange out of band
Gateway crashes with 50K connections?Clients reconnect to another gateway; exponential backoff; resume from last seq
Read receipt privacy?User setting controls send/read; server strips receipt if recipient disabled

⑫ Revision checklist

  • WebSocket gateway with sticky sessions
  • Per-conversation sequence numbers
  • Client message UUID for dedup
  • Fan-out on write vs read trade-off
  • Cassandra partitioned by conversation
  • Presence via Redis TTL heartbeat
  • Push for offline users (FCM/APNs)
  • Delivery ack and retry semantics
  • Scale: 50B msgs/day math
  • Cross-gateway message routing
WebSocketmessagingfan-outreal-timeCassandra