Designing a News Feed System

A system design interview guide to building the home feed of a social application: how a post travels from one author to the screens of millions of followers, and how the right blend of push and pull keeps both writes and reads fast.

Student edition · start with the guarantee

Choose push, pull, or hybrid feeds from measured amplification, then serve stable pages with current privacy rules.

Correctness contract: The post and fanout intent are durable; feed references are idempotent hints. Serving rechecks visibility, deletion, and pagination policy.

Basic HTTP, databases, and programming are assumed. Specialized terms are explained below. Named products motivate the problem; this is an educational design, not a claim about their current internals. Capacity numbers are illustrative decimal payload estimates, not benchmarks. Add indexes, protocol overhead, replicas, headroom, and failure capacity before provisioning.

A news feed is the continuously updating list of posts a user sees when they open a social application — status updates, photos, links, and so on, drawn from the accounts they follow. It looks simple from the outside, but it hides a sharp tension. The system must serve feeds to active users with very low latency, yet a single popular post may need to reach millions of followers. The whole design comes down to deciding when the work of assembling a feed happens: eagerly at the moment of posting, lazily at the moment of reading, or some combination of the two. This guide walks through the two core operations, the fanout models that resolve that tension, the data flow that ties them together, and the caching layers that keep the common path fast.

Contents

  1. Requirements & Scope
  2. Back-of-the-Envelope Estimation
  3. API Design
  4. Data Model
  5. Two Core Operations
  6. System Architecture
  7. Fanout Models
  8. The Publish & Fanout Flow
  9. Caching Layers
  10. The Celebrity Problem
  11. Ranking & Pagination
  12. Failure Modes
  13. Tradeoffs & Alternatives
  14. Summary
  15. 45-Minute Interview Walkthrough
  16. Common Mistakes
  17. Check Yourself
  18. Key Terms & Further Reading
  19. Concepts to understand first
  20. Worked example
  21. Runnable Python model

Concepts to understand first

Each definition includes a concrete example and the condition under which the mechanism is useful. You can read this guide without opening another guide.

Fanout on write and read

Fanout distributes one event to many recipients. On-write fanout precomputes recipient inbox entries; on-read fanout merges source events when a recipient asks. A hybrid chooses using measured write amplification and read cost.

Example. One post to 200 followers creates up to 200 inbox inserts. A celebrity's million followers can instead read the author's shared stream when they open their feed.

Limit. Fanout is not atomic across inboxes. Persist the post and delivery work via an outbox, dedup references, catch up after misses, and recheck privacy/deletion at serving time.

Cache-aside and invalidation

A read checks a cache, reads the authoritative store on a miss, then fills the cache. A write commits to the store and invalidates or refreshes the cached value. TTL means time to live, not a freshness proof.

Example. A reader fetches version 1, a writer commits version 2 and invalidates, then the delayed reader caches version 1. DB-first invalidation alone does not prevent this refill race.

Limit. Use invalidation-aware fill leases or version fences for stronger behavior; a TTL only bounds one entry's residence under additional assumptions. Authorization and revocation need an explicit stale-data policy.

Sequence numbers and replay cursors

A sequence number records committed order within a stream. A cursor is a consumer's durable position in that stream, so it can resume without skipping missing events. An opaque identity is not automatically such a position.

Example. A device receives message sequence 10 then 12. It acknowledges contiguous 10, buffers 12, and fetches 11 before advancing to 12.

Limit. A Snowflake timestamp can arrive late behind an already-acknowledged cursor. Use per-stream commit order or a durable inbox offset, define retention gaps, and use separate device cursors.

Transactional outbox

A business transaction writes its state change and an event row to the same database. A relay publishes committed event rows later, retrying until the broker durably accepts them.

Example. An order and its OrderCreated outbox row commit together. A relay crash after publish but before marking sent publishes the same event ID again on restart.

Limit. The relay is at-least-once, not magically exactly-once. Consumers dedup transactionally; preserve per-entity sequence when order matters. CDC is change data capture: reading committed database changes.

Backpressure and load shedding

Backpressure tells an upstream producer that the downstream stage cannot accept more work. Buffering delays that signal; shedding deliberately rejects or drops lower-priority work when a bound is reached.

Example. A sink accepts 100 events/s while input is 150/s. A 1000-event queue fills in 20 seconds. The system must then block, reject, or apply its documented drop policy.

