Offline-first and sync

How are sync conflicts resolved?

A sync conflict happens when two copies of the same data change independently before they sync. Systems resolve it with a rule chosen in advance. They keep the latest write, keep both versions for a person to choose, merge the changes automatically with a data type designed for it, or send the decision to an authority such as a server.

Learning objectives

After reading this article you will be able to:

  • Explain when two changes count as concurrent and therefore conflict
  • Compare last writer wins, keeping both versions, automatic merging and an authority
  • Choose a conflict rule for each kind of data in an app

When a conflict happens

Two changes conflict when they touch the same data and neither device knew about the other’s change when it made its own. Computer scientists call these changes concurrent. It does not mean they happened at the same moment: a technician might edit a job on Monday in a basement and a dispatcher might edit it on Tuesday at the office, and if the technician’s phone has not synced yet, the two edits are still concurrent.

Many apparent conflicts are not real. If one person changes a job’s address and another changes its notes, the edits touch different fields and can both be kept. How finely the data is split decides how often true conflicts occur.

Last writer wins

The simplest rule keeps whichever change is newest and discards the other. It is predictable and easy to build, and it is fine for values where only the latest matters, such as a status or a setting.

It has two weaknesses. It loses data silently: the earlier edit is gone, and nobody is told. And “newest” depends on clocks, which drift on devices that have been offline. Systems that use last writer wins often order changes with logical clocks, building on Lamport’s work, plus a fixed tie-breaker such as a device identifier, so that every replica picks the same winner even when wall clocks disagree.

Applying it per field rather than per record makes it far less destructive, because two edits to different fields of the same record no longer collide.

Keep both and let someone choose

Another approach keeps every conflicting version and asks a person, or the application, to decide. Apache CouchDB works this way: it picks a winning revision with a deterministic algorithm, so every replica shows the same one, but keeps the losing revisions so the application can find and resolve them.

This never loses data, which matters for records like inspection findings or medical notes. The cost is that someone has to resolve conflicts, and an app that surfaces too many of them becomes tiring to use.

Merge automatically

Some data can be merged without choosing a winner at all, if it is stored in a structure built for merging. Conflict-free replicated data types (CRDTs) are designed so that concurrent changes can be combined in any order and always produce the same result.

  • Counters add up increments from every device, so two people each adding three items gives six, not three.
  • Sets can keep every item that was added, with clear rules for items one device added and another removed.
  • Lists and text keep insertions from both sides in a consistent order, so two people typing in the same document both keep their words.

Collaborative editors take a related route called operational transformation, which adjusts each change against the others it crossed with, usually with a server keeping the order.

Let an authority decide

Some conflicts should not be merged automatically. Two people booking the last seat, or two technicians taking the last spare part, will merge cleanly into a state that breaks a business rule. These need an authority, usually the backend, that checks the rule when it receives the changes and rejects one of them. The app then has to tell the person whose action was rejected and offer a way forward.

Choose a rule per kind of data

There is rarely one right answer for a whole app. A good design picks the rule for each kind of data:

Kind of dataUsually bestWhy
Status, settings, single valuesLast writer wins, per fieldOnly the latest value matters
Counts and talliesCounter mergeEvery device’s increments should count
Notes and shared textText mergeNobody’s words should disappear
Findings, records, evidenceKeep both versionsLosing data is worse than resolving by hand
Stock, bookings, assignmentsAn authority decidesA business rule must hold across everyone

Two habits reduce conflicts whatever the rule. Split records into independent fields instead of storing them as one block, so unrelated edits do not collide. And refer to items by stable identifiers rather than by position, so an insertion on one device does not shift what another device meant.

Sources

Build it with Offline Protocol

In Offline Protocol's replicated documents, each collection type has a fixed merge rule, from last writer wins per map key to character-level merging for text, so every device reaches the same result. The shared state guide lists them and how to model your data around them.

Read the shared state guide