In a system design interview, "How do you handle the feed?" is a guaranteed question for any content-heavy application. This cheatsheet outlines the strategies and trade-offs you should discuss to demonstrate your understanding of mobile-specific constraints.
Your Goal: Show that you can choose the right architecture for the specific user experience (Infinite Scroll vs. Page Navigation) while considering network, battery, and memory usage.
The most common mistake candidates make is jumping straight to a solution (e.g., "I'll use cursors!") without defining the problem.
Why is this important? The user experience (UX) dictates the underlying engineering architecture. You cannot choose the right pagination strategy without knowing how the user interacts with the data.
- Infinite Scroll implies Dynamic Data. Users want to see new content without duplicates.
- Static Pages implies Random Access. Users want to jump to specific records (e.g., "Page 10 of Order History").
How it guides the solution:
- If you choose Offset for a Feed, users will see duplicate items when new content is posted (Page Drift).
- If you choose Cursor for a static list, users can't jump to specific pages, breaking expected navigation patterns.
Recommendation: Cursor-Based Pagination
- The "Why": Users endlessly scroll down. Content is dynamic (items are created constantly).
- The Problem it Solves: "Page Drift".
- Scenario: User loads Page 1 (Items 1-10). While reading, 5 new items are added to the stream. User requests Page 2.
- Offset Failure: Page 2 would return Items 6-15. Items 6-10 are duplicates of what the user already saw!
- Cursor Success: The cursor points to "Item 10". The server asks "Give me 10 items after Item 10". No duplicates, no missed items.
- Mobile Benefit: Smoother UX, no janky list updates to deduplicate items.
- The Signal: Demonstrates an understanding of data integrity (no duplicates) and a prioritization of user experience over simpler implementations. Shows an understanding of the eventual consistency of distributed systems.
Recommendation: Offset/Page-Number Pagination
- The "Why": User wants to jump to a specific record or date. "Go to Page 5". Data is relatively static.
- The Mobile Benefit: Familiar navigation patterns (Previous/Next buttons). Easier implementation.
- The Signal: Demonstrates a pragmatic approach that avoids over-engineering for simple problems.
Recommendation: Cursor-Based (Two-way)
- The "Why": Users jump to a specific message (e.g., from Search or Push Notification) and need to scroll up to see history and down to see newer messages.
- The Problem: Standard "Endless Scroll" only appends to the bottom. Chat requires prepending and appending.
- The Signal: Identifying this unique constraint distinguishes senior mobile engineers. It requires managing two cursors (
before_idandafter_id) and maintaining scroll position stability during prepend operations.
Show the interviewer you know how to design a clean, robust API contract.
Avoid simply stating that a GET request is sent. Do specify parameters and explain default safety mechanisms.
GET /v1/feed?cursor=dXNlcjpVMEc5V0ZXTjI=&limit=20cursor: The opaque token from the previous response.limit: Crucial Signal: Mention that you would enforce a server-side max limit (e.g., 100) to prevent a malicious client from crashing the server with excessive data requests.
Avoid returning a raw array. Do wrap data in an envelope with metadata.
{
"data": [ ... ], // List of item objects
"pagination": {
"next_cursor": "dXNlcjpVMEc5V0ZXTjI=", // Opaque string
"has_more": true // Helps the client UI know whether to show a spinner
}
}You might suggest including full navigation links (HATEOAS style) instead of just the cursor.
"links": {
"next": "https://api.example.com/v1/feed?cursor=dXNlcjpVMEc5V0ZXTjI=",
"self": "https://api.example.com/v1/feed?cursor=..."
}- Pros: The client doesn't need to know how to construct the next URL (loose coupling). If you change the parameter name from
cursortotoken, the client app doesn't break. - Cons: Increases payload size (string bloat).
- Verdict: Mention it as a "pure REST" option, but acknowledge that for mobile, sending just the lightweight cursor is often preferred to save bandwidth.
Interview Tip: Explicitly mention "Opaque Cursors".
- Interviewer: "What's inside that string?"
- You: "It's a Base64 encoded string containing the ID/Timestamp of the last item. We encode it so clients don't try to perform math on it (like
id + 1), allowing us to change the backend logic (e.g., switching from Integer IDs to UUIDs) without breaking the mobile app."
This is where you differentiate yourself from a backend engineer by focusing on client-side resource constraints and UX patterns.
- The Signal: Demonstrates a proactive approach to loading content before the user reaches the end of the list.
- Strategy: Trigger the next page load when the user is X items (e.g., 5-10) away from the end.
- Trade-off: Too aggressive = wasted data (user might stop scrolling). Too lazy = user sees a spinner.
- Problem: Request for Page 2 fails.
- Solution: Show a "Tap to Retry" footer. Do not automatically retry indefinitely (battery drain).
- The Signal: Demonstrates consideration for battery life and the graceful handling of failure states.
- Problem: Infinite scrolling creates an infinitely growing list in memory.
- Solution:
- View Recycling: Explain the pattern of reusing UI components. As a user scrolls, views that leave the screen are "recycled" and re-bound with new data for entering items. This ensures the memory footprint corresponds to the screen size, not the total list size.
- Diffing: Discuss using background threads to calculate the difference between the old and new list states (Insertions, Deletions, Moves). This allows the UI thread to animate only the specific changes, preventing jank.
- Large Data Sets: For massive lists (10k+ items), discuss "Windowing" - dropping off-screen pages from the backing data structure to prevent Out-Of-Memory (OOM) errors.
- Problem: How to indicate loading state for the initial page vs. subsequent pages.
- Solution:
- Initial Load: Use Shimmer/Skeleton placeholders that mimic the cell layout. This reduces perceived latency compared to a generic spinner.
- Pagination: Use a subtle Spinner/Footer at the bottom of the list.
- The Signal: You care about Perceived Performance and UI polish.
- Question: "What happens if the user backgrounds the app, the OS kills the process to save memory, and the user returns?"
- Answer:
- The app must restore the exact scroll position and the data that was visible.
- Strategy: Persist the current
cursorand thelist_state(scroll offset) to the system's saved state bundle. Re-fetch the data using the cursor or load from local persistence.
- The Signal: This is a deep mobile-specific cut that backend engineers will miss.
- Question: "How does pagination work offline?"
- Answer:
- Persist the first X pages (e.g., 50 items) in a local database.
- When offline, serve from the database.
- When online, fetch fresh data, update the database, and notify the UI (Single Source of Truth pattern).
- The Signal: Demonstrates an understanding of "Offline-First" architecture and the creation of resilient applications.
An advanced signal is discussing the abstraction of pagination logic through client libraries, often referred to as "Automatic Pagination."
- The Concept: Instead of the developer manually extracting the "next page token" and firing a new request, the client library treats the paginated resource as an asynchronous stream or iterator.
- Reducing Boilerplate:
- Token Management: The library automatically captures the
next_page_tokenfrom a response and populates thepage_tokenin the subsequent request. - Abstraction: The developer simply iterates over the results (e.g., using a
forloop or a stream observer); the library handles the network calls in the background.
- Token Management: The library automatically captures the
- Resource Management:
- Lazy Resolution: High-quality implementations are "lazy" - they only fetch the next page when the consumer reaches the end of the current buffer, preventing the "greedy" loading of massive datasets.
- The Signal: Demonstrates familiarity with high-level architectural patterns (AIP-4233) that reduce human error and improve code maintainability by hiding low-level network plumbing.
- "I'll just return all the data." -> Immediate fail. Shows lack of scalability awareness.
- "I'll use
pageparameters for the real-time feed." -> Shows lack of awareness regarding real-time data shifts (Page Drift). - "I'll sort by Client Timestamp." -> Never trust the client clock. Always use Server Time.
- Ignoring Empty States: Always plan for the
{ "data": [] }case.
- Scraping: Pagination makes it easy to scrape your entire database. Mention Rate Limiting (e.g., "Max 60 requests/minute").
- Broken Access Control: Ensure the user has permission to see every item in the page, not just the first one.
- Predictable IDs: If you use simple integers (
/users?after_id=50), competitors can guess your growth rate. Opaque cursors hide this. - The Signal: Demonstrates awareness of production risks and the importance of protecting business assets.
- Problem: High-velocity writes cause massive "Page Drift" in offset systems.
- Solution: Cursor-based (
next_token).
// Request
GET /2/tweets/search/recent?query=system+design&max_results=10&next_token=b26v89c19zqg8o3fo7ggj03...
// Response
{
"data": [ ... ],
"meta": {
"result_count": 10,
"next_token": "b26v89c19zqg8o3fo7ggj03..."
}
}- Design Pattern: Metadata envelope (
meta) separates stream state from content, allowing for stable "anchors" in a constantly changing timeline. - Reference: Twitter API Pagination
- Problem: Scanning huge offsets (
OFFSET 50000) is slow (O(N)). - Solution: Cursor-based.
// Request
GET /conversations.history?channel=C012345&cursor=dTx0...
// Response
{
"ok": true,
"messages": [ ... ],
"has_more": true,
"response_metadata": {
"next_cursor": "bmV4dF9tZXNzYWdl..."
}
}- Design Pattern: Namespaced
response_metadataensures the pagination token is discoverable even when themessagesarray is empty. - Reference: Slack API: conversations.history
- Problem: Financial reconciliation requires zero missed items.
- Solution: Strict cursors (
starting_after,ending_before).
// Request
GET /v1/charges?limit=10&starting_after=ch_12345
// Response
{
"object": "list",
"data": [
{ "id": "ch_12346", ... },
{ "id": "ch_12347", ... }
],
"has_more": true,
"url": "/v1/charges"
}- Design Pattern: Uniform
object: liststructure allows client libraries to polymorphically automate pagination across all API resources. - Reference: Stripe API Pagination
- Problem: Content rank (votes) changes constantly during a session.
- Solution: Fullname IDs as cursors.
// Request
GET /r/all/hot.json?limit=25&after=t3_15bfi0
// Response
{
"kind": "Listing",
"data": {
"children": [ ... ],
"after": "t3_16cfj1",
"before": null
}
}- Design Pattern: Standardized "Listing" wrapper ensures consistent cursor placement (
after/before) for every dynamic feed in the app. - Reference: Reddit API Documentation
- Clarify the Use Case: Is it infinite scroll (Feed), static navigation (History), or bi-directional (Chat)?
- Choose the Strategy: Cursor (Feed) vs. Offset (Static).
- Define the API: Request params (cursor, limit) and Response structure (envelope, metadata).
- Add Mobile Context: Prefetching, Error Handling, Offline Caching, and Process Death.
- Defend against Edge Cases: Rate limiting, Empty states, Malformed inputs.
- Slack Engineering: Evolving API Pagination at Slack - A classic case study on why a major tech company migrated from offset to cursor-based pagination.
- Twitter API Docs: Pagination in the Twitter API - See how one of the world's largest feeds handles pagination in production.
- Stripe API Reference: Pagination - Widely considered the "Gold Standard" for developer-friendly cursor pagination.
- Google AIP-158: Pagination - Detailed design patterns for standardizing list methods.
- Google AIP-4233: Automatic Pagination - Standards for how client libraries should abstract pagination logic.
- Nielsen Norman Group: Infinite Scrolling vs. Pagination - The definitive UX research on when to use which pattern.