Limit. A finite queue cannot absorb an infinite outage. Prioritized messages still need hard quotas, deadlines, and bounded client queues; otherwise an exemption can exhaust the system.

Top-k candidate discovery

Frequency estimation and discovering which item IDs to ask about are separate problems. A bounded candidate tracker proposes IDs, then a sketch or exact second pass scores them.

Example. Shard 1 has a=10,x=9; shard 2 has b=10,x=9. Neither local top-1 includes x, but x=18 is the global winner.

Limit. Increasing the local candidate buffer reduces risk but is not a general guarantee. Hash-partitioning each identity to one shard or using a heavy-hitter algorithm with stated bounds changes the analysis.

1. Requirements & Scope

Every design interview starts by agreeing on what you are building. Spend the first few minutes asking questions; the answers change the design.

Clarifying questions to ask
TypeRequirement
FunctionalA user can publish a post. A user can open their home feed and see recent posts from accounts they follow, newest (or best) first, and scroll back through older pages.
FunctionalFollowing or unfollowing someone changes what the feed shows.
Non-functionalLow read latency: feed opens in a few hundred milliseconds at p99. Reads are the hot path.
Non-functionalHigh availability over strict consistency: it is fine if a new post appears in followers' feeds a few seconds late (eventual consistency).
Non-functionalScale: hundreds of millions of daily users with a very skewed follower distribution.
Intuition

Think of the feed as a personal mailbox. The only real question in this whole design is when the mail gets sorted into each mailbox: when it is sent (push) or when the owner opens the box (pull).

2. Back-of-the-Envelope Estimation

Rough numbers tell you which part of the system is the bottleneck. Assume 500 million daily active users (DAU), each opening the feed 10 times a day, posting on average once every five days, with 200 followers on average. A day has 86,400 seconds; round it to about 100,000 for mental math.

QuantityWorkingResult
New posts per day500M × 0.2100M / day
Post write rate100M ÷ 86,400 (peak ≈ 3× average)≈ 1.2K/s, peak ≈ 3.5K/s
Feed reads500M × 10 ÷ 86,400 (peak 3×)≈ 58K/s, peak ≈ 174K/s
Fanout inserts (pure push)100M posts × 200 followers ÷ 86,400≈ 231K/s
Feed cache size500M users × 500 ids × 16 bytes≈ 4 TB (before replication)
TakeawayReads outnumber posts about 50 : 1, and one post becomes ~200 feed writesOptimize the read path; bound the fanout
Tip

4 TB of feed cache is large but ordinary: spread over a Redis or Memcached cluster of machines with ~100 GB RAM each, that is a few dozen nodes, plus replicas. Say this out loud; it shows the design is affordable.

3. API Design

Two endpoints cover the core. Both are authenticated; the user id comes from the auth token, never from the request body.

POST /v1/posts
  body: { "content": "text", "media_ids": ["m123"] }
  201 → { "post_id": "7310...", "created_at": "2026-09-25T10:00:00Z" }

GET /v1/feed?limit=20&cursor=<opaque>
  200 → { "posts": [ {post_id, author, content, created_at, ...}, ... ],
          "next_cursor": "<opaque>" }        # null when no more

The feed uses cursor pagination, not ?page=3. A cursor encodes "the last item I showed you" (for example the last post id). New posts arrive at the top all the time; with page numbers, every new post shifts the pages and the user sees duplicates or skips posts. A cursor says "give me what comes after this item", which stays stable.

4. Data Model

StoreKeyValue / columnsNotes
Post DBpost_idauthor_id, content, media_ids, created_at, deletedSource of truth. Time-sortable ids (Snowflake-style) let ids double as a sort order.
Graph DB(user_id, followee_id) and the reversecreated_atBoth directions stored so "who do I follow" and "who follows me" are single lookups. See the Social Graph guide.
News feed cachefeed:{user_id}List or sorted set of (post_id, author_id), newest first, capped at ~500–1,000 entriesOnly references. Older history is rebuilt on demand from the Post DB.
Post cachepost:{post_id}Serialized postHydrates feed references into full posts.
User cacheuser:{user_id}name, avatar, follower_count, is_celebrity, last_activeUsed by fanout to skip inactive users and classify celebrities.

5. Two Core Operations

Before any architecture, it helps to separate the feed into the two distinct things it actually does. They have opposite performance profiles, and almost every design decision is a tradeoff between them.

