System Design: Design Slack — Enterprise Messaging, Channels, Threads, Real-Time Delivery, Search, File Sharing

Slack handles millions of concurrent users across hundreds of thousands of workspaces, delivering messages in real-time while supporting search, file sharing, threads, and integrations. Designing an enterprise messaging platform like Slack combines real-time communication (WebSocket), search (Elasticsearch), file management (S3), and multi-tenant architecture. This guide covers the key components for a system design interview.

Data Model

Core entities: (1) Workspace — a Slack organization. Has members, channels, and settings. Each workspace is a tenant in the multi-tenant system. (2) Channel — a conversation space within a workspace. Types: public (any workspace member can join), private (invite-only), and DM (direct message between 2+ users). Attributes: channel_id, workspace_id, name, topic, member_list, created_at. (3) Message — a message in a channel. Attributes: message_id (time-sorted, Snowflake-style), channel_id, user_id, text, thread_ts (parent message timestamp for threaded replies), attachments, reactions, edited_at, deleted_at. (4) Thread — a reply chain under a parent message. thread_ts links replies to the parent. The parent message shows a reply count and preview of the latest reply. (5) User — workspace member. Attributes: user_id, workspace_id, display_name, avatar_url, status, presence (online/away/offline). Database: messages are the highest-volume entity. Store in a database partitioned by channel_id (all messages in a channel are co-located for efficient retrieval). Use a time-sorted message_id as the clustering key for chronological ordering within a channel. Cassandra or sharded PostgreSQL.

Real-Time Message Delivery

When User A sends a message in #general: (1) The message API receives the POST request, validates it, and stores it in the database. (2) The message is published to a Kafka topic partitioned by channel_id. (3) The real-time delivery service consumes from Kafka and delivers to all online members of the channel via WebSocket. Per-channel fan-out: each channel has a list of members. For each online member, look up their WebSocket connection server and push the message. For a channel with 1,000 members where 300 are online: 300 WebSocket pushes. Connection management: each user maintains a WebSocket connection to a gateway server. A Redis hash maps user_id -> gateway_server_id. When a user connects, register in Redis. On disconnect, remove. The gateway server holds thousands of concurrent WebSocket connections. Multiple gateway servers behind a load balancer. Offline delivery: users not currently connected receive the message when they next open Slack. The client sends a “last read” cursor; the server returns all messages after that cursor. Push notifications: for mentions (@user) and DMs, send a push notification to the user mobile device via APNs/FCM if they are offline or have the app backgrounded.

Message Search

Slack search indexes all messages for full-text search within a workspace. Architecture: messages are asynchronously indexed in Elasticsearch after being stored in the primary database. The Elasticsearch index is partitioned by workspace_id (each workspace is a separate index or index alias). Search query: the user searches “deployment failed” in their workspace. Elasticsearch searches the workspace index for documents matching both terms. Results are ranked by relevance (BM25 text score) and recency (newer messages score higher). Filters: by channel, by user, by date range, by file type. The search must respect access control: a user can only see messages from channels they are a member of. Implementation: the search query includes a filter on channel_ids the user belongs to. This filter is computed at query time from the user membership list. For workspaces with thousands of channels, this filter can be large. Optimization: maintain a per-user search permission bitmap (updated when the user joins/leaves channels). Search indexing lag: messages are available for search 1-5 seconds after being sent (async indexing). This is acceptable for search — users do not search for a message they just sent.

Threads and Reactions

Threads: a reply to a message creates a thread. The parent message has a reply_count and a list of reply_users (avatars shown in the channel view). Thread replies are stored as regular messages with thread_ts = parent message timestamp. Loading a thread: query messages WHERE channel_id = X AND thread_ts = parent_ts, ordered by message_id. Thread notifications: users who participate in a thread (replied or were mentioned) are subscribed to thread updates. New replies notify thread subscribers, not the entire channel. This prevents noise in busy channels. Reactions: users add emoji reactions to messages. Stored as a list of (emoji, [user_ids]) per message. Display: show the emoji and count (“thumbsup 5”). When a user reacts: update the message reaction list (atomic append) and push a reaction event to all online channel members via WebSocket. De-duplicate: a user can only react once per emoji per message. Check before adding. Message editing and deletion: edited messages update the text and set edited_at. Deleted messages set deleted_at (soft delete). The real-time delivery pushes edit/delete events to update the UI for all users viewing the channel. Deleted messages show “[This message was deleted]” in the UI.

File Sharing and Integrations

File uploads: similar to Instagram — presigned S3 URL for direct upload. The file is linked to a message. File metadata (name, size, type, thumbnail URL) is stored in the database. Images and PDFs get preview thumbnails generated by a processing pipeline (Lambda or worker). Files are scoped to the workspace and accessible only to channel members. Integrations/Apps: Slack supports bots and integrations via: incoming webhooks (external services POST messages to a Slack channel), slash commands (user types /deploy, Slack sends the command to the integration server, which responds with a message), interactive messages (buttons, dropdowns in messages that trigger callbacks to the integration server), and the Events API (Slack pushes events like message.posted, channel.created to the integration server via webhook). Integration architecture: each integration registers a callback URL. Slack sends HTTP POST requests to the URL when relevant events occur. The integration processes the event and optionally responds with a message. Rate limits prevent integrations from flooding channels. The Slack App Directory hosts thousands of third-party integrations (Jira, GitHub, Google Calendar). Each runs on the developer infrastructure and communicates with Slack via APIs.

Scroll to Top