System Design (High-Level Design) Module 24 – Case studies: messaging, real time and collaboration
Design team chat like Slack or Discord
Design team chat for workspaces and huge communities: channels, roles and threads, channel servers, permission-aware search and apps with safe limits.
What you will learn
- Estimate messages, deliveries, connections and storage for a team chat service
- Design workspaces, channels, roles and threads
- Scale channel fan-out, presence and typing to communities of hundreds of thousands of members
- Build permission-aware search, safe integrations and tenant isolation
Before you start
What you’ll learn · Before you start
What you will learn
- Estimate messages, deliveries, connections and storage for a team chat service
- Design workspaces, channels, roles and threads
- Scale channel fan-out, presence and typing to communities of hundreds of thousands of members
- Build permission-aware search, safe integrations and tenant isolation
Before you start
On this page
To design team chat like Slack or Discord for 500,000 workspaces, start from the tenant. A workspace owns its members, roles, channels, apps and search, and every request and access check names it. Inside it, channels hold messages in the order of a sequence number per channel, kept for good and searchable; threads are replies keyed by their parent message. A post is checked against the channel’s permissions, numbered and stored, then handed to the channel’s owner, a channel server chosen by hashing the channel id, which fans it out to the gateways whose members are subscribed. A change log feeds the search indexes, which filter results by what the searcher may read, and the apps, which get signed events within per-workspace limits. Communities with hundreds of thousands of members add one more rule: presence and typing go only to people who are looking.
Not affiliated
Based on public information about Slack and Discord. MySmartCoPilot is not affiliated with Slack and Discord, and Slack and Discord’s real architectures may differ from what this lesson describes.
A few giants carry the load, and every request names its workspace
Ask these first; the answer this lesson assumes follows each question:
- What is a workspace? A tenant: a company or a community, with its own members, roles, channels and apps. Discord’s developer documentation calls one a guild.
- How large? 500,000 workspaces and 20 million people a day; most workspaces have fewer than 100 members, the largest company 200,000 and the largest community a million.
- Which channels? Public channels anyone in the workspace may join, private channels for invited members, direct messages, and threads of replies under a message.
- How long is history kept? For good, unless a workspace sets a retention period, and all of it is searchable.
- Which integrations? Apps and bots that receive events and post messages, and incoming webhooks.
An interviewer who asks you to design team chat like Slack usually wants three things beyond one-to-one delivery: permissions that differ per channel, search over years of history, and apps that must not hurt other workspaces. People post, edit, delete, react and reply in threads; mention one person or a whole channel; search; manage roles; and install apps. The non-functional requirements: a post reaches online members within a second; nobody ever reads a channel they may not see, in the app or in search; one noisy workspace cannot slow the others; and nothing posted is lost.
# The estimate sheet of a team chat service (lesson system-design-hld/design-team-chat): workspaces with channels,
# history kept and searchable, apps that receive events. Every constant is an assumption with a name, so you can put
# your own numbers in and run it again. Sizes are decimal (1 TB = 10**12 bytes).
DAY = 86_400
PEAK = 3 # working hours concentrate the day's messages: the busiest hour against the daily average
DAILY_USERS = 20_000_000
WORKSPACES = 500_000
MESSAGES_EACH = 40 # messages a person sends in a working day
ONLINE_MEMBERS = 25 # members of a channel who are online when a message is posted, on average
DEVICES_EACH = 1.3
ONLINE_AT_PEAK = 0.6 # share of devices connected at the busiest hour
PER_GATEWAY = 70_000 # connections per gateway: 100,000 it can hold, run at 70 %
MESSAGE_BYTES = 1_000 # a message as stored: text, mentions, reactions and edits with their metadata
REPLICAS = 3
APP_EVENTS_EACH = 0.5 # events sent to installed apps per message, on average
per_day = DAILY_USERS * MESSAGES_EACH
peak = per_day / DAY * PEAK
sockets = DAILY_USERS * DEVICES_EACH * ONLINE_AT_PEAK
year = per_day * MESSAGE_BYTES * 365
print("Team chat: the estimate sheet")
print(f" Messages {per_day / 10**6:,.0f} million a day: {per_day / DAY:,.0f}/s on average, {peak:,.0f}/s at the peak")
print(f" Deliveries {peak * ONLINE_MEMBERS:,.0f}/s at the peak, to {ONLINE_MEMBERS} online members per message")
print(f" Connections {sockets / 10**6:.1f} million at the peak, on {-(-sockets // PER_GATEWAY):,.0f} gateways of {PER_GATEWAY:,}")
print(f" Storage {per_day * MESSAGE_BYTES / 10**9:,.0f} GB a day, {year / 10**12:,.0f} TB a year, {year * REPLICAS / 10**12:,.0f} TB with {REPLICAS} replicas, kept for good")
print(f" App events {peak * APP_EVENTS_EACH:,.0f}/s at the peak, signed, retried, limited per workspace")
print(f" A workspace {DAILY_USERS / WORKSPACES:,.0f} people and {per_day / WORKSPACES:,.0f} messages a day on average, a mean that hides the giants") Output
Team chat: the estimate sheet Messages 800 million a day: 9,259/s on average, 27,778/s at the peak Deliveries 694,444/s at the peak, to 25 online members per message Connections 15.6 million at the peak, on 223 gateways of 70,000 Storage 800 GB a day, 292 TB a year, 876 TB with 3 replicas, kept for good App events 13,889/s at the peak, signed, retried, limited per workspace A workspace 40 people and 1,600 messages a day on average, a mean that hides the giants
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 team_chat_sheet.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
The sheet shows a different balance from consumer chat. Messages are few, 27,778 a second at the peak, but each reaches 25 online members on average, so deliveries reach 694,444 a second, and history kept for good grows by 292 TB a year before replication. The average workspace, 40 people and 1,600 messages a day, describes almost none of them: a few giants carry most of the load, and the design has to survive them. Discord’s engineers met the same skew in storage: they partitioned messages by channel and a time bucket sized from their largest channels (Discord).
Checkpoint
Check the estimate
The first design
The write path, the fan-out path and the change log of team chat
Text description of the diagram
Members' apps connect to gateways and call the message API of their workspace.
- An app posts a message to the message API.
- The API asks the permissions service whether this member may post in this channel, from the workspace's roles, the channel's overwrites and its memberships.
- It gives the message the channel's next sequence number and stores it in the message store, whose partitions are keyed by channel and bucket.
- It hands the message to the channel's owner, one of the channel servers, chosen by the channel's id.
- The channel server fans the message out to every gateway that has subscribed to the channel on behalf of its connected members.
- Each gateway delivers it to the members' apps.
- Every stored change also goes to a change log, which feeds the search indexes, routed by workspace, and the events that integrations receive, signed and rate-limited.
A dashed arrow shows members' apps querying search, whose results are filtered by the searcher's permissions.
Follow one post through it. The message API checks the member’s permission to post in the channel, gives the post the channel’s next sequence number and stores it. The channel’s owner, one channel server picked by the channel’s id, sends it once to every gateway that subscribed to that channel, and each gateway delivers it to its connected members. The stored change goes to a log, which feeds the search index and the events that apps receive. Step through it:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- A post arrives. A member posts in a channel of a workspace. The request names the workspace and the channel, as every request does.
- Checked first. The message API checks that this member may post in this channel. A post that fails the check is refused here and goes nowhere else.
- Numbered and stored. The post gets the channel's next sequence number and is stored before anything is delivered, so nothing reaches anyone that could later be lost.
- To the channel's owner. The API hands the post to the channel's owner, the one channel server that a hash of the channel id picks.
- Once per gateway. The owner sends the post once to every gateway that subscribed to the channel, not once per member.
- Delivered locally. Each gateway delivers the post to its own connected members. At the peak a post reaches 25 members online on average, which makes 694,444 deliveries a second.
- Search and apps read the log. The stored change goes to a change log, which feeds the search index and the events that apps receive. A slow index or a slow app never delays a delivery.
The questions this design has to answer:
- How are workspaces, channels, roles and threads stored, and how large may a channel’s partition grow?
- How does one post reach 10,000 online members without 10,000 sends from one server?
- How do roles and channel overwrites combine, and in which order?
- What stops a member removed from a private channel from seeing the next post?
- What do presence and typing cost in a community of 100,000, and how is that cut?
- How is search laid out for 500,000 workspaces, and how does it respect permissions?
- How do apps receive events safely, and what keeps one app or one workspace from hurting the rest?
Most of these come down to the same discipline: every request carries its workspace, and every limit is counted per workspace.
Checkpoint
Check the first design
Workspaces, channels, roles and threads as data Pro: opens with a Pro, Premium or Ultimate pass
In this part:
- Messages partitioned by channel and bucket, with sizes
- Channel servers: one owner per channel, fan-out to gateways
- Permissions: roles, overwrites and their order, in code
- Removing someone from a private channel while posts are in flight
- Huge communities: presence and typing, measured
- Search that respects channel permissions, with the shards counted
- Apps and webhooks: signed events, retries and limits per workspace
- Failure drills, from a lost channel server to an @everyone
- Bottlenecks at 10× and 100×, and the designs rejected
- The deep-dive questions, answered
- Expectations by level, and a practice round
- Practise: the last check before search results are shown
- Filter search hits by workspace and channel access
- Interview questions
- Why is the workspace the first thing to design in team chat?
- Someone posts @everyone in a community of 800,000 members, and notification delivery falls behind for every workspace for twenty minutes. What happened, and how do you design mentions so that one workspace cannot do this again?
- An employee is removed from a private incident channel. A message posted a second later still reaches her laptop, and the next morning she finds another one in search. Find every path that let this happen, and fix them so that none can.
- Your team chat has 500,000 workspaces, from three people to 200,000, and all history is searchable. Design the search index layout and the permission filter, and say what you do when one company's index outgrows its shards.
- Key takeaways
4 diagrams · 3 runnable examples (JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 51 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Read a complete sample first: Design a chat app like WhatsApp: delivery
References
- Real-time messaging (Slack Engineering) (Slack)
- How Discord stores trillions of messages (Discord)
- How Discord stores billions of messages (Discord)
- Permissions (Discord developer documentation) (Discord)
- Gateway (Discord developer documentation) (Discord)
- Gateway events (Discord developer documentation) (Discord)
- Zanzibar: Google’s Consistent, Global Authorization System (USENIX ATC (Pang and others))
- Standard Webhooks specification (Standard Webhooks)
- The Events API (Slack developer documentation) (Slack)
- Rate limits (Slack developer documentation) (Slack)
- Verifying requests from Slack (Slack)
- Clusters, nodes and shards (Elasticsearch) (Elastic)
- Size your shards (Elasticsearch) (Elastic)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress