We were once asked to fix a field service app that "had a bug where it lost data". It did not have a bug. It had an architecture that assumed connectivity, deployed to 600 engineers who spent their days in basements, lift shafts and rural substations. Every assumption in the codebase was wrong for the environment it ran in, and no amount of patching was going to fix that.
Offline capability is not a feature you add. It is a decision about where the source of truth lives, and it needs making on day one.
The core inversion
A conventional application treats the server as authoritative and the client as a view. Every action is a request; if the request fails, the action fails.
An offline-first application inverts this. The local database is authoritative for the user's session. Writes go to local storage immediately and the interface updates instantly. Synchronisation with the server happens in the background, whenever it can, as a separate concern.
The immediate consequence is that the application never shows a spinner waiting on the network for a write. It feels faster even on a perfect connection, which is a pleasant side effect of designing for a bad one.
Sync is the hard part, and it is a product decision
Once two devices can both modify the same record while disconnected, you have a conflict-resolution problem. This is not primarily a technical question — it is a question about your business rules, and it must be answered by someone who understands the domain.
Common strategies, roughly in ascending order of complexity:
Last write wins. Simple, and acceptable for genuinely independent records. Dangerous when two people edit the same order — one person's work vanishes silently.
Field-level merge. Track changes per field rather than per record. If one engineer updates the status and another attaches a photo, both survive. Handles the majority of real-world cases well.
Append-only event logs. Instead of syncing state, sync the events that produced it. Conflicts largely dissolve because you are replaying intentions rather than overwriting values. More work upfront, and by far the most robust for anything involving inventory or money.
Explicit user resolution. When the rules genuinely cannot decide, show both versions and ask. Used sparingly this is honest; used often it is exhausting.
For the retail POS network we built across 140 stores, we used an append-only event log for stock movements and field-level merge for everything else. Stock could never be silently overwritten, which was the one thing the business could not tolerate.
Details that decide whether it actually works
Idempotency keys on every write. A device that loses connectivity mid-request does not know whether the server processed it. It will retry. Without an idempotency key, that retry creates a duplicate order. This is the single most common defect in half-built offline systems.
Monotonic sync tokens, not timestamps. Device clocks are wrong, sometimes by hours, occasionally by years. Never make correctness depend on a client-supplied timestamp. Use a server-issued sequence.
Bounded local storage. A device that syncs three years of history will run out of space and slow to a crawl. Sync a working window, fetch older records on demand.
Visible sync state. Users must be able to see whether their work has reached the server. A small, honest indicator — "12 items pending" — prevents the anxiety that otherwise leads to people re-entering data "just in case".
Test with the network off. Not throttled. Off. Then flapping — on for two seconds, off for ten. That is what a lift shaft actually looks like, and it breaks systems that pass every test on a stable connection.
When not to do this
Offline-first adds genuine complexity. If your users sit at desks on reliable connections, you are buying a conflict-resolution problem you do not have. Do not build it for a back-office admin tool.
But if your software goes into a warehouse, a vehicle, a factory floor, a construction site or a shop counter, the connectivity assumption will break. It is far cheaper to design for that at the start than to discover it from support tickets.
Building for the field? Talk to us about architecture — we have shipped offline-first systems to fleets of hundreds of devices.
