SysDesignPrep.com
Study guide 144 of 183

Offline-first apps and sync

Designing apps that work without a connection and sync later: local-first storage, change logs and sync cursors, conflict resolution with last-writer-wins, merges and CRDTs, operational transformation, and keeping clients and server consistent.

Reading is half of it. See this used in a real interview: walk through Design Google Docs →

Phones go through tunnels, laptops go on planes, and users expect notes, documents, messages and to-do lists to keep working and to merge correctly afterwards. Offline-first design is replication with the hardest possible replicas: devices that disappear for days and edit the same data independently. It comes up in questions about collaborative editors, file sync, chat and mobile apps.

Local first

The app reads and writes a local database (SQLite, IndexedDB, Realm) and treats the server as a sync partner, not as the source of every read:

  • The UI updates instantly from local data; no spinner for every action.
  • Writes are recorded locally with a pending flag and sent when a connection exists.
  • The server is still the authority for permissions, validation and anything shared.

Syncing changes

The usual building blocks:

  • Client-generated ids (UUIDv7 or similar) so items created offline have ids that will not collide. See unique ids, ordering and time.
  • An outbox of local changes, sent in order with idempotency keys so resending after a dropped connection is safe.
  • A server change log with a monotonically increasing sequence per user or workspace; the client stores a sync cursor (the last sequence it has seen) and asks "what changed since 18342?" on reconnect.
  • Pagination and batching on catch-up, so a device returning after a month does not download everything in one request. See pagination.
  • Tombstones for deletions, kept long enough that offline devices learn about them.

Conflicts

Two devices edit the same thing while apart. Options, from simplest to most capable:

ApproachHow it resolvesGood forWeakness
Last-writer-wins (per record)the later timestamp winssettings, profile fieldssilently drops the other edit; depends on clocks
Last-writer-wins per fieldmerge records field by fieldforms, contactsstill drops concurrent edits to the same field
Server rejects and the user resolvesshow both versionsfiles, important documentsasks the user to do the work ("conflicted copy")
Domain merge rulesunion of sets, max of counters, append listscarts, tags, logsneeds rules per data type
CRDTsdata types that merge automatically and deterministicallytext, lists, counters, collaborative statemetadata overhead, harder to reason about
Operational transformation (OT)a central server transforms concurrent operationsreal-time text editorsneeds a server in the loop; complex

Choose per data type. Most apps combine per-field last-writer-wins for simple fields, merge rules for collections, and something richer only for text. Use a hybrid logical clock or server-assigned versions rather than device wall clocks, which can be wrong by minutes.

CRDTs and OT for collaborative text

CRDTs (conflict-free replicated data types) give each character or element a unique, ordered identity so that applying the same set of operations in any order yields the same result. They work peer-to-peer and offline, which makes them the natural fit for local-first editors (Yjs, Automerge).

OT transforms each operation against concurrent ones on a central server; Google Docs historically used it. It suits always-online collaboration with a server, and is harder to apply offline. See Design Google Docs.

Files

Large files sync in chunks (content-defined chunks, hashed), so a small edit uploads only the changed chunks and identical chunks are deduplicated. Conflicting edits to binary files usually produce a "conflicted copy" rather than a merge. See Design Dropbox.

Permissions and validation while offline

The client cannot be trusted to decide what it is allowed to do. The server validates every synced change, may reject some (permission revoked while offline, invalid state), and the client must handle rejections gracefully by rolling back the local change and telling the user.

Real-time on top

When online, a WebSocket or SSE connection pushes new changes immediately, and the same change log handles catch-up after disconnects, so there is one sync path rather than two. See real-time systems.

Pitfalls

  • Relying on device clocks to order edits.
  • Forgetting deletions (no tombstones) and resurrecting deleted items from an old device.
  • Unbounded local storage growth; prune what the user no longer needs.
  • Schema migrations: old app versions sync with new servers for months, so the sync protocol must be versioned.

Checklist

  • Local database as the primary read and write path.
  • Client-generated ids, an outbox with idempotency keys, a server change log with cursors.
  • Conflict strategy chosen per data type; no reliance on device clocks.
  • CRDTs or OT only for genuinely collaborative state.
  • Server-side validation and handled rejections.
  • Tombstones, chunked file sync, and a versioned sync protocol.

Open in your browser to sign in

Google does not allow sign-in inside this app's built-in browser. Open this page in Safari and sign in there. The link opens this same page.

Tap the ⋯ or share button at the top or bottom of the screen, then Open in browser. Or copy the link and paste it into Safari.