system design

Designing a news feed and where fanout falls apart

The classic version of this question is “Design Facebook’s news feed” or “Design Twitter’s home timeline,” and most candidates lose it in the first five minutes by locking onto one feed-generation strategy and defending it to the death. The interviewer is not testing whether you know what a feed is. They want to see you notice that the naive design falls over the instant a user with 90 million followers hits post.

Start with the read/write ratio, because it decides everything downstream. A social feed is read-heavy by a wide margin. People scroll far more than they post. A reasonable back-of-envelope split is something like 100 reads for every write, sometimes worse. That imbalance is why the interesting work happens at write time, not read time, for most of your users.

The two ways to build a timeline

Fan-out on write (the push model) precomputes each user’s feed. When you post, a fanout worker looks up your followers and inserts the post ID into every follower’s timeline, which lives in a fast store like Redis. Reads are cheap: open the app, read your precomputed list of roughly 800 post IDs, hydrate them with the actual content, done. You paid the cost up front, spread across your followers.

Fan-out on read (the pull model) does the opposite. Writing a post is a single insert into your own posts table. The work moves to read time: when you open the app, the system finds everyone you follow, pulls their recent posts, merges them by time or by score, and returns the result. Nothing is precomputed, so a post is never stale, but every feed load does real work.

Both are correct. Both break. The skill the interviewer is watching for is whether you know where each one breaks and can pick per user instead of globally.

Where fan-out on write falls apart

Take a celebrity account with 100 million followers. Under pure push, one post means 100 million writes into 100 million timelines. Even on a well-tuned Redis cluster, that is a multi-minute job, and while it runs it clogs the fanout queue so that a normal person’s post, which needs 200 writes, waits behind it. Twitter’s stated target was to complete fanout in about five seconds, and celebrity posts were exactly the case that blew through it.

The numbers get worse when you remember that most of those followers may not open the app today. You did 100 million writes so that maybe 3 million people who actually log in can read a post that would have cost almost nothing to fetch on demand. That is the write-amplification trap: paying to deliver content into inboxes nobody checks.

Where fan-out on read falls apart

Now flip it. Pull is great for the celebrity, because posting is one insert regardless of follower count. The pain shows up for the reader who follows a lot of accounts. Someone following 2,000 people forces the system, on every single feed load, to fetch recent posts from 2,000 sources, merge them, and rank them. Multiply that by millions of active readers refreshing constantly and you have turned every scroll into a scatter-gather query. Read latency climbs, and the database serving all those lookups becomes the bottleneck.

Pure pull also throws away the precompute opportunity. The same heavy feed gets regenerated from scratch every few seconds even when almost nothing changed since the last load.

The hybrid, and the threshold that decides it

The design every large social product converged on is a split by follower count. Regular accounts use push: their posts fan out to follower timelines because the write cost is bounded and small. High-follower accounts skip fanout entirely; their posts sit in their own store and get pulled at read time. When you load your feed, the system reads your precomputed timeline (everything from the push accounts you follow) and merges in a live fetch of recent posts from the handful of celebrities you follow. You get fast reads for the 99% and no write storms from the 1%.

The threshold is a knob, not a law. A common starting point is somewhere around 10,000 followers, but the real answer in an interview is that you would tune it against traffic: the point where the write cost of pushing a post exceeds the read cost of having followers pull it. Say that out loud. Interviewers trust a number with a reason attached far more than a number that fell from the sky.

Feed strategy Cost when a user posts Cost when a user opens the feed Feed freshness Where it breaks
Fan-out on write (push) One write per follower; a 100M-follower post means 100M writes Read one precomputed list of about 800 post IDs Can lag while fanout catches up High-follower accounts create write storms and queue delays
Fan-out on read (pull) One insert into your own posts table, regardless of follower count Fetch and merge recent posts from everyone you follow, every load Always current Readers who follow thousands of accounts make every scroll a scatter-gather
Hybrid (push for most, pull for celebrities) Push for normal accounts; skip fanout for high-follower accounts Read the precomputed timeline, then merge a live pull of celebrity posts Current for celebrity posts, near-current for the rest Needs a tuned follower threshold and logic to move accounts across it

A rough read path for the hybrid looks like this:

def get_feed(user):
    # precomputed timeline from normal accounts (push)
    feed = redis.lrange(f"timeline:{user}", 0, 800)

    # live pull from the few high-follower accounts you follow
    for celeb in celebrities_followed_by(user):
        feed += recent_posts(celeb, limit=50)

    ranked = rank(feed, viewer=user)   # score, do not just sort by time
    return hydrate(ranked[:50])        # fetch text, media, counts

Ranking, and why the feed is not chronological anymore

Once the candidate set is assembled, you have to order it. Reverse-chronological is the easy answer and a fine place to start, but no major feed ships pure recency now. The ranked version scores each candidate post on signals like how fresh it is, how much you interact with the author, early engagement on the post, and media type, then sorts by that score. In an interview you do not need the real model. You need to show you know ranking is a separate stage that runs on a small candidate set of a few hundred posts, not on the whole firehose.

The ordering choice interacts with caching in a way people miss. A precomputed timeline of post IDs is cheap to store and easy to append to when it stays chronological. The moment you rank, the stored order can go stale as new signals arrive, so many designs keep a chronological candidate list and apply ranking at read time on that small set. Writes stay simple, and the expensive scoring moves to a bounded read.

The caching math interviewers want to hear

Storing feeds as full post objects would be enormous and redundant. Store post IDs instead and hydrate on read. If a home timeline holds about 800 post IDs at 8 bytes each, that is roughly 6.4 KB per user. Across 500 million users you are looking at low single-digit terabytes for the timeline cache, which fits in a sharded Redis cluster with each shard replicated for failover. Hydration then does a batched fetch of the actual post content from a cache sitting in front of the posts store.

This is also where you mention what to drop. You do not keep timelines for accounts that never log in; build their feed on read the first time they come back. You cap the stored timeline length so it does not grow without bound. Small decisions, but they are the difference between a design that fits in memory and one that quietly does not.

What the follow-ups usually are

Expect the interviewer to poke at the seams. What happens with deletes and edits, now that you are chasing content that was already copied into millions of precomputed timelines? How do you keep the feed fresh without recomputing on every read? How do you page through a feed that is being ranked, given that offset paging over a shifting candidate set returns duplicates and gaps (the answer is a cursor over a stable set)? How do you handle a brand-new user with no follows, or an account that goes viral and crosses your push/pull threshold in the middle of the day?

That last one is the tell that you have understood the design. The threshold is not set once at signup. An account can cross it, and a good answer stops pushing and switches that account to pull when it does, accepting that its already fanned-out posts stay where they landed. Explain that migration calmly and you have shown the thing the question is really testing: that you can hold two strategies in your head at once and route between them based on the shape of the data.

newsletter

What's actually being asked right now

Interview patterns & comp trends, straight to your inbox.

No spam. Unsubscribe anytime.

newsletter

What's actually being asked right now

Interview patterns & comp trends, straight to your inbox.

No spam. Unsubscribe anytime.

1972 Soviet postage stamp commemorating the Mars 2 probe

worth a read

Mars For The Rest of Us — a weekly-or-more deep dive on the technical side of Mars exploration: rocket propulsion, microbiology, mission architecture, and everything in between. Written by Maciej Ceglowski.

Read it on Substack →
Scroll to Top