OperationWhat happensWho triggers it
Feed publishingA user creates a post. The system stores it and then has to make it visible in the feeds of everyone who follows that user.The author, once per post.
Feed building / retrievalA user opens the app. The system has to assemble an ordered list of recent posts from the people that user follows and return it.Every reader, every time they open or refresh.

The asymmetry is the key insight. Publishing is comparatively rare — a person posts a handful of times a day at most. Retrieval is constant — the same person may open and refresh their feed dozens of times a day, and there are far more readers than authors active at any instant. A naive feed reads, for every refresh, the recent posts of every account the user follows, sorts them, and returns the top slice. That is correct but expensive on the hot path. The art of feed design is moving as much work as possible off the read path without paying for work nobody will ever look at.

A useful framing: feed building is a join between "who I follow" and "their recent posts," ordered by time or relevance. The question is whether you precompute that join (do the work at write time) or compute it on demand (do it at read time).

6. System Architecture

At a high level the system is split into a request-handling tier, a write path for storing posts, and a separate fanout path that distributes those posts into per-user feeds. A client request first resolves through DNS to a load balancer, which spreads traffic across a pool of stateless web servers. Those servers handle cross-cutting concerns — authentication to identify the caller and rate limiting to protect downstream services — before forwarding to the appropriate internal service.

News feed system architecture
A client request flows through DNS, a load balancer, and authenticating, rate-limiting web servers. Publishing writes to the Post Service (cache + DB) and triggers the Fanout Service, which reads the social graph and user data, queues work, and lets fanout workers populate each follower's news feed cache. A notification service runs alongside.

From there the request takes one of two paths:

A Notification service runs alongside the fanout path, so that delivering a post can also trigger a push notification or badge update where appropriate. Decoupling fanout behind a queue is what keeps the author's publish call fast: the post is acknowledged as soon as it is durably stored, and the potentially huge job of distributing it happens asynchronously.

7. Fanout Models

"Fanout" is the act of delivering one post to many follower feeds. There are two pure strategies, and the choice between them is the single most important decision in the whole design. Each optimizes one of the two core operations at the expense of the other.

ModelHow it worksWritesReadsWeakness
Fanout on write (push)When a user posts, immediately write the post id into the feed cache of every follower. Each follower's feed is a precomputed list, ready to serve.Heavy — one write per follower, per post.Very fast — the feed is already assembled; a read is a single cache lookup.Wasteful for inactive followers who never read; a hotkey explosion of writes when an author has millions of followers.
Fanout on read (pull)Write only the post itself. Build each feed lazily at read time by pulling the recent posts of everyone the reader follows and merging them.Light — one write, regardless of follower count.Heavy — every refresh fans out across all followed accounts, fetches and merges.Slow, expensive reads on the hot path; cost scales with how many accounts each user follows.
Hybrid (recommended)Use push for ordinary users, and pull for celebrities and accounts with very large follower counts. A reader's feed merges their precomputed push feed with freshly pulled celebrity posts.Bounded — push for the many, no explosive fanout for the few.Fast — mostly a precomputed lookup, plus a small pull for a handful of followed celebrities.More moving parts; needs a rule to classify accounts as "celebrity."

Fanout on write makes reads trivial: when a follower opens the app, their feed already exists as a list in cache, so retrieval is a single lookup. The price is paid at write time. Posting once may fan out to every follower, which is fine for someone with a few hundred followers but catastrophic for someone with tens of millions — a single post becomes tens of millions of cache writes. It also does work for followers who may not log in for weeks, spending write capacity on feeds nobody reads.

Fanout on read inverts the tradeoff. Publishing is cheap because you store the post and stop; no fanout happens. The cost moves entirely to the read path, where every feed open must gather the recent posts from each followed account and merge them on the fly. This naturally handles celebrities — a popular author causes no write storm — but it makes the most frequent operation, reading, the most expensive one.

The hybrid model is the recommended approach because it lets each kind of account use the strategy that suits it. The overwhelming majority of users have modest follower counts, so pushing their posts is cheap and gives their followers instant reads. The small number of accounts with enormous follower counts are exactly the ones that would cause a write explosion, so their posts are pulled instead. At read time, a user's feed is the precomputed push feed merged with a small live pull of the handful of celebrities they follow.

