InfoWok

Design a News Feed: Push, Pull or Both? (2026)

A news feed looks like a database query and is really a question about where to spend work: when a post is written or when a feed is read. The prompt, both designs with numbers, the celebrity problem, the read path, and what breaks.

SK
Sukhveer Kaur
Published October 24, 2026
14 min read
Open in ChatGPT Open in Claude
News feed, system design problem 3: an author's post is fanned out through a queue into follower feed lists, beside the line push, pull or bothSoftware Architecture
DESIGN A NEWS FEED
On this page +

The interviewer writes “Design a news feed” on the board. The candidate says: “Easy. Select the latest posts from the people the user follows, sorted by time.” That query is correct, and fast on a laptop with a hundred users.

Then the interviewer asks: “Assume a user follows 200 accounts and ten million people open the app at breakfast. What does that query cost?” About 200 lookups per open, paid every time anyone looks at the screen.

The whole problem is one question: do you build a feed when a post is written, or when a feed is read? This lesson answers it with numbers, then covers what breaks.

Intermediate40 min
Before you start
Remember from the rate limiter lesson

Why did the limiter keep one tiny record per client instead of recomputing it from history on every request? A small precomputed answer on the hot path is cheaper. A feed list is the same trade, with one list per reader.

Key takeaways
  • A feed is a choice of when to do work: push at write time costs per follower, pull at read time costs per account followed.
  • Push makes reads one cheap lookup but does several times more writes than reads arrive, and it breaks on accounts with millions of followers.
  • A hybrid pushes for ordinary authors and merges celebrity posts at read time.
  • Store ids in the feed list, keep post bodies in one place, and page with cursors, not page numbers.
  • The feed list may be stale, so the post store is the truth: filter deletes and blocks at read time.
After this lesson you can

Design a news feed end to end in 45 minutes: choose between push, pull and hybrid using numbers, design the read path and pagination, and say what happens with celebrities, inactive users and deletes.

Step 1: Pin Down the Problem#

Ask before drawing: text only? Private accounts? One feed or several? How fresh? How many users? If the interviewer shrugs, state an assumption aloud and move on.

Functional requirements:

  1. A user publishes a post.
  2. A user follows and unfollows other accounts.
  3. A user opens a home feed: the newest posts from the accounts they follow, newest first, 20 at a time, with a way to load more.

Out of scope (say these out loud):

  • Media upload and video processing (a post stores a media URL; files live in blob storage behind a CDN), search, notifications, ads, comments and like counts. Each is its own problem.
  • Ranking by relevance. Start with newest first, then add a ranker in deep dive 4.

Non-functional requirements, each with a number. These are assumptions you announce:

  • Fast to read: the feed should load in a few hundred milliseconds even at the p99.
  • Available: a feed that is a few seconds stale is fine, a feed that errors is not. Reads matter more than perfect freshness.
  • Eventually consistent: a new post may take seconds to reach every follower. Nobody can tell.
  • Private by default: a blocked or removed reader must never see a post, even from a stale list.
  • Scale: assume 10 million daily users. Step 4 turns that into numbers.

Step 2: Entities and API#

text
User     id
Follow   follower_id, followee_id
Post     id, author_id, text, time
Feed     derived: post id list
 
POST /posts          (auth)
  Idempotency-Key: <uuid>
  -> 201 {id}
POST /follow/{user}  -> 204
GET  /feed?cursor=&limit=20
  -> {posts[], next_cursor}
  401 no auth, 429 too many

The feed is derived. The only facts are users, follows and posts, and every design below computes the feed from them.

Index the follow table both ways. “Who does this reader follow?” (by follower_id) drives the pull design, and “who follows this author?” (by followee_id) drives the push design. Posts need an index by author and time, so “latest posts of this author” is one range read.

Retrying POST /posts with the same idempotency key must not create a second post. The read call takes a cursor instead of a page number. New posts keep landing at the top, so “page 2” changes under the reader, who sees repeats or misses posts. A cursor says “give me posts older than this one”, which stays correct.

How a real system does it

Mastodon’s API works this way: it takes max_id, since_id and min_id, and the feed reads an id range from Redis (feed model). Once ranking is added, the cursor must also encode the ranked position or a snapshot id, because a score-ordered list is not stable by id.

Step 3: A Design That Works#

Build the simple version: look up who the reader follows, fetch each account’s latest posts, merge by time and return the top 20.

This is correct, fresh and simple. It is called fan-out on read, or a pull design, because the reader pulls everything together.

Predict first

Assume the reader follows 200 accounts and 3,000 people open a feed in the same second. How many lookups per second is that, and is it a problem?

Call it 600,000 lookups a second (3,000 × 200), each one a database read. Everything about this design costs more as users follow more accounts, and the cost lands on the reader, at the moment the reader is waiting. So the next design moves the work earlier, to when a post is written.

Step 4: Estimate What Matters#

text
Assume 10M daily users
5 feed opens each a day
 
Reads:  50M / 86,400
        ≈ 580/s, peak 3,000/s
Posts:  2M a day
        ≈ 23/s, peak 115/s
Followers: 200 on average
Follows:   200 on average
Post: 1 KB (assumed)

The two 200s are equal because every follow is one followed and one follower gained. Single accounts are lopsided, which is where the trouble starts.

Now price the push design, where every post is copied to every follower’s feed list:

text
Inserts: 23/s x 200 = 4,600/s
Peak:   115/s x 200 = 23,000/s
Storage: 10M users x 800 ids
         x 50 B = 400 GB

Each line decides something:

  • At ≈ 400 GB, the feed lists are too much for one machine, so the lists must be sharded. That is the sharding lesson again.
  • At ≈ 4,600 inserts a second, push does about eight times the average read rate of 580 a second. Push does not remove work, it moves it from the reader to the writer.
  • With the assumed 3,000 opens a second at peak, each loading 20 posts, the cache sees 60,000 reads a second. That number, not the feed list, sizes the cache tier.
  • Bandwidth: 60,000 × 1 KB = 60 MB/s ≈ 480 Mbit/s of text at peak. Media would dwarf it, so it goes through a CDN.
  • Posts: 2M × 1 KB = 2 GB a day ≈ 730 GB a year, small next to the feed lists, so memory is the bill.
  • The 50 bytes per entry is an assumption that includes overhead. Measure your own store.

Step 5: Deep Dives#

Four questions decide this design. Cover them in this order, and let the interviewer steer.

Push, Pull or Both?#

Concept in use: Caching strategies

A precomputed feed is a cache of the answer to “what should this reader see?”. The same questions apply: how fresh, how big, and what happens when it is empty.

Works · Fan-out on read (pull)correct, simplest

Compute the feed when it is opened, as in step 3. No extra storage, always fresh, and a new follow shows up at once.

Trade-offThe cost grows with the number of accounts followed and lands on the reader. With the assumed 3,000 opens a second and 200 follows, that is ≈ 600,000 lookups a second.
Scales · Fan-out on write (push)handles the load

When a post is published, save it once, then insert its id into the feed list of every follower. Reading is one lookup of the reader’s own list. Mastodon’s code does this: its fan-out service walks the followers in batches and queues a job per follower, and each job adds the post to that follower’s feed.

text
on new post by author A:
  save post (once)
  add to own feed
  for each batch of followers:
    queue a job per follower
job(post, follower):
  ZADD feed:<follower> id id
  trim to newest 800

It also inserts the post into the author’s own feed first, outside the queue (a per-user setting that is on by default). That gives authors read-your-own-writes: their post is there on the next refresh while followers wait.

Trade-offWrites explode: ≈ 4,600 inserts a second on average and 23,000 at peak, plus the storage. And an author with an assumed ten million followers makes ten million inserts for a single post.

What Does One Huge Account Do to Push?#

This arithmetic changes the design. Assume the system can spend 20,000 inserts a second on one post:

text
10,000,000 followers
÷ 20,000 inserts/s
= 500 s  (over 8 minutes)

For eight minutes some followers see the post and some do not, and every other author’s fan-out waits behind it in the queue.

Survives · Hybrid: push for most, pull for celebritieshandles failure and growth

Push posts from ordinary authors. For authors over a follower threshold, skip the push: save the post once, and at read time merge the reader’s feed list with the latest posts of the celebrities they follow. A reader follows only a handful of such accounts, so the read-time merge stays small.

Trade-offTwo code paths to test and explain, and a threshold to choose. That threshold is a design judgement, so pick it from your own numbers: the follower count where one post’s fan-out is too slow or too expensive.
A rule to remember

Push pays per follower. Pull pays per follow. If one author has millions of followers, push is the expensive one. If one reader follows thousands of accounts, pull is the expensive one. The hybrid avoids both.

Where Do Feeds Live, and How Is a Page Read?#

Concept in use: Database sharding and replication

Feed lists are data too. They need a shard key (the reader’s id), a way to survive a lost machine, and a size limit.

Works · A table in the main databasecorrect, simplest

A feed_items table with one row per reader and post, indexed by reader and time.

Trade-offSimple and durable, but at the assumed ≈ 4,600 inserts a second and 400 GB it competes with everything else in the database, and trimming old rows becomes its own job.
Scales · One sorted set per reader in Redishandles the load

