Two people edit the same record while offline, both changes sync when connectivity returns, and one edit gets overwritten. That silent loss, rather than a crash or obvious error, is one of the key risks of poorly designed data synchronization.
Mobile apps that support offline use need to reconcile changes made locally with the latest server data when connectivity returns. When multiple devices modify the same record before synchronization, the app needs a defined conflict-resolution strategy to determine whether changes should be merged, rejected, or overwritten. This guide explains how mobile app data synchronization works, when offline-first architecture makes sense, and how different conflict-resolution approaches affect data consistency.
Mobile app data synchronization is the process of keeping local data on a device consistent with a server and with a user’s other devices, so changes made in one place eventually appear everywhere else. A user edits a record on their phone while offline, and synchronization is what gets that edit onto the server and onto their tablet once connectivity returns, without the user manually re-entering anything.
Offline-first architecture is worth the engineering cost only when your users genuinely work in low-connectivity conditions or when losing access during a network drop would meaningfully hurt the product experience.
Offline-first is worth it when:
Offline-first is usually not worth it when:
Offline-first architecture adds real, ongoing engineering cost: a local database, a sync engine, conflict handling, and testing for edge cases that online-only apps never encounter. Treating it as a default best practice rather than a deliberate tradeoff is a common, expensive mistake. For a deeper look at the architecture itself once you’ve decided it’s warranted, see our guide to offline-first app architecture.
An offline-first app treats its local database, commonly SQLite or Realm, as the immediate source of truth for reads and writes, rather than waiting on a network round-trip for every action. A write happens locally first, instantly, and syncs to the server as a separate, asynchronous step.
The sync engine is the component that detects local changes, queues them, and pushes them to the server once connectivity is available, typically triggered by a network callback that fires when the device reconnects. Each local write gets flagged as unsynced until the server confirms receipt, so the app always knows exactly which changes still need to go out.
Delta sync transmits only the fields or records that changed since the last successful sync, instead of re-sending an entire dataset every time. This keeps sync fast and bandwidth-efficient, especially important on the same unreliable or metered connections that make offline-first architecture necessary in the first place.
A sync conflict occurs when the same record gets edited on two different devices while both are offline, and both edits then attempt to sync to the server. The server receives two different versions of the same record with no inherent way to know which one the user actually wants to keep, or whether both edits should somehow both survive.
This is different from a typical error case, since nothing actually fails. Both edits reach the server successfully as far as the network is concerned, the conflict is a data problem, not a connectivity problem, which is exactly why it’s easy to miss during testing. A team testing on a single device with a stable connection will never see this failure mode, since it only appears once real users start editing the same records from multiple devices or in genuinely offline conditions, often well after launch.
Last-write-wins resolves a sync conflict by keeping whichever edit has the most recent timestamp and silently discarding the other, without merging any content from the losing version.
This is the part most competitor content glosses over: last-write-wins does not merge changes. If two people edit different fields on the same customer record while offline, one updates the phone number, the other updates the address, most last-write-wins systems still treat this as one conflict at the record level, not two separate field-level changes, and the entire losing record gets overwritten, address update included, with no warning to either user that their edit vanished.
Concretely, imagine a warehouse inventory app where two staff members both adjust the same product’s stock count while offline, one after a shipment arrives, one after a damaged-item writeoff. Both edits are legitimate and both should apply. Last-write-wins keeps whichever adjustment synced most recently and silently discards the other, leaving the inventory count wrong in a way nobody notices until a physical count doesn’t match the system, often weeks later when tracing the error back to its cause is far harder than catching it at the moment of the conflict itself.
When last-write-wins is acceptable:
When last-write-wins is dangerous:
Presenting last-write-wins as a universal default, the way most competitor guides do, ignores that its actual behavior is closer to a coin flip with financial consequences than a real conflict resolution strategy.
CRDTs and logical clocks solve the conflict differently than last-write-wins, by making conflicting edits mergeable or correctly orderable instead of forcing one to overwrite the other.
A CRDT, conflict-free replicated data type, is a data structure specifically designed so that when two versions get merged, the result combines both sets of changes automatically instead of picking one and discarding the other. Where last-write-wins asks “which edit happened last,” a CRDT asks “how do both edits combine into one correct result,” which is a fundamentally different question with a fundamentally different outcome, no data silently disappears.
A logical clock, commonly implemented as a Lamport timestamp, orders events based on cause and effect rather than raw device clock time, which matters because two devices’ clocks are rarely perfectly synchronized and a few seconds of drift can make an earlier edit look like it happened later. Raw timestamp comparison can pick the wrong “winner” simply because one device’s clock was slightly ahead, while a logical clock tracks the actual sequence of related events regardless of what each device’s local clock says, producing a more reliable ordering when conflict resolution does need to fall back to a winner-based approach.
The right strategy depends on what kind of data is actually at risk in a conflict, not a single default applied everywhere.
Most real apps use more than one strategy across different data types rather than picking a single approach for the entire application.
Local storage on mobile commonly uses SQLite for straightforward relational data or Realm for an object-oriented local database with built-in sync capabilities. Sync itself typically runs over REST or GraphQL APIs, with background sync triggered through platform-specific network callbacks that fire when connectivity returns, letting queued local changes push to the server without requiring the user to manually refresh anything.
Optimistic UI updates, showing a change as successful immediately in the interface before server confirmation arrives, are standard practice alongside this, keeping the app feeling responsive even though the actual sync happens asynchronously in the background. This pairing matters for perceived performance specifically, a user who taps save and sees an immediate confirmation experiences the app as fast, even when the underlying sync to the server takes several seconds or waits for connectivity to return. Getting this wrong, blocking the UI until server confirmation arrives, is a common reason offline-first apps feel sluggish even when the underlying sync architecture is sound.
A sync strategy that silently discards user data under the wrong conditions is a bug waiting to be discovered by an angry customer, not a technical detail to gloss over. If you’re deciding how your app should handle offline use and conflict resolution, our mobile app development team can help you match the strategy to what your data actually requires. Get in touch to talk through your specific situation.
A sync conflict happens when the same record is edited on two different devices while both are offline, and both versions attempt to sync once connectivity returns, leaving the system with two different versions of the same data and no inherent way to know which change the user actually wants kept.
It depends entirely on the data type. It's safe for low-stakes, single-field data like a display preference, but risky for financial, inventory, or collaborative data, since it silently discards one edit entirely rather than merging changes.
Yes, meaningfully, offline-first architecture adds a local database, a sync engine, and conflict handling logic that an online-only app doesn't need, which is why it's worth the investment only when users genuinely face unreliable connectivity or losing access during a network drop would hurt the product.
Last-write-wins picks one version and discards the other entirely. A CRDT is a data structure designed to merge both conflicting versions into one correct combined result, so no edit gets silently lost in the process.
Record-level resolution treats any conflict on a record as one conflict, even if the two edits touch entirely different fields, which means one edit can overwrite the other unnecessarily. Field-level resolution merges non-conflicting field changes automatically, worth the added complexity for records where users commonly edit different fields independently.
Device clocks are rarely perfectly synchronized, so comparing raw timestamps to determine which edit happened "last" can pick the wrong winner simply due to a few seconds of clock drift, which is why logical clocks that track cause-and-effect ordering produce more reliable results than raw timestamp comparison.
Submit your details and our team will reach out to discuss how we can bring your app or software idea to life.
