Most frameworks and tutorials start from an implicit assumption: the network is available, fast and reliable. On a mobile link in West Africa, none of the three is guaranteed.
Here are the design decisions that separate a usable application from one the teams work around by going back to paper.
1. Distinguish "slow" from "disconnected"
Most applications treat both cases identically: a spinner that turns forever. That is the worst possible behaviour, because the user cannot tell whether to wait or start over.
Set an explicit timeout — five to ten seconds depending on the operation — and distinguish three on-screen states: in progress, network failure, server failure. All three call for different user actions.
2. Write locally first
When a user confirms an entry, save it locally before attempting to send it, then confirm visually. Synchronisation becomes a background task, and a dropout no longer loses work.
This inversion completely changes how the app feels: it stays responsive even when the network is not. It does require handling conflicts, which brings us to the next point.
3. Decide the conflict rule in advance
If two people edit the same record offline, which one wins? Do not leave this to chance: the answer depends on the business.
- For stock, what usually matters is the sum of movements, not the last value written.
- For a customer record, the latest timestamped change generally prevails.
- For a booking, the server must arbitrate — two people cannot get the same room.
Writing this rule down before coding avoids painful data corrections six months later.
4. Make writes replayable
On an unstable network, a request can succeed on the server without the response getting back. The client, seeing nothing, retries — and creates a duplicate.
The fix is simple: every write operation carries a unique identifier generated by the client. If the server receives the same identifier twice, it applies the operation once and returns the same result. It is a few lines of code, and it eliminates an entire category of bugs that are impossible to reproduce.
5. Retry intelligently
A client that retries immediately in a loop worsens congestion and drains the battery. Space attempts out progressively — one second, two, four, eight — with a ceiling, and add a small random variation so that all devices do not resynchronise at the same instant after an outage.
6. Keep payloads small
Every byte counts when bandwidth is scarce and metered. Two simple habits: request only the fields actually displayed, and always paginate. A customer list that returns the entire database on every open is comfortable to write and expensive to use.
Compression must be enabled server-side — often a single configuration line, with immediate gains.
7. Tell the user the truth
An application that displays "Saved" when the data is merely queued locally betrays trust the first time something is lost. Show the real state: saved on device, then synchronised. Users accept an imperfect system very well as long as they understand where they stand.
The test that matters
Before any production release, run this exercise: switch to airplane mode mid-entry, add three more records, then turn the network back on. Everything must reappear, with no duplicates, no loss, no manual intervention.
If that test passes, your application will hold. If it does not, no amount of testing under ideal conditions will tell you what awaits you in the field.