Users add friends, post, and see a ranked news feed. Suggest people they may know. Billions of users.
In plain words
Your feed isn't newest first. Facebook gathers a few thousand recent posts from friends and pages, drops anything you're not allowed to see, then scores each one: how close you are to the person, how much people are reacting, how old it is. You see the top 20.
People you may know works like a party. You know Ana and Bob. Ana and Bob both know Cat. You've never met Cat, but two of your friends have, so Cat goes at the top of your suggestions.
Examples
- A post from your sister an hour ago beats a two day old post from someone you never talk to.
- A post with 200 likes can beat a close friend's quiet post.
- The friend graph is stored as edges sharded by user, behind a huge cache (Facebook's is called TAO).
- If ranking is slow, show a simple newest first feed instead of an empty screen.
1. Requirements
Functional
- Add friends and post with an audience.
- A ranked news feed.
- People you may know.
- Likes and comments.
Non functional
- Feed in under 300 ms for 2B daily users.
- Never show a post to someone outside its audience.
- The friend graph handles billions of reads.
- Graceful fallback when ranking is slow.
Questions to ask
- Ranked feed, or chronological?
- Which parts: feed, friends, suggestions, likes and comments?
- Privacy: friends only, public, custom lists?
- How fresh do suggestions need to be?
2. Estimates
| users | 2B daily, average 300 friends |
| feed loads | about 10 per user per day: 230k per second |
| candidates | a few thousand candidate posts per feed load, ranked down to 20 |
3. API
GET /feed?cursor=... [posts], nextCursor
POST /posts { text, audience } { id }
POST /friends/requests { userId } 202
GET /suggestions [people with mutual count]
4. Data model
posts(id, author_id, audience, created_at)
friendships(user_a, user_b) the social graph, in a graph store (TAO style), heavily cached
engagement: likes, comments, views as events for ranking
suggestions:{user} precomputed daily
5. High level design
Follow one request at a time, top to bottom.
Load the feed
- ClientGET /feed
- Feed servicegathers candidate posts from friends and pages
- Graph cache (TAO style)who are my friends, what did they post
- Privacy filterdrops posts you can't see
- Rankercloseness x engagement / age, keep the top 20
- Clientrenders, cursor for more
Suggest friends
- Daily batchfriends of friends per user
- Count mutual friendsskip current friends and blocks
- Suggestions storetop results per user
- ClientGET /suggestions
6. Deep dives
How is the feed built?
Gather candidates from friends and pages, filter by privacy, score each by how close you are to the author, how engaging the post is and how fresh it is, then show the top. Cache the result for a short time.
How do you suggest friends?
Friends of friends, counted by mutual friends, skipping current friends and blocked people. Computed in a daily batch, plus signals like same school or contacts.
How do you store the social graph?
As edges in a graph store sharded by user id, with a big read through cache in front. Most reads are "friends of X", so keep each user's edges together.
How do you enforce privacy?
Check the audience at read time for each candidate, after gathering and before ranking. Never trust a cached feed across a privacy change; invalidate it.
See the core idea run
Core logic (JavaScript, tested)
// Friends of friends ("People you may know"): count mutual friends, skip people you already know.
function suggest(friends, me, limit = 3) {
const mine = friends.get(me) ?? new Set();
const mutual = new Map();
for (const f of mine)
for (const ff of friends.get(f) ?? [])
if (ff !== me && !mine.has(ff)) mutual.set(ff, (mutual.get(ff) ?? 0) + 1);
return [...mutual].sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : 1)).slice(0, limit);
}
// Ranked feed: score = how close you are to the author x how engaging x how fresh.
const score = p => p.affinity * (1 + p.likes / 10) / (1 + p.ageHours / 6);
const rank = posts => [...posts].sort((a, b) => score(b) - score(a)).map(p => p.id);
7. Failures
| Ranking service slow | Fall back to a simple recency order rather than a blank feed. |
| Graph cache cold after a deploy | Warm the hottest users first and cap load on the graph store. |
| A post goes viral | Counters are sharded and approximate; exact numbers are fixed later in batch. |
8. Trade-offs
- Ranking increases engagement but makes the feed harder to explain; offer a recent view too.
- Daily suggestions are cheap and good enough; real time ones cost far more.