A sorted set keeps entries ordered by a score. Use the post id as member and score, which works when ids grow with time. ZADD is O(log N) per insert. Mastodon keeps each home feed as a sorted set and caps it at 800 entries (current main branch, as of 2026-10-23). Your cap is your own choice.

text
ZADD feed:42 7001 7001
ZREMRANGEBYRANK feed:42 0 -801

To read a page, ask for 20 ids below the cursor, newest first. ZRANGE takes BYSCORE, REV and LIMIT since Redis 6.2:

text
ZRANGE feed:42
  (7001 -inf
  BYSCORE REV
  LIMIT 0 20
Trade-offMemory is the cost: the cap and the active-user rule decide the bill. Old posts fall off, so “load everything since last month” needs a fallback to the database.
Survives · Sharded lists, replicas and ids onlyhandles failure and growth

Shard the lists by reader id with consistent hashing (lesson). Give each shard a replica for failover. Keep only ids in the list, not post text.

Trade-offIds make the list small, but every page needs a second step to turn 20 ids into 20 posts, which must hit a cache to stay fast.

That second step is to hydrate the ids. Keeping bodies in one place has a benefit: an edit or a delete shows up in every feed at once.

What Happens When One Post Goes Viral?#

A viral post’s id sits in millions of feed lists, so every page load asks the cache for the same key. That is a hot key, and adding shards does not help, because one key lives on one shard.

The fix is more copies, not more shards:

  • Keep copies of hot posts on several cache nodes, and read from any of them.
  • Or keep a small in-memory copy on each feed server for a few seconds.

Both trade a little staleness after an edit for spread-out load.

How Does an Open App Hear About New Posts?#

Polling is the pull design again. Mastodon instead pushes the post over an open streaming connection when it lands in that reader’s feed. A lighter variant sends only a “new posts” signal, and the client fetches with a cursor.

What Breaks?#

This section separates senior answers. Three things go wrong in any push design.

Works · Inactive users cost writescorrect, simplest

Pushing into feeds nobody reads wastes inserts and memory.

Trade-offSkip them: Mastodon does not push into the home feed of an account that has not signed in recently, which defaults to 7 days (as of October 2026). The returning user finds a cold feed.
Scales · Rebuild a cold feed on returnhandles the load

When a user comes back, build the list from the recent posts of the accounts they follow, the pull query from step 3 run once. Mastodon queues that rebuild when an inactive user signs in again, and its home timeline API answers HTTP 206 (partial content) while that rebuild is running, and 200 after.

Trade-offThe first open after a long absence is slower or shows a short page. Rate-limit rebuilds, or a wave of returning users after an outage becomes a pull-design load spike.
Survives · Treat the list as a hint, not the truthhandles failure and growth

The fan-out queue can lag, so posts appear late. Retries are safe when the insert is idempotent: adding the same member to a sorted set twice leaves one entry, because a sorted set cannot hold the same element twice. That is the delivery guarantees lesson idea: at-least-once delivery plus an idempotent write.

Deletes and unfollows are the other half. Removing one post from millions of lists costs as much as the fan-out did. Mastodon does that cleanup in the background anyway: deleting a post walks the followers and removes it from each feed, and an unfollow removes that account’s posts from the reader’s list. A new follow does the reverse and merges up to 200 recent posts of the followed account in.

The cleanup lags, so the read path is the safety net. When posts are loaded, skip any that are missing or whose author is now blocked.

Trade-offPages can come back with fewer than 20 posts after filtering, so the service asks for a few extra ids or loops once more.

Who Ranks the Feed?#

A real feed is rarely just newest first. X publishes the code for its For You feed, and the repository README (updated September 2026) describes the shape. The feed is assembled per request: one in-network source holds recent posts in memory and returns those from accounts the viewer follows. That is a pull design, made fast by keeping recent posts in RAM.

To add ranking to this lesson’s design:

  1. The feed list and the celebrity merge produce a few hundred candidate ids.
  2. Filters drop duplicates, blocked and muted authors, and posts already seen.
  3. A ranker scores the candidates. X’s model predicts how likely the viewer is to like, reply, block and so on, then adds the predictions up with weights.
  4. Visibility rules remove unsafe posts, then the service returns 20 and a cursor.

Out-of-network posts come from other sources. The README describes a retrieval step that turns the viewer and each post into embeddings and returns the nearest posts, plus a cluster-based source added in August 2026 (vector search lessons). Ads are blended in after ranking.

Rankers keep changing: LinkedIn’s engineers describe replacing a DCNv2-based feed ranker with a sequence transformer (paper first posted February 2026). In the room, say what you would optimise and how you would launch it (shadow traffic, then an A/B test).

What Else Gets Probed?#

Consistency, stated concretely

This is an availability-and-latency system. If a network split cuts a region off, keep serving possibly stale feeds (the A in CAP), and even when nothing is broken choose low latency over fresh data (the L in PACELC). Spend strong consistency only where being wrong hurts: the author’s own post (read-your-own-writes above) and blocks, which the read path checks against the source of truth.

If this diesWhat the reader seesWhat you do
A feed-list shardSome readers get empty listsFail over to its replica; if none, rebuild by pull
The post cacheSlower pages, more database readsRate-limit misses, serve a shorter page
The fan-out queuePosts arrive lateAlert on lag; pull covers the gap
The rankerUnranked, newest-first feedTimeout and fall back
A whole regionAnother region serves a cold feedRebuild by pull, as for returning users
TopicOne-line answer
Multi-regionWrite a post in the author’s home region, copy it to others asynchronously. Keep feed lists and the celebrity merge region-local, so no cross-region reads on the hot path
Security and abuseAuthenticate every call; rate-limit POST /posts (the limiter lesson); private accounts and blocks are checked at read time
Privacy lawA deletion request must reach the post store, the lists and every cache; backfill jobs are the usual miss
ObservabilitySLIs: feed p99 and error rate, fan-out lag (post time to last follower insert), short-page rate, cache hit rate, queue depth. Alert on lag against an SLO you pick, for example under 5 seconds for 99% of posts (assumed)
CostMemory is the bill. Levers: lower the cap, skip inactive users, ids only, push only below the threshold
RolloutChange the threshold or list format behind a flag. Write to old and new lists, compare, switch reads
New user, no followsShow popular or suggested posts and prompt follows
AdsInsert after ranking at fixed slots, outside the cursor

The Final Design in One Picture#

Follow one post: save it once, queue fan-out unless the author is over the threshold, and insert its id into each active follower’s list. Follow one read: take 20 or more ids below the cursor, add celebrity posts, load from the cache, rank, return 20 and a cursor. A cold list is rebuilt by pull.

What Each Level Should Show#

LevelExpected in the room
Mid-levelPull query, then push with a feed list per user. May need a hint about celebrities.
SeniorPrices both with numbers, finds the celebrity problem, designs a hybrid, uses cursors, and handles deletes at read time.
Staff+Cold feeds and rebuild limits, queue lag and priorities, hot posts in the cache, failure and region plans, SLOs on fan-out lag, ranking as candidates plus a ranker, and what to build first.

Red Flags and Green Flags#

✗ Red flag

“Fan-out on write, because reads are faster.” No numbers, no mention of celebrities, and no answer to what happens when a post is deleted.

✓ Green flag

“Assuming ≈ 4,600 inserts a second against 580 reads, push moves work to the writer. That breaks for an assumed ten million followers, so I’ll skip the push for those and merge at read time. The list is a hint, so I filter deletes when I load the posts.”

The most common way to lose this round

Picking push or pull in the first minute and defending it for 40 more. The interviewer is waiting for the celebrity, the cold feed and the delete.

Try It: A Much Bigger Author#

Exercise · One author with 5 million followers15 min

Assume an author with 5 million followers posts three times in an hour, at 20,000 inserts a second per post.

  1. How long does one post’s fan-out take?
  2. What goes wrong for the other authors?
  3. How do you change the design?
Hint

Divide followers by the insert rate, then ask what else is waiting in the same queue.

One reasonable answer
text
5,000,000 / 20,000 = 250 s
3 posts = 750 s (12.5 min)

One post takes about four minutes, and three keep the queue busy for over twelve. Other authors’ posts wait behind it.

Treat this account as a celebrity: save each post once and merge it at read time. A reader follows only a few such accounts, so the extra read cost is small.

In an interview

Expect “What about a user with ten million followers?”, “How do you page while posts arrive?”, “What if the queue falls behind?” and “How would you add ranking?” Each maps to a deep dive above. Answer with a trade-off and a number.

Follow-upStrong answer
10x traffic?Add shards and cache nodes; lower the cap; raise the celebrity bar only if fan-out time allows
Redis is down?Serve by pull from the post store, rate-limited; rebuild lists afterwards
A user follows 50,000 accounts?Push is cheap for them; pull is not, so cap the merge or treat them as push-only
Reader sees a post twice?Dedupe by id before ranking and keep a seen set

A 45-minute plan: 5 requirements, 5 numbers and API, 10 simple design, 15 deep dives, 5 failures and operations, 5 questions.

Check Yourself#

Check yourself5 questions · answer, then open
1.Which costs more at read time, push or pull, and what does each one's cost depend on?

Pull costs more at read time, growing with accounts followed. Push costs more at write time, growing with the author’s followers.

2.Why are page numbers a bad way to page through a feed?

New posts shift every page, so the reader sees repeats or misses posts. A cursor always means “older than this”.

3.A post is deleted. Why not remove it from every feed list?

It may sit in millions of lists, so removal costs as much as the fan-out and finishes late. Clean up in the background, and meanwhile skip missing or blocked posts when loading a page.

4.A returning user has been away for a month. What do you do, and what changes if a thousand return at once?

Their list was skipped, so rebuild it by pull and show what is ready. A wave of returning users is pull-style load, so cap concurrent rebuilds.

5.One post goes viral and its cache shard overloads. Why does adding shards not help, and what does?

One key lives on one shard. Spread the reads with copies: the hot post on several cache nodes, or a short-lived copy in each feed server.

In One Picture: The 60-Second Answer#

“A feed is derived from users, follows and posts. With my assumed numbers, pull costs about 600,000 lookups a second and push about 4,600 inserts a second, which fails for a ten-million-follower account. So: hybrid, threshold from fan-out time.

Feeds are capped id lists in sharded Redis (Mastodon caps at 800), paged by cursor. The list is a hint: rebuild cold feeds, filter deletes at read time. Favour availability, watch fan-out lag.”

Which of the three numbers would you reach for first in the room? Tell us in the comments.

Where to go from here

Frequently asked questions

What is the difference between fan-out on write and fan-out on read? +
Fan-out on write does the work when a post is published: the post id is inserted into the feed list of every follower, so reading a feed is one cheap lookup. Fan-out on read does the work when a feed is opened: the service fetches the latest posts of everyone the reader follows and merges them. Push pays per follower, pull pays per account followed.
How do you handle celebrities in a news feed design? +
Use a hybrid. Push posts from ordinary accounts into follower feeds, but skip the push for accounts with a very large number of followers. Store their posts once, and merge the latest ones into the reader's feed at read time. The follower threshold is a design choice, not a standard number.
Why use cursors instead of page numbers for a feed? +
New posts keep arriving at the top, so page 2 shifts every time someone posts, and the reader sees duplicates or misses posts. A cursor is the id of the last post the reader saw, and the next page is everything older than it, which stays correct while new posts arrive.
What happens when a user has been away for a month? +
Many systems skip fan-out for accounts that have not been active, to save writes. When the user returns, the feed is rebuilt from the recent posts of the accounts they follow. Mastodon's code does this: by default it stops pushing to accounts that have not signed in for 7 days, starts a rebuild when one signs in again, and its home timeline API answers HTTP 206 while the rebuild is running.
Where does ranking fit in a news feed? +
After candidates are gathered. The feed list and any pull sources supply candidate post ids, and a ranker reorders them before the page is returned. X's published For You repository (as of October 2026) describes this shape: in-network and embedding-based out-of-network candidate sources, filters, a transformer that predicts how likely the viewer is to take each action, a weighted score, then ads and other items blended in.

References

  1. Mastodon source — FeedManager (Redis sorted-set feeds, 800-entry cap)
  2. Mastodon source — FanOutOnWriteService (queue jobs per batch of followers)
  3. Mastodon source — Feed model (id cursors over a Redis range)
  4. Mastodon source — home timeline controller (HTTP 206 while a feed is rebuilt)
  5. Redis documentation — ZADD
  6. Redis documentation — ZRANGE (BYSCORE, REV, LIMIT)
  7. Redis documentation — ZREMRANGEBYRANK
  8. Mastodon source — User::Activity (7-day active window for fan-out)
  9. Mastodon source — User model (feed regenerated when an inactive user returns)
  10. Mastodon source — RemoveStatusService (deleted posts removed from follower feeds)
  11. X For You feed algorithm repository (xai-org/x-algorithm) — README
  12. An Industrial-Scale Sequential Recommender for LinkedIn Feed Ranking (arXiv, 2026)
Written by
Sukhveer Kaur
Sukhveer KaurSoftware Developer & AI Engineer

Sukhveer is a software developer specialising in AI systems and backend engineering. She has hands-on experience designing agentic AI applications, working with large language model pipelines, autonomous agent frameworks, and cloud-native services in Java and Python. At InfoWok, she bridges the gap between cutting-edge AI research and practical implementation — helping developers understand and apply emerging technologies through clear, experience-backed writing.

New AI engineering guides, the day they ship

Real Python, production depth. No digest spam.

Comments