“Design Discord” is a senior+ frontend system-design prompt that combines real-time messaging, voice/video, presence at scale, and a large client-side state graph. Discord’s desktop client is famous for being a heavy Electron app; the frontend interview tests whether you can decompose its features into a clean state architecture.
Clarify scope
- Servers (guilds), channels, DMs?
- Voice and video channels included?
- Threads, forum channels, stage channels?
- Offline / cached state?
- Cross-platform (desktop + mobile parity) or web only?
The state graph
Discord’s state is large; the most-cited Discord engineering post described moving from Redux-of-everything to a more scoped store. The shape:
- guildsById — Map of all servers the user is in
- channelsById — Map of channels (across all guilds and DMs)
- messagesByChannel — Map of channel ID → (message list, cursor)
- membersByGuild — guild members (often huge)
- presenceByUserId — online state, custom status
- voiceStates — who is in which voice channel, mute, deafen
- readState — last-read message per channel
Memory matters
A user in 100 servers with 50 channels each can have data for 5,000 channels in memory. Strategies:
- Lazy-load message history per channel; do not preload all
- Drop messages from inactive channels under memory pressure
- Member lists for very large guilds (100K+) are virtualized and lazily fetched
- Discord historically stored a large in-memory cache; modern variants page to disk via IndexedDB
WebSocket gateway
- Single persistent connection per client
- Receives all events: messages, presence, typing, voice state, member joins
- Sharded server-side; client is unaware
- Heartbeat every 41 seconds (Discord-specific); reconnect with sequence number on disconnect
- Resume protocol replays missed events
Voice and video
- WebRTC for media; Discord runs custom SFU servers
- Voice channel join: signaling over the gateway, then RTP to the SFU
- Video and screen share are layered on the same SFU
- Krisp-style noise suppression in the client (CPU)
- Voice activity indicator updates every ~30ms during talking
Channel switching
Snappy channel switch is a Discord differentiator:
- Pre-fetched recent messages for the most-recently-used channels
- Rendered placeholder while paginating older messages
- Compose box state preserved per channel (drafts)
- Typing-indicator state preserved (do not flash)
Message rendering
- Virtualized list — only visible messages mounted
- Variable-height messages (text, image, embed, code block)
- Markdown rendering with custom Discord syntax (mentions, channel links, custom emoji)
- Edit / delete updates by message ID; tombstones for deletes
- Reactions update the reaction map without re-rendering the message body
Presence at scale
- Discord delivers presence for the visible member list, not all members
- Subscribe to presence updates for the displayed members; unsubscribe when scrolled away
- For very large guilds, presence is fanned out via lazy queries
Notifications and unread state
- Per-channel last-read message ID, persisted on the server
- Mention bumps a separate counter
- Per-guild “any unread” derives from channel unread states
- Sidebar shows unread bubbles; navigation marks-as-read
Threads and forum channels
- Thread = child channel of a parent channel
- Auto-archive after inactivity
- Forum channel = root with many threads, no top-level chat
- State stored in same channel/message maps with a parent link
The Electron / web split
- Discord desktop is Electron — same codebase as web with native integrations
- Native: push-to-talk hotkey (global), system overlay, autostart
- Web: subset; voice still works, push-to-talk limited
- Mobile: separate React Native codebase
Offline behavior
- Cached message store in IndexedDB for recently-active channels
- Queued send: messages composed offline are sent on reconnect
- “Reconnecting…” banner; messages sent during disconnect remain optimistic until confirmed
Performance considerations
- The Discord engineering post about reducing CPU on the message list is a classic; rendering tens of messages efficiently matters
- Memoize messages by ID
- Avoid context that re-renders entire trees on small updates
- Profile with React DevTools; focus on the most-mounted components
What separates senior from staff
Senior candidates draw the state shape and discuss virtualization. Staff candidates address memory pressure across thousands of channels, the WebSocket reconnect/resume protocol, and the lazy-presence subscription model. Principal candidates raise the cross-platform architecture (web/desktop/mobile) and the platform-specific tradeoffs.
Frequently Asked Questions
How does Discord handle a guild of 1M members?
Member list is lazy and chunked; full member fetch is rate-limited. Presence subscriptions are scoped to the visible window. The client never has 1M members in memory at once.
What about end-to-end encryption?
Discord does not E2E-encrypt by default (server-readable for moderation). Voice DM E2E encryption was added recently. If asked about E2E DM design, see the Signal-style mobile system design question.
Why is the desktop app heavy?
Electron + the size of state + voice processing. There is no fundamental fix; the team has reduced memory and CPU significantly over the years but the architecture has costs.