Courses › System Design › Question Breakdowns
Design Reddit
Votes as derived scores, the hot formula and precomputed listings, comment trees and "best" sorting.
Design a community discussion site like Reddit: users join communities (subreddits), post links and text, vote posts and comments up or down, and read deeply nested comment threads, with front pages ranked by "hot". It's a question about vote counting at scale, time-decaying ranking, storing and loading comment trees, and precomputed listings for read-heavy pages.
Requirements
Functional
- Users can create posts in a community and comment, with replies nested to any depth
- Users can upvote / downvote posts and comments (one vote each, changeable)
- Users can browse a community or their home feed sorted by hot, new, top (by time window)
- Comment threads load sorted by best/top, collapsing deep or low-scored branches
Out of scope: chat, moderation tooling (mention it), ads, awards, search.
Non-functional
- Read-heavy: ~100:1 reads to writes; listing pages load in < 200ms
- Scale: ~100M daily users, ~1M posts/day, ~10M comments/day, ~100M votes/day (~5k votes/s peak, far more on viral posts)
- Eventually consistent scores are fine (seconds); a user must see their own vote immediately
- Highly available
[!TIP] Scores are derived data The source of truth is "user U voted +1 on item I". Scores and rankings are aggregates recomputed from votes, asynchronously. Separating the vote record (needs uniqueness) from the score (needs speed) is the key to scaling voting — see Design Distributed Counters.
Capacity estimates
Core Entities
- Community — name, rules, moderators
- Post — community, author, title, body/link, created at, score, comment count
- Comment — post, parent comment, author, body, created at, score, depth/path
- Vote — user, item (post or comment), direction (+1 / −1)
API
POST /v1/communities/{name}/posts { "title": "...", "url": "..." }
POST /v1/posts/{postId}/comments { "parentId": "c_17", "body": "..." }
PUT /v1/votes/{itemId} { "direction": 1 } (idempotent: setting your vote)
GET /v1/communities/{name}/posts?sort=hot&after=...
GET /v1/posts/{postId}/comments?sort=best&depth=6&limit=200High-Level Design
user = client "Clients" @0,1
api = service "API Servers" @1,1
posts = store "Posts & Comments DB\n(sharded by post)" @2,0
votes = store "Votes DB\n(user, item)" @2,1
q = queue "Vote Events" @2,2
agg = service "Score Workers" @3,2
lists = cache "Listings\n(Redis sorted sets)" @3,1
cache = cache "Comment Tree Cache" @3,0
user -> api
api -> posts
api -> votes : set vote
votes --> q
q -> agg
agg -> posts : update score
agg -> lists : hot / top
api -> lists : read listing
api -> cache : read thread- Vote: the API upserts
(user, item, direction)— the unique key makes double votes impossible — and emits a vote event with the delta (e.g. changing −1 to +1 is +2). - Score workers batch deltas per item, update the item's score, and recompute its position in listing sorted sets (hot, top-day, top-week…).
- Listing pages read precomputed sorted sets; comment threads come from a cache of the post's comment tree.
Deep Dives
1. Votes without hot rows
A viral post gets thousands of votes per second; UPDATE posts SET score = score + 1 on one row won't keep up. Record votes in a table keyed by (user, item) (sharded by user or item), and apply score deltas asynchronously in batches (sum the deltas per item each second). The voter's own vote state is read from the votes table (or a per-user cache), so they see it immediately even though the public score lags.
2. Ranking: hot, top, new
Hot combines score and age so new posts can rise and old ones fade. Reddit's classic formula:
from math import log10
def hot(ups, downs, created_epoch):
s = ups - downs
order = log10(max(abs(s), 1)) # 10 votes ≈ +1, 100 ≈ +2: diminishing returns
sign = 1 if s > 0 else -1 if s < 0 else 0
return round(sign * order + (created_epoch - 1134028003) / 45000, 7) # +1 every 12.5 hoursBecause time is added (not multiplied), the score of an existing post never changes unless it gets votes — new posts simply start higher. So a listing's order only needs updating when votes arrive, which makes precomputed sorted sets practical.
- New: sort by created time.
- Top (day/week/all): score, filtered by window — one sorted set per window, entries expire out.
- Keep only the top ~1,000 per listing; nobody pages deeper.
3. Home feeds
A user's home feed merges hot listings from their subscribed communities (often 50–100). Merge the top N of each community's hot set at read time (cached per user for a minute or so) — like the read-side merge in News Feed, but per community instead of per followed user.
4. Comment trees
Threads can have tens of thousands of comments nested deeply. Options:
Load all comments for a post in one query (they share a shard keyed by post ID), build the tree in memory, sort siblings by "best" (a confidence-interval score like the Wilson lower bound, so 5/0 doesn't beat 500/50), and truncate deep or long branches ("load more replies"). Cache the rendered tree per post and sort order; invalidate or patch it as comments and votes arrive.
[!WARNING] Don't sort comments by raw score alone Raw score favors early comments forever and lets 2/0 outrank 200/30. "Best" sorting uses the lower bound of the confidence interval of the upvote ratio, which rewards both quality and enough votes to trust it.
5. Scale and hot posts
- Shard posts and comments by post ID; a thread lives on one shard.
- Viral threads get heavy reads: cache the comment tree and serve logged-out readers a slightly stale cached page from a CDN.
- Comment counts and scores are approximate in the UI ("1.2k"), so batching is invisible to users.
6. Abuse
Vote manipulation (bots, brigading) is filtered before scores count: rate limits per account, account-age weighting, and fraud models over the vote stream; suspicious votes are recorded but not applied.
What's expected at each level
- Mid-level: posts, nested comments, a votes table with one vote per user, sorting by score and time.
- Senior: async score aggregation from vote deltas, the hot formula and why it allows precomputed listings, materialized-path comment trees built in memory, caching.
- Staff: "best" ranking with confidence intervals, home-feed merging across communities, hot-thread handling, vote fraud filtering, and the trade-offs of approximate public counts.