Newsletter
Newsletter

Offline first is an architecture decision, not a feature you add later

Scroll down
Ashly Dhani
Ashly Dhani
I'm a

Available for consultancy

7+ Years
70+ Projects
40+ Clients
8 Awards
Currently building A vertical SaaS platform In build, deploying soon
  • Location Harare, Zimbabwe
  • Languages English, Shona, Chinese
  • Focus Mobile & data systems

July 24, 2026

9:10 am

Ashly Dhani

Every field project I have worked on arrives at the same conversation eventually. Someone asks whether the app will work without a network, and the answer is treated as a yes or no question about a feature. It is not. Offline capability is a decision about how the whole application is shaped, and the cost of taking it late is far higher than the cost of taking it first.

The version most teams build by accident

The default way to build a mobile app is to treat the server as the source of truth. A user taps something, the app calls an API, the server answers, the screen updates. It is simple to reason about and it works beautifully in an office.

Then the app goes to a district where the signal drops for four hours a day. Every screen that assumed a response now has to decide what to do without one. Teams usually patch this with a cache and a retry queue. That gets you through a demo. It does not get you through a season of data collection, because a cache answers the question of what to show and a queue answers the question of what to send, and neither answers the question the field team actually has, which is what is true right now on this device.

What changes when the device holds the truth

In an offline-first build the local database is the source of truth and the server is a peer that the device reconciles with when it can. Every write lands locally first and returns immediately. Sync becomes a background process rather than the thing standing between a user and their next screen.

That inversion touches almost everything. Identifiers have to be generated on the device instead of handed out by the server, so records created by two enumerators on the same morning do not collide. Timestamps need to record when something happened as well as when it arrived, because those are different facts and reports care about the first one. Deletions become flags rather than removals, since a record deleted on one device and edited on another has to resolve into something sensible. Media files need their own upload lifecycle, because a form submits in a second and the three photos attached to it might take an hour.

None of this is exotic. It is just structural, which is why bolting it on later means rewriting the data layer, the sync layer, and a good portion of the screens.

Conflicts are a policy question

The part teams underestimate most is conflict resolution, and the reason is that it is not really a technical problem. When the same record is edited on two devices, something has to decide which version survives. Last write wins is easy to implement and quietly destroys data. Merging field by field is better but only works if you know which fields can safely diverge.

The honest answer usually comes from the programme rather than the codebase. On a household survey, the enumerator assigned to that household wins, because nobody else should have been editing it. On a stock count, the later reading wins, because stock moves. On a case management record, neither wins automatically and a supervisor is asked to resolve it. Those are three different rules and all three are correct in their own context. Getting them out of the client early is worth more than any clever merge algorithm.

What it costs, honestly

An offline-first build takes longer up front. There is a local schema to design, a sync protocol to specify, and a set of states to handle that a connected app never sees. On a typical data-collection project I would budget somewhere between a quarter and a third more time for the first release compared with the connected equivalent.

What you get back is that the second release is cheaper, because the hard structural work is already done, and that the field phase does not generate the class of incident that kills these projects: a week of collection lost because devices could not reach a server, and nobody could tell which submissions had gone through.

Things worth deciding before any code is written

Which entities are created in the field, and therefore need device-generated identifiers. Which fields can be edited after submission, and by whom. What the retention rule is for data sitting on a device that has not synced. Whether a supervisor can see another enumerator’s unsynced work, and what happens to it if that device is lost. How long a device is expected to run without ever reaching the server, because two days and two weeks lead to different designs.

Answer those and the build is straightforward. Skip them and you will answer them anyway, later, in a hurry, with data already in the system.

The shortcut that is actually fine

Not everything needs full bidirectional sync. Plenty of field applications only ever push, and never need to pull edits back down. If your enumerators create records and nobody else touches them, one-way sync with device-generated identifiers gets you most of the benefit for a fraction of the work. Reach for the complicated version when the workflow genuinely requires it, not because it sounds more thorough.

The test I use is simple. Ask what the person holding the phone should be able to do with no bars on the screen. If the answer is anything more than reading yesterday’s data, the architecture decision is already made.

Posted in Mobile Apps
Write a comment
© 2026 Ashly Dhani. All Rights Reserved.
Email: ashly@rowland.co.zw | WhatsApp: +263 78 744 3918
Download CV Contact me