Design Slack
The setup
"Design Slack. Focus on the channel-based messaging core: users in channels exchange messages in real time; channels can have thousands of members; the message history is searchable. 10M concurrent users; 100K messages/sec peak."
What's different from WhatsApp: WhatsApp is 1:1 (or small groups). Slack is many-to-many through channels. Each channel has potentially thousands of members. Posting a message must fan out to every member's connected device.
Clarifying questions
| Question | Story answer |
|---|---|
| Max channel size? | Up to 100K members (busy companies have huge #general channels). |
| Message edit/delete? | Yes — updates propagate. |
| Threading? | Yes — but treat threads as separate channels for design. |
| Search? | Mention briefly; full-text search across all messages user has access to. |
| Mobile + desktop? | Yes; mobile may be intermittent. |
Design Decision 1: channel-based pub/sub
When user A posts in #general (5,000 members), the message must reach 5,000 connected clients. Naive: server iterates members and sends individually → 5,000 messages per post → at 100K posts/sec, 500M outbound messages/sec.
Better: per-channel pub/sub. Each WSS client subscribes to topics for the channels they're in. Posting publishes once; the pub/sub layer fanout-delivers to subscribers.
Client (subscribed to: #general, #engineering, #random) ─┐
│
[WSS Gateway] │
│
subscribed to topics: #general, #engineering, #random
│
↓
[Pub/Sub layer]
↓ ↓ ↓
#general #engineering #random
When a message arrives in #general:
- Posted via API to Message Service
- Persisted to Cassandra (channel history)
- Published to pub/sub channel "#general"
- Each gateway with at least one subscriber to "#general" receives the message
- Gateway forwards to each subscribed client via their WSS
This is fundamental to many-to-many messaging. Pub/sub absorbs the fan-out.
Design Decision 2: the Slack Flannel pattern
Slack famously built "Flannel" — a per-datacentre service that caches all channel/user/team metadata at the edge.
The problem Flannel solves: every UI interaction needs context (what channels am I in? who's in this channel? what are my permissions?). Naively, each interaction queries backend databases — slow under load.
Flannel: each datacentre holds a complete in-memory picture of the company's metadata (channels, members, permissions). Clients query Flannel, not the backend, for the bulk of UI needs. Flannel synchronises with backend asynchronously.
Architectural lesson: when you have richly-relational metadata accessed on the hot path, a dedicated metadata cache tier is enormously valuable.
Design Decision 3: message storage
Channel messages partitioned by (channel_id, time_bucket) in Cassandra. Within a partition, messages clustered by timestamp. Same pattern as WhatsApp; channel_id replaces conversation_id.
For search: messages indexed in Elasticsearch (or similar). User-facing search APIs query the search index, not Cassandra. Permission filtering applied at query time (you only see messages from channels you're a member of).
Cross-examination round 1: presence at scale
Cross-examination round 2: thread reply notifications
Takeaway
Slack-class messaging is fundamentally pub/sub with rich metadata caching. Pub/sub handles fanout to thousands of concurrent connections per channel. The metadata cache (Flannel) ensures every interaction has fast access to "who can see what" without backend hits. The general lesson: any system with rich relational metadata accessed on the hot path benefits enormously from a metadata caching tier.
The deeper insight: permission and visibility computation is a hot path that's often overlooked. "Show me this channel" requires knowing "is this user a member?" — which requires permission data. At Slack scale, this metadata is queried millions of times per second. Caching it at the edge is essential.