Mobile

Offline-first is a product decision, not a technical one

Teams treat connectivity as an implementation detail and then discover the product does not work where it is used. The decision belongs earlier, and higher up.

Software designed in offices, used in basements

Field applications are specified in meeting rooms with reliable wifi and validated in the same rooms. They are used in substation basements, loading bays, rural roadsides and hospital corridors, where connectivity is intermittent in ways that are worse than its absence.

Complete absence is easy to handle: you know you are offline and behave accordingly. The difficult case is a connection that resolves DNS, opens a socket and then delivers a fraction of the packets. Applications that check a simple online flag and proceed will hang, retry, duplicate and eventually corrupt.

This is why retrofitting offline support so rarely works. It is not a feature you add; it is an assumption you either made or did not.

The question to answer before architecture

The decision is not “should we support offline”. It is: what is the user permitted to do when the server cannot be reached, and what happens when two people do it at once?

That is a product question with commercial consequences, and it has to be answered feature by feature. Reading a cached record offline is trivially safe. Allocating scarce inventory offline is not, because two people can allocate the same unit and someone has to lose.

Classify every write operation as safe offline, safe with reservation, or requiring connectivity. The third category is legitimate and under-used — some operations genuinely should be blocked, and saying so clearly beats a conflict resolution flow nobody understands.

Conflict resolution is a user experience problem

Once you allow offline writes you will have conflicts. The literature offers CRDTs, operational transforms, vector clocks and last-write-wins. All of them answer the mechanical question. None answers the one that matters: what does the person see?

Last-write-wins is the default and is almost always wrong for field data, because it silently discards work someone did. The engineer who spent twenty minutes on an inspection and had it overwritten does not care about the elegance of the merge strategy.

What works: detect the conflict deterministically, preserve both versions with full provenance, and present them to a person with enough context to choose. Trust in the data is the asset being protected.

Sync rules worth adopting

  • Make every write idempotent with a client-generated identifier. The network will deliver your request twice. Design for it rather than detecting it.
  • Queue with explicit ordering and dependencies. A photograph attached to a record cannot upload before the record exists.
  • Never block the interface on sync. The user's work completes locally; synchronisation is a background concern with visible status.
  • Show sync state honestly. “3 items waiting to send” builds trust. A spinner that might mean anything destroys it.
  • Cap the queue and degrade explicitly. A device offline for a fortnight needs defined behaviour.
  • Test with genuinely bad networks — not disabled wifi, but a proxy that drops packets and adds latency.

Offline-first adds meaningfully to the build cost of the affected features, and rather more to the test effort. That number should be presented to whoever funds the work, with the alternative stated plainly: a product that does not function where it is used. The cost is also almost entirely front-loaded — adding it later changes the data layer and therefore every feature above it.

Working on this?

A conversation with our team, no charge and no obligation.

Book a conversation