Fanout on write (push) Fanout on read (pull) Author posts Feed of follower 1 Feed of follower 2 Feed of follower N N writes when posting · 1 read when opening Posts of A Posts of B Posts of K Reader opensfeed: merge 1 write when posting · K reads when opening
Push moves the work to posting time (one write per follower). Pull moves it to reading time (one fetch per followed account, then a merge).
Intuition

Push is like a newspaper delivered to every doorstep: expensive for the publisher, instant for the reader. Pull is like walking to each newsstand yourself: free for the publisher, slow for the reader. Most people get the paper delivered; a few huge publications are picked up at the stand.

8. The Publish & Fanout Flow

Tracing a single post through the system makes the fanout path concrete. Once the Post Service has durably stored a post and the Fanout Service picks it up, the numbered steps from the architecture diagram are:

  1. Get friend ids from the graph DB. The Fanout Service asks the social graph for the list of accounts that follow the author. This list is the set of feeds the post must reach.
  2. Get friend data from the user cache / DB. For each follower the service may need lightweight user data — for example to skip inactive accounts, respect privacy or block settings, or apply per-user ranking signals. This comes from the user cache, falling back to the user DB on a miss.
  3. Push to a message queue. Rather than writing millions of feed entries inline, the service enqueues distribution work — typically batches of (post id, follower id) deliveries. The queue absorbs bursts and decouples the author's request from the heavy work.
  4. Fanout workers consume. A horizontally scalable pool of workers drains the queue. Because the work is in a queue, the system can add workers to drain a backlog without touching the publish path, and a slow spell simply means the queue grows rather than the author's request stalling.
  5. Write into the news feed cache. Each worker writes the post reference into the target follower's news feed cache, prepending it so the most recent posts sit at the top of the list. After this step, that follower's next feed open is a fast cache read.
# publish: fast, synchronous part
function publish(author_id, content):
  post = post_service.store(content)        # Post DB + Post cache
  fanout_service.enqueue_fanout(post)       # hand off, return now
  return ack(post.id)                       # author's call is done

# fanout: heavy, asynchronous part
function enqueue_fanout(post):
  followers = graph_db.get_followers(post.author_id)   # step 1
  for batch in chunk(followers, size=1000):
    enrich(batch, user_cache)                          # step 2
    message_queue.push(post.id, batch)                 # step 3

function fanout_worker():                               # step 4
  while True:
    post_id, batch = message_queue.pop()
    for follower_id in batch:
      news_feed_cache.prepend(follower_id, post_id)     # step 5
      news_feed_cache.trim(follower_id, MAX_FEED_LEN)   # keep newest ~800 only
Celebrity posts are the exception to this flow. Under the hybrid model an account above a follower threshold is not fanned out at all; its posts stay in the post store and are pulled in at read time, sidestepping steps 1 through 5 entirely for that author.

9. Caching Layers

Caching is what makes the read path cheap, and the important design choice is what to store where. The two caches play different roles.

CacheWhat it storesWhy
News Feed cacheAn ordered list of ⟨post id, user id⟩ pairs per follower — references, not full posts.Keeps each feed list small and cheap to write during fanout. A million followers means a million tiny list updates, not a million copies of the content.
Post cacheThe full content of recent posts, keyed by post id.Lets the read path turn a list of ids into displayable posts with fast lookups, without hitting the Post DB for hot content.

The reason the news feed cache stores only id pairs rather than whole posts is both space and consistency. Storing references means a post exists in exactly one place — the post store — even though it appears in millions of feeds. If the post is edited or deleted, the single canonical copy changes and every feed reflects it on the next read; there is no fan of stale duplicates to chase down. The cost is one extra step at read time: after reading the list of references from the news feed cache, the system hydrates them by fetching the full content for each id from the post cache (falling back to the post DB on a miss).

function get_feed(user_id, limit):
  refs = news_feed_cache.get(user_id, limit)   # ⟨post id, user id⟩ list
  if hybrid_enabled(user_id):
    refs = merge_by_time(refs, pull_celebrity_posts(user_id))[:limit]  # hybrid read
  posts = []
  for ref in refs:
    post = post_cache.get(ref.post_id) \
        or post_db.get(ref.post_id)            # hydrate full content
    if post is None or post.deleted:           # deleted since fanout: skip
      continue
    posts.append(post)
  return posts                                 # ordered, ready to render
Common mistake

