All posts

Designing for intermittent connectivity is not a degraded mode

Most systems treat disconnection as an exception to recover from. Across much of our footprint it is the normal operating condition, and that changes the architecture.

AAmara Nwosu

There is a common shape to software built elsewhere and deployed here: a synchronous core, and an 'offline mode' bolted on once the first field report comes back. It never works well, because offline was designed as an exception rather than a state.

Connectivity is a spectrum, not a boolean

A sensor on a farm outside Nakuru is not online or offline. It has a bandwidth budget that varies by hour, a latency profile that varies by weather, and a power budget that caps how often it can try. Modelling that as a boolean throws away everything useful.

Our ingestion layer models it as a spectrum, and lets each device negotiate its own reconciliation schedule:

const policy = negotiate({
  bandwidthBudgetKb: 240,
  powerBudgetMah: 18,
  maxStaleness: hours(72),
});

Conflict resolution is a product decision

When a device has been dark for four days and reconnects with buffered writes that conflict with what the server has since accepted, there is no generic correct answer. Last-write-wins silently destroys data. Rejecting the write silently destroys work.

We surface the conflict to the domain layer and make the product team choose. It is more work up front and it is the only approach that survives contact with real deployments.

What it costs

Store-and-forward with negotiated reconciliation costs roughly 30% more engineering than a synchronous design, and about 12% more storage at the edge. In exchange, a week-long outage costs zero data — which on a yield model trained on sparse inputs is the difference between a usable prediction and a useless one.

Keep reading