The passenger has found the right ticket. The train enters a tunnel. The app replaces the ticket with a spinner.
The server may be healthy and the mobile code may be behaving exactly as written. The product has still failed at the moment it was needed.
Offline design is not about making every feature work without a network. It is about deciding which promises survive a bad one.
Write a capability map first
Android's architecture guidance gives a useful minimum: “At a minimum, an offline-first app must be able to perform reads without network access”.
That is a starting point, not a universal rule for writes. Classify each important action:
- Must remain available: an issued ticket, saved itinerary or previously downloaded document.
- Can be accepted locally: a note, preference or low-risk form that can wait to synchronise.
- Needs live authority: a purchase, availability check or identity-sensitive change whose truth may have moved.
- Should degrade: recommendations, live status or secondary content that can disappear without blocking the main task.
This map gives design and engineering a shared answer before storage choices harden accidental behaviour.
Make local state the interface's source
For offline-capable reads, render from a durable local store and synchronise network results into it. The interface then observes one source across connection changes instead of switching between unrelated loading, networkData and cachedData branches.
Store provenance with the data: when it was last confirmed, which account owns it and whether it can expire. “Available offline” should not quietly mean “an old value happened to remain in memory”.
Consider the ticket example:
- The app downloads the issued ticket after purchase.
- It stores the barcode, validity window and display details in encrypted local storage.
- The ticket screen reads that record immediately.
- A background refresh can update platform or departure details without controlling access to the ticket itself.
The network improves the screen. It does not gate the core artefact.
Pending is a real status
For writes that can wait, create the operation locally before attempting the network. Give it a stable identifier, preserve the user's input and show a state that distinguishes:
- saved on this device;
- waiting to send;
- synchronising;
- confirmed;
- needs attention.
Do not show “Done” when the durable truth is “Queued”. Equally, do not punish the user with a blocking error for work the app has safely accepted.
The queue must survive an app restart. Retries need backoff, an upper bound and idempotency at the server. A memory array drained by a connectivity listener is a demo; persistent work scheduled under sensible battery and network conditions is a product.
Conflicts need product language
Imagine a field engineer edits a job note offline while a dispatcher closes the same job on the web.
“Last write wins” is an implementation answer. Product questions remain:
- Is an edit allowed after closure?
- Can the note be appended without reopening the job?
- Must the engineer see the dispatcher's change before deciding?
- Who can discard whose work?
For a preference, automatic merging may be harmless. For a booking, payment or clinical record, resolution may require a server rule or a person. Preserve both versions until the rule has run. Never turn a conflict into silent data loss.
Test transitions, not airplane mode
Airplane mode at launch covers one state. The damaging bugs live between states:
- disconnect after the server commits but before the response returns;
- background the app with queued work;
- rotate credentials before synchronisation;
- edit the same entity on a second device;
- reconnect to a captive portal that has a network but no useful internet;
- sign out while another account's data is stored locally.
Run these scenarios on real devices. Include process termination and operating-system background limits, not only a simulator with a toggled connection.
Our mobile product work treats those transitions as part of the interaction design. Products such as Virgin Trains Ticketing make the point particularly clearly: the screen matters most when the person is moving and connectivity is least predictable.
Offline is not an error page. It is a product state with explicit capabilities, honest language and a recovery path.
Sources
Filed under
- Offline-first
- Mobile architecture
- Data synchronisation
- Resilience