Storing the whole post in every follower's feed. With 200 followers per post that is 200 copies of the content, and an edit or delete must chase all of them. Store ids in the feed; store content once.

10. The Celebrity Problem

Push/pull move costs rather than remove them. Pull still makes a popular author's stream a hot read key; cache/replicate immutable batches and coalesce refreshes. A reader may follow many celebrities, so 'a handful' is an assumption, not a bound.

Choose policy from posting rate, active followers, measured fanout cost, and latency budgets. Mode switches need overlap/backfill/dedup to prevent gaps. Partition durable fanout tasks and limit an author's share so viral activity cannot starve ordinary work.

11. Ranking & Pagination

Chronological order uses an explicit timestamp plus deterministic tie-breaker or committed sequence. Snowflake ID order is only approximate clock order. Ranked feeds generate bounded candidates, score them, and apply diversity/visibility rules; bounded candidates entail a recall tradeoff.

For stable ranked pagination, retain a short-lived ranking snapshot and cursor into its ordered set, or explicitly handle duplicate/skip behavior when ranks change. Always recheck privacy/blocks/deletions, even for an old ranking snapshot. Bind opaque cursors to user, query, expiry, and snapshot identity.

12. Failure Modes

What goes wrongEffectHow the design handles it
Fanout workers fall behind (queue backlog)New posts appear late in feeds.The queue absorbs the burst; add workers (autoscale on queue depth). The author's publish call is not affected because fanout is async.
Feed cache node diesThose users' precomputed feeds are gone.Rebuild on read: pull recent posts from followed accounts (the pull path) and re-populate the cache. Replicas reduce how often this happens.
A fanout worker crashes mid-batchSome followers got the post, others did not.The queue redelivers the batch (at-least-once). Writes are idempotent: inserting the same post id twice into a sorted set keyed by id is a no-op.
Post deleted after fanoutStale references in many feeds.Hydration checks the canonical post and skips deleted ones; references expire naturally as the capped list rolls over.
User unfollows someoneOld posts from that author still sit in the feed cache.Filter at read time against the current following set, or run a small async cleanup job for that user's list.
Thundering herd after an outageMillions of cold feeds rebuilt at once overload the Post DB.Rate-limit rebuilds, serve a slightly stale or shorter feed, and warm the caches for the most active users first.

13. Tradeoffs & Alternatives

DecisionOption AOption BUsual choice
Fanout modelPush: fast reads, heavy writesPull: cheap writes, slow readsHybrid, split by follower count
Who gets pushed toAll followersOnly recently active followersActive only; inactive users get a pull-built feed when they return
Feed contentFull posts in the feedPost id referencesReferences plus a post cache
OrderingChronologicalRanked by a modelRanked at large scale; chronological is simpler and fully predictable
PaginationOffset / page numberCursorCursor, because the list changes constantly

14. Summary

A news feed design is a series of decisions about moving work between the write path and the read path:

