Skip to content

Slack / Discord#

Problem statement (interviewer prompt)

Design Slack/Discord: workspaces with public + private channels, threads, reactions, DMs, mentions, bots, slash commands, voice rooms, and full-history search. Handle 5M concurrent WS connections, 3000 msgs/s sustained, and workspaces from 5 to 500k members.

flowchart LR
  C([Client])
  WS[WebSocket Gateway]
  CH[Channel Service]
  MSG[(Messages)]
  IDX[(Search index)]
  PUSH[Push]
  C --> WS --> CH --> MSG
  CH -. fan-out .-> WS --> C
  MSG --> IDX
  CH --> PUSH

    classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
    classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
    classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
    classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
    classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
    classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
    classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
    classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
    classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
    classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
    class C client;
    class WS edge;
    class CH,PUSH service;
    class MSG,IDX datastore;
flowchart TB
  subgraph Clients
    DESK[Desktop]
    MOB([Mobile])
    BR([Browser])
  end

  subgraph Edge
    GLB[Geo LB]
    GW[WebSocket Gateway<br/>per-workspace shard]
    REST[REST API Gateway]
  end

  subgraph Core
    AUTH[Auth / SSO]
    WS_SVC[Workspace Service]
    CH[Channel Service]
    MSG[Message Service]
    THR[Threads / Replies]
    REACT[Reactions]
    PRES[Presence Service]
    TYP[Typing]
    INV[[Inbox / Mentions]]
    NOTIF[Notification Service]
    FILE[File Service]
    OBJ[(S3)]
    APPS[Apps / Bots / Slash cmds]
    HOOK[Webhooks out]
  end

  subgraph Realtime[Real-time fan-out]
    PUB[[Pub/Sub fan-out<br/>per-channel topic]]
    BRIDGE[Region bridge]
  end

  subgraph Storage
    META[(Workspace metadata - SQL)]
    MSGS[(Messages - Vitess / MySQL sharded by workspace)]
    SEARCH[(Search index - Elasticsearch)]
    HUDDLE[(Voice / huddle state)]
  end

  subgraph Voice[Voice / Huddle / Stage]
    SIG[Signaling]
    SFU[Selective Forwarding Units]
    TURN[STUN / TURN]
  end

  subgraph ML
    SUM[Recap / summaries]
    SAFE([Spam / abuse classifier])
    RANK[Channel ranking / sort]
  end

  subgraph Obs
    M[Metrics]
    T[Traces]
  end

  Clients --> GLB --> GW
  Clients --> REST
  GW --> WS_SVC
  REST --> CH
  CH --> MSG --> MSGS
  MSG --> SEARCH
  CH --> PUB --> GW
  PUB --> BRIDGE
  CH --> REACT
  CH --> THR
  PRES --- GW
  TYP --- GW
  MSG --> NOTIF
  FILE --> OBJ
  APPS --> CH
  HOOK --> CH
  Voice --- GW
  ML --- MSGS
  WS_SVC --- META

    classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
    classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
    classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
    classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
    classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
    classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
    classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
    classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
    classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
    classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
    class MOB,BR client;
    class GLB,GW,REST edge;
    class DESK,AUTH,WS_SVC,CH,MSG,THR,REACT,PRES,TYP,NOTIF,FILE,APPS,HOOK,BRIDGE,SIG,SFU,TURN,SUM,RANK service;
    class META,MSGS,SEARCH,HUDDLE datastore;
    class INV,PUB queue;
    class SAFE compute;
    class OBJ storage;
    class M,T obs;

Workspace sharding#

  • A workspace lives on one logical shard: Vitess keyspace keyed by team_id.
  • All channels, messages, members for that workspace co-located.
  • WS gateway pins user to shard's gateway pool for stickiness.

Channels and fan-out#

  • Channel = topic in pub/sub bus.
  • Server subscribes WS sessions for each user's channels they have open.
  • Lazy fetch for channels not in foreground (load on switch).

