Requirements & scope
Problem Statement & Requirements
Functional Requirements
- 1:1 messaging — send and receive text messages between two users in real-time
- Group messaging — support group chats with up to 500 members (Slack-style) or 1024 (WhatsApp-style)
- Online/offline presence — show whether a user is online, offline, or "last seen"
- Message delivery status — sent, delivered, read (double/blue checkmarks)
- Push notifications — notify offline users of new messages
- Media sharing — images, videos, documents (up to 100 MB)
- Message history — persistent storage, searchable, sync across devices
- End-to-end encryption (E2E) — messages encrypted on sender, decrypted only by recipient
Non-Functional Requirements
- Low latency — message delivery < 100 ms (same region), < 300 ms (cross-region)
- High availability — 99.99% uptime (< 52 min downtime/year)
- Ordering — messages within a conversation must be displayed in order
- Durability — no message loss once server acknowledges
- Scalability — support 500M+ daily active users, 100B+ messages/day
- Multi-device sync — seamless experience across phone, web, desktop
Out of Scope
- Voice/video calling (that's a separate WebRTC design)
- Stories/status updates
- Payment integration
- Bots / slash commands (Slack-specific)
Scale estimations
Scale Estimations
Users & Traffic
| Metric | Value |
|---|---|
| Daily Active Users (DAU) | 500M |
| Average messages sent per user per day | 40 |
| Total messages per day | 500M × 40 = 20B |
| Messages per second (avg) | 20B / 86400 = ~231K msg/s |
| Peak messages per second | ~700K msg/s (3x average) |
| Average concurrent connections | ~150M (30% of DAU) |
Message Sizes
| Metric | Value |
|---|---|
| Average text message | 200 bytes |
| With metadata (sender, timestamp, IDs, status) | 500 bytes |
| Media messages (pointer only, media stored separately) | 500 bytes + media URL |
| Average message with encryption overhead | ~600 bytes |
Storage
| Metric | Value |
|---|---|
| Text storage per day | 20B × 600 bytes = ~12 TB/day |
| Text storage per year | 12 TB × 365 = ~4.4 PB/year |
| Media storage per day (20% of messages have media) | 4B × 500 KB avg = ~2 PB/day |
| Retention | Text: forever; Media: configurable |
Network
| Metric | Value |
|---|---|
| Incoming bandwidth (messages) | 231K × 600 bytes = ~139 MB/s |
| Outgoing bandwidth (fan-out) | ~278 MB/s (avg 2 recipients per msg including 1:1) |
| WebSocket connections (concurrent) | ~150M |
| WebSocket servers needed (50K conn/server) | ~3,000 servers |
Layered architecture
High-Level Architecture
High-Level ArchitectureExcalidraw diagram · editable shapes · reveal step by stepExplore
Detailed Component Architecture
Detailed Component ArchitectureExcalidraw diagram · editable shapes · reveal step by stepExplore
API & contracts
Communication Protocol — WebSocket Deep Dive
Why WebSocket Over HTTP?
Why WebSocket Over HTTP?Excalidraw diagram · editable shapes · reveal step by stepExplore
WebSocket Connection Lifecycle
WebSocket Connection LifecycleExcalidraw diagram · editable shapes · reveal step by stepExplore
Data model
Data Model
Message Table (Cassandra)
Message Table (Cassandra)Excalidraw diagram · editable shapes · reveal step by stepExplore
Conversation Table
Conversation TableExcalidraw diagram · editable shapes · reveal step by stepExplore
User Conversation Index (for "list my chats")
User Conversation Index (for "list my chats")Excalidraw diagram · editable shapes · reveal step by stepExplore
Core design decisions
Message Ordering & IDs
Why Not Auto-Increment IDs?
Why Not Auto-Increment IDs?Excalidraw diagram · editable shapes · reveal step by stepExplore
Message Ordering Guarantee
Message Ordering GuaranteeExcalidraw diagram · editable shapes · reveal step by stepExplore
Request flows
Message Flow Diagrams
1:1 Message — Both Users Online
1:1 Message — Both Users OnlineExcalidraw diagram · editable shapes · reveal step by stepExplore
1:1 Message — Recipient Offline
1:1 Message — Recipient OfflineExcalidraw diagram · editable shapes · reveal step by stepExplore
Group Message Flow
Group Message FlowExcalidraw diagram · editable shapes · reveal step by stepExplore
Performance & caching
Workshop note · added for the website’s common reading format
Start with the dominant access pattern of Chat System. Message queue is one place to inspect capacity and tail latency. Measure before introducing a cache: define the cache key, invalidation policy, stale-data budget, and cold-start behavior.
Batch independent work where the latency budget allows it. Bound queues and concurrency, and verify that an optimization does not move the bottleneck to a dependency.
Advanced design
Presence (Online/Offline Status)
Presence Architecture
Presence ArchitectureExcalidraw diagram · editable shapes · reveal step by stepExplore
Presence at Scale — The Problem
Presence at Scale — The ProblemExcalidraw diagram · editable shapes · reveal step by stepExplore
End-to-End Encryption (E2E)
Signal Protocol (used by WhatsApp)
Signal Protocol (used by WhatsApp)Excalidraw diagram · editable shapes · reveal step by stepExplore
E2E Encryption Flow
E2E Encryption FlowExcalidraw diagram · editable shapes · reveal step by stepExplore
Group E2E Encryption
Group E2E EncryptionExcalidraw diagram · editable shapes · reveal step by stepExplore
Message Queue & Delivery Guarantees
Kafka as Message Bus
Kafka as Message BusExcalidraw diagram · editable shapes · reveal step by stepExplore
Media Handling
Media HandlingExcalidraw diagram · editable shapes · reveal step by stepExplore
Multi-Device Sync
Multi-Device SyncExcalidraw diagram · editable shapes · reveal step by stepExplore
Chat Search
Chat SearchExcalidraw diagram · editable shapes · reveal step by stepExplore
Edge cases
Handling Edge Cases
Message Delivery Reliability
Message Delivery ReliabilityExcalidraw diagram · editable shapes · reveal step by stepExplore
WebSocket Connection Failure
WebSocket Connection FailureExcalidraw diagram · editable shapes · reveal step by stepExplore
Tradeoffs
Tradeoffs & Design Decisions Summary
| Decision | Option A | Option B | Chosen | Why |
|---|---|---|---|---|
| Protocol | HTTP Long Polling | WebSocket | WebSocket | Bidirectional, low overhead, real-time push |
| Message DB | MySQL (sharded) | Cassandra | Cassandra | Write-heavy (20B/day), partition by conversation, linear scaling |
| Message Queue | Redis Pub/Sub | Kafka | Kafka | Durability, replay, consumer groups, ordering per partition |
| Presence Store | Database | Redis (TTL) | Redis | In-memory speed, TTL auto-expiration, pub/sub for updates |
| Message IDs | UUID v4 | Snowflake ID | Snowflake | Time-sortable, compact 64-bit, no coordination |
| Delivery | Push only | Push + offline queue | Push + queue | Guaranteed delivery even for offline users |
| Encryption | TLS only (server-side) | E2E (Signal Protocol) | E2E | Privacy — server never sees plaintext |
| Group encryption | N×encrypt per message | Sender Key | Sender Key | O(1) encryption per message instead of O(N) |
| Presence fan-out | Push to all contacts | Hybrid (push active + pull on demand) | Hybrid | Bounded fan-out, scales to 500M users |
| Multi-device | Phone-primary | Server-primary with per-device keys | Server-primary | All devices work independently |
| Media upload | Through API server | Direct to S3 (presigned URL) | Direct S3 | Offloads bandwidth from API servers |
| Search | Server-side Elasticsearch | On-device search | Depends on E2E | E2E → device only; no E2E → Elasticsearch |
| Redirect | 301 vs 302 for links | N/A | N/A | N/A |
Reliability & fault tolerance
Workshop note · added for the website’s common reading format
Track accepted, delivered, and read states separately. Expect duplicate delivery and deduplicate by ID.
Set service-level objectives for the user-visible path, then map its dependencies. Define bounded retries with jitter, deadlines, and backpressure. Keep a degraded mode that protects authoritative state, and test recovery instead of treating replication as a backup.
For Chat System, pay special attention to Message queue, Message store, Presence store when deciding failure domains and recovery procedures.
Production architecture
Production Architecture
Production ArchitectureExcalidraw diagram · editable shapes · reveal step by stepExplore
Further exploration
Workshop note · added for the website’s common reading format
Rebuild Chat System from memory, then change one assumption: ten times more traffic, a new region, or a stricter consistency requirement. Which component must change first—and which does not?
Compare Message service with the same boundary in a related design. Write down one alternative you rejected, what it would simplify, and when you would choose it instead. Follow the source link at the end of this article to explore the original document.
Interview playbook
Interview Tips
Start with protocol choice — "For real-time messaging, WebSocket is the clear winner over polling." Briefly explain why, then move on.
Draw the 1:1 message flow first — It's the simplest. Show: sender → WS gateway → message service → persist + Kafka → delivery worker → recipient's WS gateway → recipient.
Address "what if the recipient is offline?" early — This is the follow-up every interviewer asks. Show the offline queue + push notification path.
Presence is a trap topic — Don't spend too long on it. Mention the fan-out problem, say "hybrid pull + push" and move on unless asked to deep-dive.
Group messaging is just fan-out — "A group message is a 1:1 message sent to each member. The interesting part is how to fan out efficiently" → Kafka with conversation-level partitioning.
E2E encryption shows depth — Mention Signal Protocol, X3DH, Double Ratchet. You don't need to derive the math — just show you understand why keys rotate per message (forward secrecy).
Message ordering matters — Explain Snowflake IDs and why Kafka partitioning by conversation_id guarantees FIFO within a conversation.
Multi-device sync is a common follow-up — "Sync cursor per device. On reconnect, replay from cursor."
Don't forget media — "Media goes directly to S3 via presigned URL. Message contains only a pointer. CDN for delivery." This 3-sentence answer covers it.
Scale numbers impress — "500M DAU × 40 messages = 20B messages/day = 231K messages/second. We need ~3,000 WebSocket servers at 50K connections each."