advanced 3 min answer

A field application must work fully offline for hours and sync when connectivity returns. How should conflicts, permission changes and schema migrations on the device be handled?

offline-firstsyncconflictspermissionsmigrations
Show the full answer Hide the answer

The starting position

Offline-first means the local store is the application's source of truth for reads and writes, and the server is a peer it reconciles with. Every read is local; every write applies immediately; synchronisation happens when it can.

That gives instant interaction and genuine offline capability, and it creates three problems that do not exist in a request-response application.

Conflicts

  • Field-level rather than record-level resolution, so two people editing different fields of the same record do not conflict at all. The single most valuable design decision here, and it eliminates most conflicts before any resolution logic runs.
  • Operation-based sync for composable changes — increments, list reorderings, set membership, assignments — where sending the operation composes and sending the resulting value does not.
  • Server authority with last-writer-wins for simple values, which is correct for the large majority of fields once the test is applied: does a lost concurrent update cause harm? Usually not.
  • Explicit conflict surfacing for long-form text and for anything where a lost edit is expensive, because silent resolution destroys work — the one case a user must be involved in.
  • A monotonic version per record, so a client can detect that its base was stale rather than overwriting blindly.

Permission changes made while offline, which is the hard case

The device holds a full local copy. If access is revoked while it is offline, the data is still on the device, and the user may have made changes to records they no longer have any right to touch.

  • The server re-authorises every mutation on arrival, never trusting that the client had permission when it made the change. The client's local state is a cache, not an authority — and this is the property that makes the whole model safe.
  • Revocations sent on reconnection, with the client deleting the affected local data.
  • Rejected changes surfaced honestly to the user rather than silently discarded, since the user believed their work was saved.
  • The sync scope itself derived from authorisation, so the delta stream carries only what the user may see — which makes sync a permission-evaluation problem rather than merely a data-transfer one.

Schema migrations on devices you do not control

Local databases persist across application versions, on hardware you cannot reach, with users who skip versions or stay on an old one for months.

  • Versioned local schema with forward migrations, run at startup.
  • A minimum supported version below which the client wipes and re-syncs. This escape hatch must exist, because some migrations are not worth writing for a version six releases old.
  • Tolerate unknown fields, so a newer server's data does not break an older client.
  • Test migrations against real accumulated local state, not a fresh database — the failures live in data that has already been through several previous migrations.
  • Migrations must be resumable and safe to interrupt, since a mobile application can be killed mid-migration and must not be left in an unusable state.

What must be designed before shipping

Unsynced local changes must survive an application update, a device restart and a crash — which means the mutation log is durable rather than in memory. And the user needs an honest indication of sync state: pending changes, last successful sync, and conflicts requiring attention. A silent sync that fails looks identical to one that works, which is how users lose confidence in the application entirely.