Design: Offline-First Feed Flashcards

ADVANCED › System Design

Why is the local database (Room) the single source of truth in an offline-first feed?
The UI reads only from Room and all writes land in Room first. Because Room exposes observable Flows, consumers update automatically when data changes, reads work without a network, and consistency is guaranteed across connection states. Network is just a way to keep the DB fresh.
Describe the network-bound resource flow for loading feed data.
Immediately emit cached data from the DB; decide whether to fetch from the network; on success write the response into the DB, which re-emits to observers; on failure emit the cached data plus an error. The DB remains the source of truth and the UI never reads the network directly.
What is RemoteMediator and how does it relate to PagingSource?
PagingSource pages from the local DB. RemoteMediator is invoked when the DB runs out of cached pages (APPEND/PREPEND) or on REFRESH; it fetches network pages, writes them into the DB in a transaction, and the PagingSource then emits the new rows. It returns MediatorResult.Success(endOfPaginationReached) or Error.
What is a RemoteKeys table and why do you need it?
A separate Room table mapping items/pages to their prev/next page tokens. RemoteMediator reads it to know what to fetch on APPEND/PREPEND. On LoadType.REFRESH you clear items and keys, then insert the first page and its keys atomically in a single transaction.
How does Paging 3 surface loading, empty, and error states?
Via CombinedLoadStates: loadState.refresh, .append, .prepend each are Loading, NotLoading, or Error. Refresh Loading drives a full-screen shimmer; refresh Error with itemCount 0 is a full error+retry; NotLoading with endOfPaginationReached and itemCount 0 is the empty state; append Error shows a retry footer. Call retry() to re-attempt.
Explain last-write-wins conflict resolution and its alternatives.
Each write carries timestamp/version metadata; on sync the authoritative source discards data older than its current state and accepts newer writes. Alternatives: server-authoritative (server always wins), field-level merge, version vectors, or CRDTs for true mergeable state. Last-write-wins is simple but can silently drop concurrent edits.
What are the three write strategies for an offline-first app?
Online-only: write network first, update DB on success, fail if offline (e.g. bank transfer). Queued: enqueue and drain via WorkManager with backoff, best for non-critical writes like analytics. Local-first (lazy): write the DB immediately then sync later, best for likes/to-dos, and it requires conflict resolution.
Why use WorkManager for feed sync and what does it guarantee?
It persists work across process death and reboots, runs under constraints (e.g. NetworkType.CONNECTED), and dedupes via enqueueUniqueWork / unique periodic work. A CoroutineWorker returning Result.retry() gets automatic exponential backoff. It is the durable, OS-managed home for sync rather than an in-process coroutine.
How would you test an offline-first feed?
Use an in-memory Room DB with a fake network. Unit-test RemoteMediator.load() for REFRESH/APPEND, asserting it writes correct rows and RemoteKeys and returns the right endOfPaginationReached. Assert the source-of-truth Flow re-emits after sync (Turbine). Test WorkManager with WorkManagerTestInitHelper/TestDriver, plus conflict resolution and LoadState transitions.

Back to Design: Offline-First Feed