Designing a Delivery App for Unreliable Networks, Not Just Offline

TIL that 'handle offline' undersold the actual problem — the failure mode that hurt drivers most was a flaky, half-connected state, not a clean offline/online boundary.

3 min read

Sabin Shrestha

Full-Stack Developer — Next.js, React & React Native

The Problem #

Early network handling in the driver app treated connectivity as binary: online, requests go through; offline, show a banner and block actions. Real driver conditions — moving between areas with patchy coverage — mostly aren't either state cleanly. A request can start, hang for ten seconds, and either eventually resolve or fail in a way that looks identical to the driver either way.

Context #

Drivers are moving through areas with inconsistent coverage for most of a shift, not sitting in one place with a stable connection or a clean loss of signal.

What I Tried #

Used NetInfo to detect offline state and disabled the "complete delivery" button when it reported no connection.

What Went Wrong #

NetInfo reporting "connected" doesn't mean a request will actually succeed quickly — a technically-connected but very slow or lossy network looked identical to a healthy one from NetInfo's perspective, so the button stayed enabled and drivers hit long hangs on requests that eventually timed out.

The Solution #

Stopped gating actions on connectivity detection and instead made every action locally durable first: a "complete delivery" tap writes to local state and a persistent upload/sync queue immediately, regardless of network state, and a background process retries the queue with backoff whenever a request actually succeeds or fails.

async function completeDeliveryAction(orderId: string) {
  await db.markComplete(orderId); // durable locally, instantly
  await syncQueue.enqueue({ type: "COMPLETE_DELIVERY", orderId }); // retried in background
}

Why It Works #

The driver's action needs to feel instant and safe regardless of what the network is doing at that exact moment, because the network's exact moment-to-moment state isn't something the driver — or the app — can reliably know in advance. Durable local writes plus a retrying background queue make the network's reliability a background concern instead of something blocking the driver's next move.

Lessons Learned #

"Offline support" framed the problem too narrowly. The actual requirement was "the app should work the same regardless of network quality," and a binary online/offline check doesn't capture the messy, in-between states that make up most of a real shift.

What I Would Do Differently #

I'd design around "every user action is durable locally before it's synced" from the first version of the app, instead of retrofitting it after NetInfo-based gating proved unreliable in the field.

Local-first mutation, sync queues with retry/backoff, the gap between connectivity detection and request success.