"It should work offline" is one sentence in a brief and several weeks of design work in practice. Here is what that sentence actually expanded into on a recent project.
The naive version fails fast
The first pass at any offline feature is usually a local cache that mirrors the server, synced when a connection returns. This works for reading data. It falls over the moment two people can write to the same record while both are offline, because now there are two versions of the truth and something has to decide which one wins.
Every write needs a resolution rule, decided in advance
We went through the write paths in the app one at a time and asked what should happen if two offline writes to the same record arrive out of order. The answers were not uniform:
- Status updates — last write wins, by timestamp, because only the current state matters.
- Notes and comments — append-only, both survive, ordered by device time with a server-corrected sequence.
- Anything touching a monetary total — no automatic resolution. It queues for a person to look at.
Deciding this after writes are already colliding in production is much more expensive than deciding it on a whiteboard before a line of sync code exists.
Clock drift is a real bug source
Devices disagree about the time, sometimes by minutes, especially when they have been offline for a while. A resolution rule based on device timestamps needs a server-side correction step, or "last write wins" quietly becomes "whichever device's clock is most wrong wins", which is worse than no rule at all.
Offline-first is not a checkbox on a feature list. It is a second, smaller product — with its own storage, its own conflict rules, and its own testing — that happens to share a codebase with the first one.
Testing it required breaking the network on purpose
Standard testing assumes a connection. We built a harness that could kill connectivity mid-operation, at specific points in a write sequence, and re-establish it in a controlled order — the only way we found the conflict cases that mattered, because they do not occur reliably by accident.
What we would tell a client scoping this today
Budget offline support as its own line item, not as a modifier on the main feature's estimate. The UI work is the smallest part of it. The conflict rules and the testing harness are where the time goes, and they need to exist before the feature is considered done, not added once the first conflict is reported.
