Offline & Data· 5 min read

Sync Conflicts in Mobile Apps: Strategies That Don't Lose Data

How to resolve offline sync conflicts in mobile apps: last-write-wins, versions, merges, CRDTs, append-only data, server authority, clocks and testing.

By

Full-Stack, Mobile & AI Engineer

Two phones, one account, no signal on either. On the first, the user renames a workout plan. On the second, they delete it. Both phones come back online. What should the server do?

There's no universally correct answer, which is exactly why offline sync conflict resolution has to be designed rather than discovered in production. This guide covers the strategies that work in mobile apps, how to choose one per type of data, the clock problems that trip teams up, and how to test conflicts before users find them.

Key takeaways

  • Conflicts are a product decision as much as a technical one. Decide per type of data what "correct" means.
  • Avoid conflicts by design: append-only records (events, logs, completed sessions) never conflict.
  • Last write wins is fine for low-stakes fields, especially when applied per field rather than per record.
  • Versions (optimistic concurrency) detect conflicts so you can merge or ask the user instead of silently overwriting.
  • Don't trust device clocks for ordering; use server-assigned versions or sequence numbers.

Why conflicts happen

An offline-first app lets users change data without a connection. A conflict occurs when the same record changes in two places (two devices, or a device and the server) before they sync. Without a rule, the last one to sync silently overwrites the other, and users experience it as "the app lost my changes".

The strategies

StrategyHow it worksBest forRisk
Append-onlyNever edit; add new records (events, sessions)Logs, workouts, messages, transactionsNeeds compaction or projections for "current state"
Last write wins (record)The latest change replaces the whole recordRarely the best choiceLoses unrelated edits to other fields
Last write wins (field)Each field keeps its latest valueProfiles, settings, simple formsCan combine values into an odd state
Optimistic concurrencyEach change carries the version it was based on; mismatches are rejectedImportant records edited in several placesNeeds a resolution step
Merge operationsSync operations ("add item", "increment"), not valuesLists, counters, setsMore design work
CRDTsData structures that merge automatically and consistentlyCollaborative text, shared documentsComplexity and storage overhead
Server authorityThe server decides; devices accept its answerPayments, bookings, inventory, permissionsOffline edits may be rejected
Ask the userShow both versions and let them chooseRare, high-value editsInterrupts users; use sparingly

Design to avoid conflicts

The cheapest conflict is the one that can't happen:

  • Model actions as facts. "Completed a workout at 07:12" is an event, not an editable document. Two devices can both add events without conflicting.
  • Split records so independent fields live in different places. A user's display name and their notification settings don't need to be one row.
  • Keep shared mutable data small, and give it clear ownership (only the owner edits it).

Optimistic concurrency in practice

Give each record a version number that the server increments on every change. Devices send the version their edit was based on:

UPDATE plans
SET name = $1, version = version + 1, updated_at = now()
WHERE id = $2 AND version = $3;   -- $3 is the version the device started from

If the update affects zero rows, someone else changed the record first. The server returns the current version, and the app either merges automatically (if the changes touch different fields) or applies your rule for that data type.

Deletes: use tombstones

If a record is deleted while another device is offline, simply removing the row means that device will happily recreate it on its next sync. Instead, mark the record deleted (a tombstone with a deletion version) and sync that. Decide your "edit vs delete" rule explicitly: for example, a delete wins unless the edit happened after it was synced.

The clock problem

Device clocks are wrong more often than you'd think: set manually, drifting, or in the wrong time zone. Ordering changes by device timestamps leads to "newer" edits losing to older ones.

  • Use server-assigned versions or sequence numbers for ordering.
  • Record device timestamps for display, not for deciding conflicts.
  • If devices must order events among themselves, use logical clocks (such as hybrid logical clocks) rather than wall time.
From the field: For MOVE, a fitness app with 100K+ active users, I built offline-first workout tracking with cache-first sync so sessions persist through network drops with zero data loss. For data like workout sessions, append-only design is the natural fit: a finished session is a fact, and facts don't conflict, which removes the hardest class of sync bugs from the data users care about most.

Choosing rules per data type

A practical table for a typical app:

DataRule
Completed sessions, messages, check-insAppend-only
Profile name, avatar, preferencesLast write wins per field
A shared shopping or packing listMerge operations (add, remove, check)
Notes edited on several devicesOptimistic concurrency, auto-merge fields, otherwise keep both
Bookings, payments, stock levelsServer authority

Testing conflicts

  • Two devices (or simulators) signed in to the same account, both offline, editing the same record differently, then reconnecting in each order.
  • Edit on one device and delete on another.
  • A device with its clock set an hour wrong.
  • A long-offline device with many queued changes against a server that moved on.

Automate the core cases as integration tests against your sync endpoints, because conflict rules are easy to break with an innocent-looking change.

FAQ

What is the best way to resolve sync conflicts in a mobile app?

There isn't one rule for all data. Use append-only records for events, last-write-wins per field for simple settings, optimistic concurrency with merging for important shared records, merge operations for lists, and server authority for payments and bookings. Designing data to avoid conflicts is the most effective strategy.

Is last write wins good enough?

For low-stakes fields like preferences, usually yes, especially applied per field. For records where edits matter, it silently discards one user's changes. Use versions to detect those conflicts and merge or ask instead.

Should I use CRDTs in my React Native app?

Only if you need real-time collaborative editing of the same content, such as shared documents or whiteboards. For most apps, simpler strategies (append-only data, per-field last-write-wins, optimistic concurrency) cover the cases with far less complexity.

Why shouldn't I use device timestamps to order changes?

Device clocks can be wrong, manually changed or drifting, so a newer edit can carry an older timestamp and lose. Use server-assigned versions or sequence numbers for ordering, and keep device timestamps for display.

Next steps

If your app's sync occasionally loses or duplicates data, I can review the rules, design conflict handling per data type and add tests that keep it correct. Get in touch, or see how I work as a React Native developer.

Sources

Bhuwanesh Sisodia

Bhuwanesh Sisodia

Full-Stack, Mobile & AI Engineer

Bhuwanesh Sisodia is a Full-Stack, Mobile & AI Engineer based in Mumbai, with 7+ years of experience and 50+ apps shipped to the App Store and Google Play. He works independently with startups and product teams worldwide.

About meGitHubLinkedInX

Related