ConcernApproach
What are the two operations?Feed publishing (an author posts) and feed building/retrieval (a reader opens their feed) — rare heavy-distribution work versus constant read work.
How does a post become durable?The Post Service stores it in the Post DB and warms the Post cache before any distribution happens.
How does a post reach followers?Fanout: push (write into every follower's feed at post time), pull (build the feed at read time), or a hybrid of both.
Which fanout model to choose?Hybrid — push for ordinary users (fast reads), pull for celebrities (no write storm).
How is fanout kept off the publish path?The Fanout Service reads the graph, enriches with user data, and queues work for fanout workers that write into the news feed cache asynchronously.
What lives in the feed cache?⟨post id, user id⟩ references, not full posts; content is hydrated from the post cache/DB at read time.
How is the celebrity hotkey solved?Skip fanout for very-high-follower accounts and pull their posts in at read time, merging them into the precomputed feed.
The recurring theme: there is no single right place to do the work. Publishing and reading pull in opposite directions, and a good feed lets each account — ordinary or celebrity — use the strategy that keeps both the write storm and the read latency under control.

Worked example: a cache-miss storm during fanout

Assume 20,000 reads/s and a 99% hit rate.

Worked example, in orderNormal read load: 1% misses gives 200 backend reads/s. RAM depends on resident users and measured entry overhead, not only reference payload.. Hit rate drops to 95%: Backend misses rise to 1000/s, fivefold. Coalesce fills and bound fallback fan-in to protect storage.. Publish a celebrity post: Pull avoids millions of inbox writes but creates hot author reads. Cache shared streams and bound followed-source fan-in.. Page a ranked feed: Freeze/version the ranking batch or define changing-rank semantics. Recheck current authorization and removal on every page.1Normal read load2Hit rate drops to 95%3Publish a celebrity post4Page a ranked feed
Read top to bottom. The table below explains the state and guarantee at each step.
StepState, calculation, and reasoning
1. Normal read load1% misses gives 200 backend reads/s. RAM depends on resident users and measured entry overhead, not only reference payload.
2. Hit rate drops to 95%Backend misses rise to 1000/s, fivefold. Coalesce fills and bound fallback fan-in to protect storage.
3. Publish a celebrity postPull avoids millions of inbox writes but creates hot author reads. Cache shared streams and bound followed-source fan-in.
4. Page a ranked feedFreeze/version the ranking batch or define changing-rank semantics. Recheck current authorization and removal on every page.
Check your understanding: Does a cached reference prove a post is still visible?

No. Blocks, privacy, membership, moderation, and deletion may change after fanout. The read path must enforce the current policy.

Run a small, testable model

Download concepts.py. It uses only the Python 3.10+ standard library. From the directory where you saved it, run:

python3 concepts.py delivery
python3 concepts.py test

The cursor model demonstrates stable ordered paging and why maximum-seen IDs can skip delayed entries.

The program contains deterministic assertions and failure traces. It is a learning model, not a production service: it does not implement network consensus, distributed durability, authentication, or third-party delivery. Read its contracts and try changing an input before extending it.

Primary references for these concepts

15. 45-Minute Interview Walkthrough

TimePhaseWhat to cover
0–5 minRequirementsChronological or ranked, follow or friend, media, DAU, freshness. Write the functional and non-functional lists.
5–10 minEstimationRead and write QPS, fanout inserts per second, feed cache size. Conclude: reads dominate, fanout must be bounded.
10–15 minAPI + data modelPOST /posts, GET /feed?cursor; posts, graph, feed cache of ids.
15–25 minHigh-level designDraw the publish path and fanout path. Explain push vs pull and pick hybrid.
25–40 minDeep divesCelebrity problem, caching layers and hydration, failure handling, ranking if asked.
40–45 minWrap-upRecap tradeoffs, name bottlenecks, suggest monitoring (fanout lag, feed p99 latency).

16. Common Mistakes

17. Check Yourself

  1. A user has 50 followers and posts twice a day. Push or pull, and why?
    Answer

    Push. Each post costs only 50 cheap cache writes, and all 50 followers get instant reads. Pull would make every one of their many feed opens do extra work.

  2. With 100M posts a day and 200 followers each, how many feed inserts per second does pure push need?
    Answer

    100M × 200 = 20 billion a day; 20B ÷ 86,400 ≈ 231K inserts per second on average, more at peak.

  3. Why does the feed cache store post ids instead of whole posts?
    Answer

    Space (one small reference per follower instead of a full copy) and consistency (an edit or delete changes one canonical copy; readers hydrate the latest version).

  4. A fanout worker crashes after writing to half the followers of a batch. What stops duplicates when the batch is retried?
    Answer

    Idempotent writes: the feed is a set keyed by post id (for example a sorted set), so inserting the same id again does nothing.

  5. Why is cursor pagination better than page numbers for a feed?
    Answer

    New posts keep arriving at the top. Page numbers shift, causing duplicates and skipped posts; a cursor ("items after post X") stays stable.

  6. How does a user who follows five celebrities get their posts under the hybrid model?
    Answer

    At read time the service pulls the recent posts of those five accounts and merges them by time (or score) with the user's precomputed push feed.

18. Key Terms & Further Reading

Fanout
Fanout distributes one event to many recipients. On-write fanout precomputes recipient inbox entries; on-read fanout merges source events when a recipient asks. A hybrid chooses using measured write amplification and read cost. Example and limits above.
Fanout on write (push)
Copying a post reference into every follower's feed when it is published.
Fanout on read (pull)
Building a feed at read time by fetching and merging recent posts from followed accounts.
Hydration
Turning a list of post ids into full posts by looking each one up.
Hot key / celebrity problem
One key (an account) receiving far more traffic than others and overloading its part of the system.
Cursor pagination
Paging by "items after this marker" instead of by page number.
Idempotent
Safe to apply more than once with the same result.

⌂