FSD Frontend System Design

Part VI β€” The Blueprint Β· Lesson 11.4

HLD – News Media Feed (Facebook, Twitter)

In one lineA cursor-paginated list that must accept live inserts without ever moving content under the reader.

Where you've seen itThe "12 new posts" pill at the top of a timeline β€” the polite alternative to yanking the page.

  • Time19 min
  • DiagramCursor feed with buffered live prepends
  • Chapter11 Β· High Level Design

Diagram

diagram
1–2 Β· SCOPE AND SCALE

  in scope   timeline Β· post detail Β· compose Β· like/repost/reply
  out        DMs Β· search Β· trends Β· ads Β· moderation
  scale      300M DAU Β· ~50 posts/session Β· live updates every few sec
             mixed media, mostly text + one image
diagram
3–4 Β· ARCHITECTURE AND DATA

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  GET /feed?after=<cursor>   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Client │◄───────────────────────────►│   BFF   │──► feed service
  β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜                             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    (ranking)
      β”‚  SSE / WebSocket (2.5 / 2.4)
      β–Ό
  live events: post.created Β· post.liked Β· post.deleted

  normalized store (6.6)
    posts:  { p7: {...} }      users: { u1: {...} }
    feed:   ['p9','p8','p7']   ← order separate from entities
diagram
6a Β· DEEP DIVE β€” LIVE INSERTS WITHOUT DISRUPTION

  user at top                    user scrolled down
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ [ 12 new posts β–²]β”‚           β”‚  post 40         β”‚
  β”‚  post 9          β”‚           β”‚  post 41         β”‚
  β”‚  post 8          β”‚           β”‚  post 42         β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
   click β†’ prepend +              buffer silently; scroll anchor
   scroll to top                  stays fixed (10.13)

  Insert immediately ONLY if scrollTop === 0 and the tab is focused.
diagram
6b Β· DEEP DIVE β€” WHY OFFSET PAGINATION BREAKS HERE

  offset  page 2 returns posts already seen, because 12 new
          posts shifted every index (10.11)

  cursor  after=<p7>  β†’ always the item AFTER p7,
          regardless of what arrived above it

  Cursor = base64(rankScore | postId) so it survives
  ranked (non-chronological) feeds too.

How it works

  1. Cursor pagination everywhere; offsets are unusable on a feed that receives inserts.
  2. Normalized store so one post.liked event updates the post in the timeline, the detail view, and the profile at once.
  3. Buffer live inserts behind a pill; auto-insert only when the user is genuinely at the top.
  4. Virtualize past a few hundred items (10.7), and preserve scroll position on back-navigation.
  5. Optimistic like and repost with rollback; the counter is derived, never separately stored.

Trade-offs

ChoiceCost
Buffered insertsFeed is briefly stale by design
Ranked (non-chrono) feedCursor must encode the rank score
VirtualizationLoses native in-page find, complicates SEO
SSE for updatesOne-way only β€” compose still uses REST (2.5)

Interview angle

"New posts arrive while the user is reading. What do you do?"

  • Buffer them and show a count pill; only insert automatically when scroll is at the very top.
  • Use cursor pagination so those inserts don't cause duplicates on the next page fetch.
  • Patch by id into a normalized store, so a like on one post doesn't re-render the entire list (10.5).

Recap

  • Cursor pagination is mandatory the moment items can be inserted.
  • Never move content under someone who's reading β€” buffer and offer.
  • Normalize, so one event updates every view of that entity.