Skip to content

    System Design

    System Design Case Study: News Feed

    Design a social news feed: fan-out on write versus read, the celebrity problem, hybrid delivery, ranking basics, timeline storage, pagination and failure modes.

    By Published 17 min read

    Share

    This lesson is part of Advanced System Design and Case Studies. Enroll for free, then sign in, to read it in full.

    What you'll learn: By the end of this lesson you will be able to (1) compare fan-out on write, fan-out on read and hybrid feed designs with numbers, (2) explain and solve the celebrity problem, (3) design timeline storage, cursor pagination and caching, and (4) describe how a feed degrades when parts of it fail.

    Imagine a newspaper stand. One option is to print a custom newspaper for every reader every time any journalist files a story. Reading is instant, but printing is enormous work. The other option is to keep stories in one pile and assemble each reader's paper only when they walk up. Printing is cheap, but every reader waits while you collect and sort.

    A news feed faces exactly this choice. When someone you follow posts, do we do the work when they post (and pre-build your feed), or when you open the app (and build it on demand)? The best answer is usually a mix, chosen per author.

    We assume you know caching, sharding and queues from the Building Blocks course. The post content itself (photos, captions) is covered in the photo sharing case study. Here we focus on how posts reach the right feeds.

    The core idea

    A feed is a materialised view of posts from the accounts you follow, ordered by time or relevance. You can compute it at write time, at read time, or both. Which one wins depends on the ratio of reads to writes and on the fan-out: the number of followers each post must reach. Because follower counts are wildly uneven, no single strategy fits every author.

    Requirements and scope

    Functional: publish a post, follow and unfollow, view a home feed of posts from followed accounts (newest first or ranked), scroll through older posts, see a new-post indicator.

    Out of scope: post creation internals and media, comments and likes (we touch their counters briefly), notifications (see the notification service design), ads.

    Non-functional (assumptions):

    • Feed load p99 under about 300 ms.
    • Posts appear in followers' feeds within a few seconds. Perfect real-time is not required.
    • High availability for reading. A slightly stale feed beats an error page.
    • Eventual consistency is acceptable, but a user should see their own post immediately (read-your-writes).

    Back-of-envelope estimate

    All numbers are illustrative assumptions.

    Quantity Assumption Result
    Daily active users 100 million
    Posts per user per day 0.5 (most people read more than write) 50 million posts per day, about 580 per second average
    Feed opens per user per day 10 1 billion feed loads per day, about 11,600 per second average
    Peak factor 3x about 35,000 feed loads per second
    Average follows per user 200
    Fan-out on write per post follower count, average 200 about 10 billion feed insertions per day, about 115,000 per second
    Feed page size 20 posts

    The takeaway: reads are about 20 times more frequent than posts, but each post is written into hundreds of feeds. Pure fan-out on write converts 580 posts per second into about 115,000 timeline writes per second on average. That is large but spreadable. The danger is the tail. One account with 50 million followers turns one post into 50 million writes.

    Storage per timeline entry: store only IDs and a sort key, say 16 to 24 bytes per entry. Keeping the latest 800 entries per active user: 100 million users times 800 times about 20 bytes is about 1.6 TB for timelines. That fits in a sharded in-memory or SSD-backed store. Post bodies are stored once, elsewhere.

    API design

    POST /v1/posts                          -> 201 { "postId": "..." }
    POST /v1/follows/{userId}               -> 204
    DELETE /v1/follows/{userId}             -> 204
    GET  /v1/feed?cursor=<opaque>&limit=20  -> 200 { "items": [...], "nextCursor": "..." }
    GET  /v1/feed/new-count?since=<cursor>  -> 200 { "count": 7 }

    The cursor is opaque to clients. Internally it encodes the sort key of the last item seen (for example a post ID that encodes time). We will see why this matters in the pagination section.

    Data model

    • Post store: posts(post_id, author_id, body_ref, created_at, ...), keyed by post_id. A wide-column or sharded relational store both work. Post IDs should be time-sortable (see IDs, clocks and ordering) so that sorting IDs sorts by time.
    • Social graph: two mappings: following(user_id -> followee ids) and followers(user_id -> follower ids). Both are needed: reading a feed on demand needs "who do I follow", and fan-out needs "who follows this author". Store both, sharded by user_id. For huge follower lists, store them as paged or chunked rows.
    • Timeline store: timeline(user_id, post_id) sorted by post_id descending, capped to the latest N entries. A sorted set in an in-memory store (Redis sorted sets are a common choice) or a wide-column table with clustering by time fits. Shard by user_id so one feed read hits one shard.
    • Counters (likes, comments): kept separately and updated asynchronously. Do not store counts inside the timeline.
    erDiagram USERS ||--o{ POSTS : writes USERS ||--o{ FOLLOWS : follows USERS ||--o{ TIMELINE_ENTRIES : owns POSTS ||--o{ TIMELINE_ENTRIES : appears_in USERS { string user_id PK boolean is_celebrity } POSTS { string post_id PK string author_id FK datetime created_at } FOLLOWS { string follower_id FK string followee_id FK } TIMELINE_ENTRIES { string user_id FK string post_id FK }

    How to read it: TIMELINE_ENTRIES is a derived table of pairs and can be rebuilt from POSTS and FOLLOWS. The is_celebrity flag is the switch that selects push or pull for an author.

    High-level design

    News feed high-level designClient to API gateway; API gateway to Post service; API gateway to Feed service; Post service to Post store; Post service to Fan-out queue (post event); Fan-out queue to Fan-out workers; Fan-out workers to Social graph; Fan-out workers to Timeline store; Fan-out workers to Celebrity posts index (celebrity); Feed service to Timeline store; Feed service to Post cache; Post cache to Post store (miss); Feed service to Ranking service; Feed service to Celebrity posts indexClientClientAPI gatewayAPI gatewayPost servicePost servicePost storePost storePost cachePost cacheFeed serviceFeed serviceFan-out queueFan-out queueTimeline storeTimeline storeCelebrity posts indexCelebrityposts indexRanking serviceRankingserviceFan-out workersFan-outworkersSocial graphSocial graphpost eventcelebritymiss

    Reading a feed: the feed service fetches post IDs from the timeline store for this user, merges in recent posts from any celebrity accounts they follow, hydrates the IDs into full posts from a post cache (batched), optionally re-ranks, and returns a page. To give the author read-your-writes, the author's own latest posts are merged in the same way (or written into their own timeline synchronously at publish time), so they do not wait for the asynchronous fan-out.

    Reference deployment topology

    In deployment terms the design splits into a synchronous read path, an asynchronous fan-out path and a sharded data tier, spread over at least two zones.

    News feed reference deployment topologyAPI gateway to Post service; API gateway to Feed service; Post service to Post store; Post service to Kafka; Kafka to Fan-out workers; Fan-out workers to Graph store; Fan-out workers to Timeline shards; Fan-out workers to Celebrity index; Feed service to Timeline shards; Feed service to Celebrity index; Feed service to Post cache; Post cache to Post store (miss); Feed service to Ranking serviceEDGEREAD AND WRITE PATHFAN-OUT TIERDATA TIERAPI gateway: edgeAPI gatewayedgePost service: writesPost servicewritesFeed service: readsFeed servicereadsRanking service: optional re-rankRankingserviceoptional re-rankKafka: fan-out topicKafkafan-out topicFan-out workers: consume topicFan-outworkersconsume topicPost store: source of postsPost storesource of postsPost cache: batched hydratePost cachebatched hydrateTimeline shards: Redis, shardedTimelineshardsRedis, shardedCelebrity index: celebrity postsCelebrityindexcelebrity postsGraph store: followersGraph storefollowersmiss

    How to read it: a post takes the path post service, Kafka topic, fan-out workers, timeline shards (or the celebrity index for a high-fan-out author), while a feed read goes from the feed service to the timeline shards, the celebrity index and the post cache. Queue lag is the main thing that makes the two paths drift apart.

    Deep dive 1: fan-out on write, on read, and hybrid

    Fan-out on write (push). When a post is created, workers look up the author's followers and insert the post ID into each follower's timeline. Reading is a single key lookup: cheap and fast. The cost is write amplification, wasted work for followers who never log in, and slow propagation for very large authors.

    Fan-out on read (pull). Store only posts. On a feed load, fetch the list of accounts the user follows, query each one's recent posts, and merge. Writes are trivial and never wasted. Reads are expensive: with 200 follows, one feed load is up to 200 lookups plus a merge, on the critical path.

    Hybrid. Push for ordinary authors, pull for celebrities. The rule is a follower-count threshold (for illustration, something like 100,000 followers, tuned from measurements). Posts from accounts below the threshold are pushed into followers' timelines. Posts from accounts above it are not fanned out; they are kept in a per-author recent-posts list. At read time the feed service merges the user's precomputed timeline with the latest posts of the few high-fan-out accounts the user follows. Since most people follow only a handful of celebrities, the extra read work is small.

    sequenceDiagram participant A as Author participant PS as Post service participant Q as Queue participant W as Worker participant T as Timeline store participant R as Reader participant F as Feed service A->>PS: publish post PS->>Q: new post event Q->>W: deliver event W->>T: push to followers if author is not a celebrity R->>F: open feed F->>T: read timeline F->>F: merge celebrity posts and rank F-->>R: first page
    Strategy Use when Cost Alternative
    Fan-out on write Read-heavy, modest and even follower counts Write amplification, wasted work for inactive users, slow for huge authors Hybrid
    Fan-out on read Few follows per user, strong freshness, small scale Slow, expensive reads on the critical path Cache merged results briefly
    Hybrid Skewed follower distribution (typical social networks) Two code paths, threshold tuning, merge logic Push only to active users

    Further optimisations. Fan out only to recently active followers and rebuild a dormant user's timeline by pulling on return. Batch timeline writes and partition the queue by follower shard so a big fan-out spreads over many workers. Throttle one author's fan-out so a spike does not starve other work.

    NOTE

    The threshold is a policy, not a law. Measure the cost of pushing (writes per second, latency to last follower) and the cost of pulling (extra reads per feed load) and choose the point where they balance for your traffic.

    The per-author decision can be written as a flow that the fan-out worker runs for every new post.

    flowchart TD P["New post event"] --> A{"Followers above threshold?"} A -->|yes| B["Write to author recent-posts list only"] A -->|no| C{"Is the follower active recently?"} C -->|yes| D["Push post ID to their timeline"] C -->|no| E["Skip, rebuild by pull on return"] B --> F["Readers merge it at read time"]

    How to read it: only the above-threshold branch (author recent-posts list) adds read-time work, and only the push branch costs write-time work. The threshold and the activity window are the two knobs.

    Deep dive 2: ranking, storage and pagination

    Ranking basics. Reverse-chronological is simplest and predictable. Most products rank. A common shape is a two-stage pipeline: candidate generation (the timeline plus celebrity posts, perhaps a few hundred candidates) followed by scoring (a model or a hand-tuned formula using signals such as recency, author affinity, past engagement and post type), then a final pass for diversity (do not show ten posts from one author in a row) and policy rules. Scoring happens at read time over a bounded candidate set, so cost stays predictable. We will not design the model here; the system-design point is that the ranking service must be fast, have a timeout and a fallback to plain chronological order if it is slow or down.

    Why cursors beat offsets. With OFFSET 40 LIMIT 20, new posts arriving at the top shift everything, so the user sees duplicates or skips items. A cursor says "give me items older than this ID", which is stable under insertions and efficient on a sorted index. For a ranked feed, which is not strictly ordered by time, the server may snapshot the ranked list of candidate IDs for a short time (a "feed session"), store it in a cache keyed by a session ID, and page through the snapshot. The cursor then encodes session and position.

    Timeline storage details. Cap each timeline (for example the latest few hundred to a thousand entries). Older scrolling falls back to a slower pull path over the post store. Capping bounds memory and makes pushes cheap because you trim on insert. Keep entries small: IDs, not bodies. Hydrate bodies through a cache because a hot post appears in millions of timelines, and you want one cached copy, not millions.

    Unfollow, delete and block. A delete must disappear from feeds. Rather than hunting through millions of timelines, the feed service filters at read time: after hydration, drop posts that are deleted, from blocked authors, or no longer visible to this viewer. Lazy cleanup removes stale IDs in the background. Unfollow works the same way: filter by current following list at read time.

    Caching strategy

    Layers, from the outside in: the client cache keeps the last page so opening is instant; the timeline store is already a precomputed result for active users; the post cache holds post bodies and author snippets cache-aside, with TTLs and explicit invalidation on edit or delete; an optional feed page cache holds the rendered page for a few seconds, but personalisation keeps hit ratios modest, so evaluate before adding it.

    Hot keys are a real risk: a viral post's cache entry may receive enormous read traffic. Replicate hot entries across cache nodes or keep a tiny in-process cache in each feed server with a very short TTL. See advanced caching for hot-key and stampede techniques.

    Bottlenecks and scaling

    • Fan-out workers. Scale on queue lag. Partition work by follower shard so each worker batch writes to one timeline shard.
    • Timeline store. Shard by user_id. Memory is the dominant cost, so cap lengths and consider keeping only active users in memory.
    • Social graph reads. Fan-out needs the follower list, which can be millions of IDs. Page through it, and cache the lists of mid-sized authors.
    • Feed service latency. The slowest dependency sets the tail. Issue timeline, celebrity and cache reads in parallel and set per-call timeouts.
    • Ranking cost. Bound candidate set size and score in batches. Keep a cheap fallback.

    Failure modes

    Failure Effect Mitigation
    Fan-out queue backlog New posts delayed in feeds Autoscale on lag, prioritise small authors, shed or throttle huge fan-outs, alert on lag
    Timeline store node lost Some users have empty or stale feeds Replicas, and a rebuild path: pull recent posts from followed accounts and refill
    Ranking service slow or down Slow feeds Timeout and fall back to chronological order
    Post cache stampede Database overload Request coalescing, jittered TTLs
    Duplicate fan-out (retry) Same post twice in a timeline Idempotent insert keyed by (user_id, post_id), so repeats are no-ops
    Celebrity post storm Hot cache keys, read spike Replicate hot entries, per-process micro-cache
    Graph store unavailable Cannot fan out or filter Retry from queue, serve existing timelines without new entries
    Deleted post still shown Trust and legal problem Read-time visibility filter, plus lazy cleanup

    The rebuild path deserves emphasis. A cache or timeline store is a derived copy, so you must be able to regenerate it. If you cannot rebuild timelines from the post store and graph, you have turned a cache into your database without realising it.

    Trade-offs worth stating

    Decision Option A Option B How to choose
    Feed order Chronological Ranked Chronological is simple and transparent. Ranked boosts engagement but needs a model and a fallback
    Fan-out target All followers Active followers only Active only saves large amounts of work, at the cost of a rebuild on return
    Timeline content IDs only Full post copies IDs only. Copies make edits and deletes painful
    Freshness Strong, immediate Eventual, seconds Seconds of delay are rarely noticed and cut cost sharply
    Celebrity threshold Low High Tune from measured push cost versus pull cost

    Interview cheat sheet

    Step What to say Key numbers (illustrative)
    Requirements Publish, follow, home feed, scroll. Read-heavy, seconds of delay OK 100M DAU
    Estimate Pure push means billions of timeline writes per day 580 posts/s, about 115k timeline writes/s, 35k feed loads/s peak
    API Opaque cursor pagination 20 per page
    Data Posts, two graph mappings, capped timeline of IDs About 1.6 TB timelines
    Design Queue and fan-out workers, timeline store, feed service, post cache Hybrid push and pull
    Deep dives Hybrid threshold and celebrity merge, ranking pipeline with fallback, cursor stability Candidate then score
    Failures Fan-out lag, timeline loss and rebuild, stale deletes Idempotent inserts
    Trade-offs Push versus pull, ranked versus chronological, active-only fan-out Threshold is tunable

    In practice: operating the reference design

    NOTE

    A realistic scenario, not a case history. This is a composite example built from common production patterns. It does not describe any specific company, and every number in it is an illustrative assumption.

    Assume a platform team of ten runs this on Kubernetes with Kafka for the fan-out topic, Redis for timelines and the post cache, PostgreSQL or a wide-column store for posts, and Prometheus and Grafana for monitoring.

    SLOs and dashboards

    SLI SLO (assumed) Dashboard panels
    Feed load succeeds 99.9% over 30 days Success ratio, p50 and p99 latency per dependency
    Feed load latency p99 under 300 ms Timeline read, celebrity merge, hydration and ranking time
    Fan-out freshness 99% of pushes land within 10 s Consumer lag in messages and seconds, worker utilisation
    Ranking fallback rate Under 1% of loads Ranking timeouts, fallback count
    Deleted content hidden Within 60 s Post cache invalidation age, read-time filter hits

    Capacity-review cadence

    • Weekly: fan-out lag at peak, timeline memory headroom per shard, post cache hit ratio, the top 20 authors by follower count.
    • Monthly: re-measure the celebrity threshold from real push cost versus pull cost, and re-check follower-count distribution drift.
    • Quarterly: replay a peak day against a staging cluster, and run a timeline-shard rebuild drill.

    Runbook

    Symptom Likely cause First check Mitigation
    New posts show up late Fan-out lag after a large author posts Consumer lag by partition, per-author push rate Throttle that author, add workers, raise their celebrity flag
    A slice of users sees empty feeds Timeline shard lost or evicted Shard health, key count per shard Fail over to replica, rebuild most active users first
    Feed latency up, errors flat Ranking or hydration slow Per-dependency timings Shorten ranking timeout, serve chronological
    Database load spikes Post cache stampede or hot key Cache hit ratio, top keys Coalesce requests, jitter TTLs, replicate the hot entry
    Same post twice Retry without idempotent insert Insert path logs Use set semantics keyed by user and post
    Deleted post still visible Cache not invalidated Post cache entry for that ID Invalidate explicitly, verify the read-time filter

    Decision record: hybrid fan-out threshold

    Field Entry
    Context Pure push turns one post from a 50-million-follower author into 50 million writes. Pure pull makes every feed open do up to 200 lookups.
    Decision Push for authors under a follower threshold (start at 100,000, tune monthly), pull for the rest, and push only to followers active in an assumed 30-day window.
    Consequences (good) Bounded write amplification, cheap reads for most users, no storm from one post.
    Consequences (bad) Two read paths, a merge step with its own latency, and a rebuild path for returning users.
    Revisit when Merge time exceeds a set share of the 300 ms budget, or the number of authors above the threshold grows enough that pull cost dominates.

    Cost view

    Memory for timelines dominates. At the lesson's assumptions the capped timelines are about 1.6 TB, so roughly 3.2 TB with one replica per shard, before overhead. Fan-out compute and post-cache memory follow.

    Cost driver Lever
    Timeline memory Cap length, store IDs only, keep dormant users out of memory
    Fan-out compute Active-only push, batching per shard, a celebrity threshold
    Post cache Short TTLs for volatile fields, one cached copy per post
    Ranking Bound candidate count, score in batches

    Architect's notes

    • Measure the follower distribution before picking a threshold. The shape of the tail, not the average, decides the design.
    • Ask what "fresh" means to the product. If seconds of delay are fine, you can batch and throttle aggressively. If not, the budget changes everywhere.
    • Watch memory, not just CPU. At these assumptions timeline memory (see the cost view) is the largest line item, so size it as carefully as the workers.
    • Decide who owns the ranking fallback. The ranking team must agree that chronological order is an acceptable degraded mode, and who gets paged when it activates.
    • Defer personalisation depth. Ship chronological plus a simple score before building models. Keep the fallback working at every step.
    • Rehearse the rebuild. A timeline store you cannot rebuild is a database nobody backed up.

    Common mistakes

    • Choosing pure fan-out on write. It breaks on accounts with huge follower counts. Use a hybrid with a threshold.
    • Choosing pure fan-out on read for a large product. Feed loads become the slowest, most expensive path. Precompute for ordinary authors.
    • Storing full post bodies in every timeline. Edits and deletes become impossible to apply. Store IDs and hydrate through a cache.
    • Using offset pagination. New items cause duplicates and skips, and deep offsets are slow. Use cursors.
    • Ignoring deletes, blocks and unfollows. Stale entries leak content. Filter at read time and clean up lazily.
    • No fallback for ranking. A ranking outage becomes a feed outage. Time out and fall back to chronological.
    • Treating the timeline store as the source of truth. Without a rebuild path, one lost node loses data. Keep it derivable.

    Key takeaways

    • A feed is a materialised view. The design question is when to compute it: at write time, read time, or both.
    • Fan-out is the cost driver. Posts times followers is the number that matters, and it is skewed.
    • Hybrid wins for skewed graphs. Push for ordinary authors, pull for celebrities, merge at read time.
    • Store IDs, hydrate from cache. One cached copy of a post serves millions of timelines.
    • Cursors keep scrolling stable. Offsets break when the top of the list changes.
    • Rank in two stages. Generate bounded candidates, then score, with a chronological fallback.
    • Everything derived must be rebuildable. Plan the rebuild path for timelines and caches.
    • Filter deletions at read time. Cleanup is lazy, visibility is immediate.

    Practice exercises

    Exercise 1 — Starter (30 min): Using the illustrative numbers in this lesson, compute the timeline writes per second if every post fanned out to every follower, for authors averaging 200 followers versus 2,000 followers. Then compute how long one post from a 50-million-follower author would take to fan out if workers collectively sustain 200,000 writes per second.

    Exercise 2 — Intermediate (2 hours): Design the data schema and the exact read algorithm for the hybrid feed: how the feed service merges the timeline with three followed celebrities' recent posts, deduplicates, handles a user who just followed a celebrity, and produces a cursor that stays valid across pages. Write pseudocode and an example with five timeline entries and two celebrity posts.

    Exercise 3 — Advanced (half day): Add "mute this author" and "post edited" features. Specify what changes in the timeline store, the post cache, the read-time filters and the ranking inputs. Then write a runbook for the failure where the timeline store loses a shard: how you detect it, how you rebuild affected users (most active first), and how the product behaves in the meantime.

    Enroll to keep reading

    Lesson 11 of 14 · Advanced System Design and Case Studies

    Design a social news feed: fan-out on write versus read, the celebrity problem, hybrid delivery, ranking basics, timeline storage, pagination and failure modes.

    In the rest of this lesson

    • Data model
    • High-level design
    • Reference deployment topology
    • Deep dive 1: fan-out on write, on read, and hybrid
    • Deep dive 2: ranking, storage and pagination
    • Caching strategy

    Enrolling is free. It unlocks every lesson in Advanced System Design and Case Studies, tracks your progress across devices and keeps your quiz scores. The newsletter is optional.

    Ask about this article

    Get answers grounded in this post. AI-generated — based on this article, and may be imperfect.

    Scaled AI Weekly

    Enjoyed this? Get more like it every Monday.

    Real architecture decisions, LLMOps patterns that survive production, and engineering leadership advice — from 12+ years of building at enterprise scale. Free. No spam. Unsubscribe anytime.

    Get new articles by email

    Free. We email a confirmation link first, and you can unsubscribe from any email.

    Join engineers building production AI systems

    Comments