System Design Cases
Infinite Scroll & Feed UI
Infinite scroll & feed UI concept: cursor-based pagination vs offset, virtualization (TanStack Virtual / react-window), IntersectionObserver load-more, scroll restoration, optimistic updates with rollback, WebSocket new-posts pill. Architecture: Browser (viewport, virtualizer, TanStack Query cache, IntersectionObserver, History API) + Network (Image CDN, edge cache with stale-while-revalidate) + Backend (Feed API, ranking service, Posts DB with composite index, WebSocket gateway). 5 scenarios: cursor pagination + IO load-more, virtualization for 10K items, scroll restore on back nav, optimistic like + error retry banner, WS realtime new-posts pill with backpressure. 2 ADRs: cursor vs offset pagination; infinite scroll vs load-more vs numbered pagination (dark-pattern discussion).
Infinite scroll feed: stable cursors, bounded DOM и accessible escape
Infinite scroll — interaction pattern поверх pagination contract. IntersectionObserver асинхронно сообщает пересечение sentinel, но callback может сработать повторно; controller обязан deduplicate in-flight request, проверять generation и сохранять cursor semantics.
Для изменяющегося feed offset pagination может давать duplicates/skips. Stable keyset cursor обычно кодирует sort tuple и query/snapshot scope, остаётся opaque для клиента и проверяется сервером.
Проверяемые утверждения
- IntersectionObserver снижает потребность в synchronous layout polling, но не является точным сигналом пикселей на экране и не гарантирует ровно один callback.
- Cursor — capability-shaped input, не доверие: сервер проверяет signature/scope, authorization, page-size bound и сортировку.
- Virtualization ограничивает DOM, но удаление focused/semantically referenced item может сломать keyboard и assistive-technology navigation.
- Доступный feed предоставляет явный Load more/pagination path, status announcement, reachable content after the feed и restorable URL/position.
Границы и компоненты
| Компонент | Роль в модели |
|---|---|
| Viewport and User Controls | observe boundary; explicit load control; render and focus |
| Intersection Sentinel | observe boundary; intersection signal |
| Generation-aware Feed Controller | explicit load control; intersection signal; cursor request; append deduplicated items; save cursor and anchor; announce load state |
| Authorized Cursor API | cursor request; indexed keyset query |
| Indexed Ranked Store | indexed keyset query |
| Focus-safe Virtual Window | render and focus; append deduplicated items |
| URL and History State | save cursor and anchor |
| Load Status and End Marker | announce load state |
Сценарии
Load the next cursor page
A sentinel or explicit button requests one page using the current opaque cursor and filter generation. The API validates scope and reads k rows after the stable sort tuple.
Проверяемый исход: Items are deduplicated by stable ID, cursor advances only from the accepted response, and end-of-feed is explicit.
Suppress duplicate and stale fetches
Observer fires repeatedly while a request is running, then filters change. The controller coalesces the first generation and discards a late result that does not match the active query generation.
Проверяемый исход: Repeated callbacks do not amplify RPS, and old data cannot append into the new filter results.
Restore navigation position and content
Before opening an item, the controller records route/filter, loaded cursor frontier and stable anchor ID. Back navigation reconstructs enough pages, then restores focus/scroll relative to the anchor.
Проверяемый исход: The user returns to the same logical item despite virtualization or inserted feed items; a missing anchor has a defined fallback.
Offer an accessible Load more path
Keyboard and assistive-technology users can activate a real Load more control, receive loading/result status and move past the feed to following content without an endless focus trap.
Проверяемый исход: The same cursor API supports automatic and explicit loading; focus remains stable and the terminal state is announced.
Failure, concurrency и evolution checklist
- Abort obsolete network work when possible and still compare response generation/cursor before commit.
- Track retry state per cursor. Automatic tight retry at the sentinel can create a request loop while the error remains visible.
- Dedupe by stable item ID and define ranking snapshot semantics; silently mixing pages from different ranking epochs causes gaps or reorder.
- Keep focused item and accessible context mounted, or deliberately move focus before virtualizing it away.
Security и privacy
- Treat cursors as untrusted: validate authenticity/expiry/query scope and never skip object-level authorization for returned items.
- Cap page size, cursor length and request frequency; prevent observer loops and prefetch from becoming resource exhaustion.
- Avoid putting personal ranking features or raw database keys into readable cursor payloads when disclosure matters.
Метрики, формулы и допущения
- With a matching B-tree index, keyset page work is approximately
O(log N + k)for seek pluskrows; this bound depends on sort/filter/index compatibility. rendered_dom_nodes = visible_items + overscan_items + focus_retention_items, bounded independently from total items loaded in memory.request_amplification = physical_page_requests / accepted_logical_pages; coalescing should keep normal operation near 1, while retries are reported separately.- Prefetch distance uses units:
lead_seconds ≈ pixels_to_sentinel / scroll_pixels_per_second; compare it with measured p95 page latency, not a magic pixel constant.
Числа и bounds выше действуют только при названных units, population и assumptions. Ни паттерн, ни browser API сами по себе не задают SLA, capacity или correctness.
Решения для production review
- Choose stable ordering and cursor snapshot semantics before implementing the observer.
- Make automatic sentinel and explicit Load more share one idempotent controller/API contract.
- Specify history, focus, end-of-feed and following-content access as product requirements, not cleanup work.
Первичные и официальные источники
- https://www.w3.org/TR/intersection-observer/
- https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver
- https://www.w3.org/WAI/ARIA/apg/patterns/feed/examples/feed/
- https://www.w3.org/TR/WCAG22/
Scope note
Диаграмма показывает mutable feed UI и keyset contract. Она не определяет ranking algorithm, обещание exactly-once delivery, универсальный ARIA feed implementation или постоянную scroll position после удаления anchor item.