Threads#

  • Thread = parent message id + child messages. Index for unread thread counts per user.
  • Per-workspace index in Elasticsearch (one index/team or rollover by month).
  • Permission-aware: ACL per channel; filter at query time.

Presence#

  • Aggregated per-workspace; expensive at large workspaces - sampled / lazy.

Voice / huddle#

  • Discord-style SFU for many-to-many; small numbers fine for mesh.

Bots / apps#

  • Apps install per workspace, talk via REST + Events API webhooks.
  • Slash commands and modals via interactive components API.

Glossary & fundamentals#

Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.

Tag Concept What it is Page
HLD API gateway / BFF single ingress, auth, rate limit, routing api-gateway
HLD Sharding horizontal partitioning across nodes database-sharding
HLD Pub/Sub & message brokers topics, consumer groups, delivery semantics pub-sub-pattern
HLD Observability metrics, logs, traces, SLOs observability
HLD Realtime protocols WS / SSE / polling / gRPC streaming realtime-protocols
LLD REST API design verbs, statuses, pagination, errors rest-api-design
LLD Async models futures / async-await / coroutines / actors async-models
LLD Structural patterns Adapter, Decorator, Facade, Proxy, Composite structural-patterns

Quick reference#

Functional#

  • Workspaces with channels (public/private), DMs, group DMs, threads.
  • Messages with reactions, edits, deletes, attachments.
  • Real-time presence, typing, notifications.
  • Search across history.
  • Bots, slash commands, webhooks.
  • Voice / huddles / stages (Discord stages).

Non-functional#

  • Real-time delivery < 1 s.
  • 99.99% uptime.
  • 12M+ DAU (Slack 2023), Discord 500M registered.
  • Workspaces from 5 to 500k members.

Capacity (Slack-scale)#

  • 2B+ messages/week → ~3300 msgs/s avg.
  • Concurrent WS connections: ~5M.
  • Per gateway box: ~50-100k connections.

Schema#

  • workspaces(id, plan, region)
  • channels(id, workspace_id, type, name, archived)
  • messages(id, channel_id, user_id, ts, text, thread_ts, edits[]) sharded by workspace
  • memberships(workspace_id, user_id, role)
  • reactions(message_id, user_id, emoji)

Trade-offs#

  • Workspace-level shard simplifies authorization but skews on huge teams.
  • Vitess + MySQL vs NoSQL: SQL chosen for ACID on team data.
  • Long-poll vs WS: WS for foreground, push for background, hybrid for resilience.
  • Search per-workspace keeps ACL simple; harder for cross-workspace enterprise grid.

Refs#

  • Slack engineering blog ("Scaling Slack", "Real-Time Messaging"), Discord engineering blog (Cassandra → ScyllaDB migration), ByteByteGo "Design Slack", Alex Xu Vol 2.

FAQ#

How does Slack scale WebSocket connections?#

A fleet of WS gateway servers sharded by user holds 100k connections each. A pub/sub bus like Kafka or Redis fans out channel messages across all gateways in milliseconds.

How is workspace isolation enforced?#

Every request carries a workspace ID resolved from the auth token. Storage, search indexes, and gateway routing are keyed by workspace, blocking cross-tenant reads.

How does channel-based message fan-out work?#

On write, the channel service persists the message, then publishes to a topic for that channel. Gateways subscribed for online members in that channel push to their sockets.

How is full-history search built?#

Per-workspace Elasticsearch indexes hold messages with channel and user filters. Permissions are checked at query time so private channels stay private from non-members.

How do you handle 5M concurrent WebSocket connections?#

Run a horizontal fleet of WS servers behind an L4 load balancer, shard by user, offload TLS at the edge, and back fan-out with a partitioned pub/sub bus.

Further reading#

Curated, high-credibility sources for going deeper on this topic.

Video walkthrough

Design Slack (MVP): Frequently Asked at OpenAI, Meta & Databricks : via System Design Walkthrough