Worked example: design a feed
Lesson 3 of 41 min read
Pagination, caching, images, pull-to-refresh and the "new posts" problem.
Apply the five moves to the most common prompt there is.
Scope
Text and image posts, like and comment counts, infinite scroll, pull to refresh, works offline on open. Out of scope: ranking, video, stories.
Data
Post(id, authorId, text, imageUrl, likeCount, commentCount, createdAt, fetchedAt)
FeedPage(cursor, nextCursor, postIds)
Pagination: cursor, not page number
Offset pagination duplicates and skips items when new posts arrive while the user scrolls. A cursor ("give me 20 after this id") is stable. Say this unprompted — it is a strong signal.
Caching
- Posts and the page order are stored in Room; the UI collects a Flow of the ordered list.
- Pull to refresh replaces the first page and keeps the rest, so the user does not lose their scroll position.
- Trim the cache — keep the most recent few hundred posts, not everything ever fetched.
Images
Memory LRU for decoded bitmaps, disk cache for bytes, decode at display size, prefetch one screen ahead, cancel on recycle, and a stable placeholder so the layout never jumps.
The "new posts" problem
Inserting new posts at the top while someone is reading is hostile. Fetch them, store them, and show a "12 new posts" pill. The user decides when the list jumps. Interviewers notice product judgement here as much as engineering.
Counts that change
Likes are optimistic: update the local count immediately, send the request, roll back on failure. Do not re-fetch the whole post for one count.
What to measure
- Time to first content on a cold, offline open.
- Scroll jank — frames over budget per 1000 frames.
- Page load failure rate, and how often retry succeeds.
- Bytes per feed session, which is the number that matters on a metered connection.
