SECTION 0 — WHY FEEDS ARE HARD
A “feed page” looks like UI work.
In production, it’s a multi-system problem:
-
latency budgets (TTFB + render + interaction)
-
caching and staleness tolerance
-
pagination correctness
-
ranking consistency
-
partial failure handling
SECTION 1 — REQUIREMENTS
Functional
-
Show a personalized list of items.
-
Support infinite scroll.
-
Allow filtering/sorting.
-
Clicking an item opens a detail view quickly.
Non-functional
-
Performance: fast first content, smooth scrolling.
-
Correctness: no duplicates/missing items across pages.
-
Reliability: degrade gracefully if personalization service fails.
-
Cost: avoid per-request expensive ranking.
SECTION 2 — INVARIANTS
-
Pagination must be deterministic for a given cursor.
-
A page fetch must be idempotent (retries don’t create duplicates in UI state).
-
UI must represent “staleness” explicitly when applicable.
SECTION 3 — RENDERING STRATEGY: SSR / ISR / CSR
Decision inputs:
-
personalization strength
-
SEO needs
-
freshness requirements
-
backend latency
Practical default (senior-friendly)
-
SSR for shell + first page when you need fast first content and SEO.
-
CSR for subsequent pages (infinite scroll).
-
ISR only when feed is mostly cacheable (e.g., “trending”, not deeply personalized).
Senior rule:
Split the page: render what’s stable, fetch what’s personal.
SECTION 4 — API CONTRACT (CURSOR-BASED PAGINATION)
GET /feed?limit=25&cursor=...&filter=...
Response:
-
items: FeedItem[] -
nextCursor: string | null -
generatedAt: ISO timestamp -
stale: boolean(optional)
Cursor design:
-
opaque to clients
-
encodes sort key + tie-breaker (e.g.,
(score, createdAt, id)) -
includes filter hash to prevent mismatched cursors
Avoid offset pagination for large feeds.
SECTION 5 — CACHING STRATEGY (HIERARCHY)
Cache layers:
-
Browser cache (short, for assets)
-
CDN (only for cacheable variants like “trending”)
-
App cache (per-user feed segments if feasible)
-
DB / search index (precomputed or query-optimized)
Common senior pattern:
-
Cache ranked ID lists (cheap to serve)
-
Fetch item details by IDs (batched)
This reduces cost and improves tail latency.
SECTION 6 — STALENESS + CONSISTENCY
Consistency options:
-
Snapshot feed: user sees a consistent list for a time window.
-
Live feed: list changes as new items arrive.
Senior default:
-
Snapshot for pagination correctness.
-
Provide “new posts available” banner to refresh snapshot.
Mechanism:
-
server issues a
feedSessionIdor includesgeneratedAt -
cursor ties to that snapshot
SECTION 7 — FRONTEND STATE MACHINE
IDLE
-> LOADING_FIRST_PAGE
-> READY
-> LOADING_NEXT_PAGE
-> READY
-> ERROR (retry)
READY
-> REFRESHING (new snapshot)
Senior UX rules:
-
De-dupe items by stable key.
-
Keep scroll position stable.
-
Use skeletons for first load, inline spinners for next page.
SECTION 8 — FAILURE MODES
-
Ranking service slow → fallback to recency feed
-
Partial item details missing → render placeholders for those cards
-
Cursor invalid → reset and show “feed updated, reloading”
-
Cache stampede on trending → request coalescing + jitter
SECTION 9 — OBSERVABILITY
End-to-end metrics:
-
TTFB p50/p95
-
INP / interaction latency
-
feed API latency + error rate
-
cache hit rate for ranked lists
-
duplicates detected (should be near zero)
SECTION 10 — EXERCISES
-
Pick SSR vs CSR vs ISR for your feed and justify with constraints.
-
Design an opaque cursor payload (fields + tie-breaker).
-
Define staleness semantics and what UI communicates.
-
Propose a caching plan that reduces ranking cost.