Lesson 24 of 26
Worked Scenario: Design a Social Media Feed
A full worked answer to a classic interview prompt — rendering strategy, an infinite-scroll data model, virtualization, and optimistic interactions, with the trade-offs named.
"Design a Twitter-like feed" is one of the most common frontend system design prompts precisely because it touches nearly every stage of this course at once. This lesson walks through a full answer, in the structure from the previous lesson: clarify, translate into constraints, propose with trade-offs named, and call out what's deliberately out of scope.
Clarifying requirements first
Before proposing anything, the questions worth asking out loud: Is this feed viewable while logged out (does SEO matter for at least some of it)? Roughly what scale — thousands or hundreds of millions of daily active users? Does content need to feel real-time (new posts appearing without a refresh), or is "refresh to see new posts" acceptable? For this answer, assume: a public profile's recent posts are viewable logged-out; the main scrolling feed requires login; scale is large enough that infrastructure cost is a real constraint; and new posts don't need to appear automatically mid-scroll, though the "like" count should update in near-real time for posts currently on screen.
Rendering strategy: split by what's actually public
The public, logged-out profile view is exactly the case for SSR or ISR (from this course's first stage): it's indexable, benefits from a fast first paint for a visitor with no session yet, and its content — a public figure's recent posts — changes frequently enough that pure SSG would require constant rebuilds, but infrequently enough that ISR with a short revalidation window (or on-demand revalidation triggered by a new post) is a good fit. The logged-in, continuously-scrolling main feed is naturally CSR after that initial shell: personalized, per-user, and driven by ongoing interaction (scrolling, liking) that doesn't benefit from being crawlable in the first place.
The data model: normalized, for a specific reason
posts.byId
Each post stored once, keyed by ID, referencing an authorId rather than embedding a full author object.
users.byId
Each user (author) stored once. A display-name change or new avatar updates every post that references them, instantly.
feed.pageIds
An ordered list of post IDs representing the current feed's scroll position — the 'view' layer, referencing the normalized entities above rather than duplicating them.
This matters concretely here because the same post can legitimately appear
in more than one place at once — a user's own timeline, a search result, a
repost surfaced in someone else's feed — and the same author appears on
every post they've written. Without normalization, updating a like count
or an edited caption would mean finding every denormalized copy across
however many arrays currently hold it; with it, one write to
posts.byId[postId] is reflected everywhere that post is currently
rendered.
Rendering the list: virtualization, non-negotiably
A feed is an unbounded, user-generated list by nature — exactly the shape that needs virtualization (covered in the performance stage) rather than rendering every fetched post into the DOM. Given posts vary in height (text length, presence of an image, a quoted repost), this is the harder, variable-height virtualization case: items are measured as they render, with an estimated height correcting once the real size is known, rather than the simpler fixed-height arithmetic case.
Interactions: optimistic, with a real rollback path
A like button needs to feel instant. That's the optimistic-update pattern from the state management stage: flip the icon and increment the count immediately on tap, fire the mutation, and — this is the part that's easy to skip under interview time pressure but shouldn't be — define what happens on failure. If the server rejects the like (rate-limited, the post was deleted, a permissions check failed), the UI reverts the count and icon and surfaces a brief, non-disruptive error, rather than leaving a like showing that never actually took effect on the server.
What's explicitly out of scope, and why saying so is a strength
A strong answer names what it isn't solving, rather than silently ignoring it: real-time delivery of brand-new posts appearing mid-scroll (a WebSocket/SSE concern, and one that trades complexity for a requirement that wasn't stated as necessary here); full offline support; and feed ranking algorithm design, which is a backend/ML problem, not a frontend rendering one. Calling these out explicitly is stronger than pretending they don't exist.
What to remember
- Splitting rendering strategy by which part of a feature is actually public and indexable versus personalized and interactive is usually stronger than one strategy for the whole feature.
- A feed's data model benefits from normalization because the same post and author can legitimately appear in multiple places at once.
- Variable-height virtualization is close to mandatory for an unbounded, user-generated list with posts of differing heights.
- Optimistic updates make interactions feel instant, but only responsibly if paired with an explicit rollback path for the failure case.
Check yourself
3 questions · pass 3/3 to unlock Worked Scenario: Design an Image-Heavy E-Commerce PLP
1.For the initial page load of a public, logged-out view of a feed (e.g. a profile page's recent posts, viewable without an account), which rendering strategy fits best, and why?
2.Why should the client-side data store for a feed normalize posts and authors (as covered in the state management stage) rather than storing each page of results as a self-contained, denormalized array?
3.A 'like' button on each post in the feed should feel instantaneous when tapped. Which two concepts from earlier in this course combine to deliver that, and what's the risk being accepted?
3 left to answer