The first time I watched a grower use an app we had built, they held the phone at arm's length, turned slightly away from the sun, and waited eleven seconds for a map tile to load. Then they put the phone back in their pocket and carried on with the job.
Eleven seconds is not a performance problem. It is a product problem. The software had been designed for a desk, a fast connection and someone with time to spare. The field has none of those things.
Here is what I keep coming back to when the users are outdoors, the signal is patchy, and the phone is a tool rather than a toy.
Start with the connection you don't have
Offline-first is often treated as a feature you add at the end. It is much cheaper to treat it as an assumption from the first sketch. That changes the questions you ask:
- What is the smallest useful unit of data this person needs before they leave the office?
- What can be captured without a round trip to the server?
- How will two devices disagree, and who wins when they do?
- What should the screen say when it has been waiting for thirty seconds?
If the answer to the last question is a spinner, the design is not finished.
Sync is a product decision
It is tempting to hand sync to a library and hope. But sync is where the product's rules become visible. Does a later edit always win? Can a field observation be deleted after it has been reviewed? Is a duplicate a mistake or a legitimate second reading?
We wrote those rules down before choosing a mechanism. The mechanism ended up being fairly ordinary: a local queue, idempotency keys, server-side timestamps, and a conflict view for the rare cases a human needed to arbitrate. The important part was not the technology. It was that everyone agreed what "correct" meant before we started.
The hard part of offline software is not storing data locally. It is deciding what the data means when it comes back.
Make failure visible
A field user cannot debug your application. They can, however, understand a sentence. Replace "Something went wrong" with "Saved on this device. Will upload when you have signal." Show the number of pending records. Let people retry one item without retrying everything.
We also learned to make the boring states legible: last successful sync, the age of the map, whether a boundary is provisional. Trust is built out of small honest details.
Design for one bar of signal
Some practical constraints that shaped our interface:
- Text before imagery; a list of fields is useful when tiles are not.
- Cache aggressively, but label stale data as stale.
- Keep the critical path to two taps or fewer.
- Use the camera and GPS as inputs, not as novelties.
- Test on the cheapest handset you can find, in direct sunlight.
That last one is worth repeating. A flagship phone in a dark office is a laboratory. A three-year-old Android in a beet field is the truth.
The checklist
Before shipping anything that will be used outdoors, I try to answer these:
- Can the core task be completed with no connection at all?
- Is unsynced work visibly safe, and visibly unsynced?
- Does the interface explain what it is waiting for?
- Can a user recover from a mistake without calling support?
- Have we watched someone use it while standing up?
None of this is exotic. It is just the ordinary discipline of taking the context seriously. The field is not an edge case. For a lot of software, it is the whole point.