Offline-First Field Apps: CRDTs vs Last-Write-Wins for Rural Routes
Short answer
For most field apps, use field-level merge as the default for inspection forms and orders. Record inventory counts as operations (deltas or observations) instead of stored totals. Keep last-write-wins only for fields that one person owns, like a job status. Payments, stock limits and approvals should stay server-authoritative. Don't order writes by device clock time. A full CRDT library is only worth it when several people edit the same long text or list at once.

Most writing on sync conflicts assumes chat apps, shared documents and users who lose signal for 30 seconds in an elevator. Field apps work under different conditions. An inspector or route rep can be offline for a whole shift, and a bad merge doesn't just garble a paragraph. It can leave a wrong pallet count, a duplicate order or an inspection result nobody can defend. This article covers the strategy choice for those conditions.
Why are offline periods so long on Northeast Louisiana routes?
The territory is large and the network is still being built. Louisiana State Police Troop F lists Morehouse Parish at 807 square miles with 938.20 highway miles, Richland at 867.77 highway miles, Franklin at 918.35 and Union at 884 square miles. A rep working out of Bastrop covers a lot of road between towers.
The buildout is still underway. Louisiana's GUMBO 2.0 program, backed by the federal BEAD program, carries $1.355 billion in Infrastructure Investment & Jobs Act funding and aims to close the state's digital divide by 2028. Named local projects are still in progress: a GUMBO-funded SkyRider Communications project in Union Parish has a total project cost of $3,579,074.30 to serve 670 households and 24 businesses at a base speed of 100:20 Mbps. Pelican Broadband is replacing legacy copper across roughly 300 miles over five years.
Those programs fund fixed broadband, not cell coverage, and nobody publishes a good parish-level dataset on mobile dead zones. Still, the practical conclusion holds. Design for offline periods measured in hours and sometimes days, and treat a quick reconnect as a bonus.
Long offline windows change the engineering in 3 ways:
- More concurrent edits. Two reps who are both offline for 6 hours overlap far more often than two users who were each offline for 30 seconds.
- Larger replay queues. A device can come back with hundreds of queued writes, so replay has to be idempotent and has to handle ordering explicitly.
- More clock drift. A device that can't resync its time for days is the worst case for timestamp-based ordering.
What happens when two reps edit the same record offline?
That depends on which of the 3 core strategies you picked. One guide sums them up as resolving by "the newer copy of the whole record," "every field only one side touched," and "every update, merged by rule." Back4app's sync glossary adds manual resolution as a fourth option and compares them on data loss and cost:
| Strategy | When data is lost | Build cost | Fits |
|---|---|---|---|
| Last-write-wins (LWW) | Silently, whenever writes overlap | Trivial | Single-writer records, toggles, statuses |
| Field-level merge | Only when both sides change the same field | Moderate | Forms edited by a few people |
| Manual resolution | Never | Needs a conflict interface and a workflow | Fields where a wrong guess is costly |
| CRDT / CRDT-lite | Never at the data-structure level | Modeling discipline, library weight | Counters, sets, collaborative structures |
Take a concrete case. Rep A adds 2 line items to an order in Union Parish. Rep B, also offline, changes the delivery date on the same order. With record-level LWW, whichever sync lands second replaces the whole order, so either the line items or the date disappear and nobody is told. With field-level merge, both changes survive because they touched different fields.
LWW isn't always wrong. Hasura notes that Firebase and Trello use the simplest LWW approach on the assumption that users will fix bad data later. That assumption holds for a kanban card. It doesn't hold for a stock count that drives a reorder, or for a signed inspection.
Can you trust device timestamps for ordering?
No. Mobile clocks are often several minutes or even days off, and that's why timestamp-based LWW is known for silent data loss in production. A rep whose phone clock runs fast will win every conflict, including conflicts against corrections a supervisor made afterward.
Vector clocks replace wall-clock time with logical counters that record which version each device had seen. In practice, I use a simpler combination for most field apps:
- Each record (or each field) has a server-assigned version number.
- Every write from a device carries the base version the device last saw, plus a per-device sequence number.
- The server can then detect a conflict exactly: a conflict exists when the base version is older than the current version. It doesn't have to guess from timestamps.
The device timestamp is still stored, because auditors and supervisors want to know when the rep said they did the work. It just never decides which write wins.
Is last-write-wins safe for inventory counts?
It isn't. A stored total is the classic way to silently lose a correction. Suppose a driver counts 40 cases at 9:00 while offline. At 9:30 the warehouse books a delivery of 24, bringing the total to 64. The driver syncs at 2:00, and LWW sets the total back to 40. The delivery has vanished from the books.
The fix is the CRDT-lite pattern: store a quantity as a series of increment operations, not as a total, so those fields no longer conflict. A physical count needs one more refinement. A count is an observation, not a delta, so record it against the baseline the device saw and let the server work out the adjustment:
CREATE TABLE stock_event (
op_id uuid PRIMARY KEY, -- generated on device; replay is idempotent
device_id text NOT NULL,
device_seq bigint NOT NULL, -- logical order per device
location_id bigint NOT NULL,
sku text NOT NULL,
kind text NOT NULL, -- 'delta' | 'count'
delta integer, -- for receipts, damage, transfers
observed_qty integer, -- for physical counts
base_version bigint, -- ledger position the device had seen
device_time timestamptz, -- informational only
received_at timestamptz NOT NULL DEFAULT now()
);
-- On-hand is derived: SUM of deltas, with a count resetting the baseline
-- as of base_version, so events after that baseline still apply on top.
When the driver's count of 40 comes in with a base version from before the delivery, the server applies the count at that point in the ledger and then applies the delivery of 24 on top. If the count doesn't match what the ledger expected at that point, the difference becomes an explicit shrink or adjustment entry that someone can review. It is never folded silently into the total. This is the same reasoning as a double-entry ledger for commissions: a single mutable number can't explain why it changed.
Because op_id is generated on the device, a sync that times out and retries can't apply the same adjustment twice. That is the same idempotency key discipline used for payments.
Do inspection forms need CRDTs?
Usually not. Inspection forms are structured records with a few editors, and field-level merge is the sensible default for that shape of data. Another practitioner reaches the same conclusion: per-field merging with a reliable clock covers status flags, dates and short text, and a CRDT library is only worth it when users edit the same long document at the same time.
The risk with inspection data isn't a merge failure. It's an automatic merge that nobody can account for afterward. A CRDT guarantees that replicas converge to the same state regardless of arrival order, but converging on a state isn't the same as being defensible. If two inspectors record different pass/fail results for the same item, I don't want any algorithm choosing one. That field should go to manual resolution, with both values and both authors kept.
A three-way field merge keeps that rule explicit:
type Merge<T> = { value: T; conflict?: { local: T; remote: T } };
function mergeField<T>(base: T, local: T, remote: T, manual: boolean): Merge<T> {
if (local === remote || remote === base) return { value: local };
if (local === base) return { value: remote };
// Both sides changed the field to different values
return manual
? { value: base, conflict: { local, remote } } // queue for a human
: { value: remote }; // policy for low-stakes fields
}
Signatures and photos sit outside the merge entirely. Treat them as immutable, append-only attachments tied to the form version they signed. If the form changes after signing, the signature should show as attached to an older version. It should never silently carry over to the new one.
Can you mix strategies in one app?
Yes, and almost every serious field app should. The bugs come from mixing them carelessly. A common split keeps UI state and drafts CRDT-friendly while payments, inventory limits and permissions stay server-authoritative. CRDTs resolve data-structure conflicts. They don't resolve business-rule conflicts: 2 reps can each promise the last 10 units, and the CRDT will converge on an order for 20.
The decision framework I use, field by field:
| Data | Who writes it | Strategy |
|---|---|---|
| Job or stop status | Assigned rep only | LWW by server version |
| Order header (date, PO, notes) | Rep plus office | Field-level merge |
| Order line items | Rep plus office | Add/remove set keyed by line ID |
| Stock quantities | Many | Operations ledger, counts as observations |
| Tags, defect lists | Many | Add/remove set (CRDT-lite) |
| Inspection results | Inspector, reviewer | Field merge, manual on same-field collision |
| Long shared notes edited at once | Several | CRDT text type, if truly needed |
| Credit limits, pricing, approvals | Server | Server-authoritative; device submits a request |
Mixing strategies causes bugs in 3 places. First, a field's strategy changes between app versions while old clients are still in the field, so version the sync protocol. Second, derived fields (an order total, for example) get synced as though they were source data, so always recompute them on the server. Third, cross-field rules (if a result is "fail," a corrective action is required) are checked only on the device, so validate them again after the merge and flag records that break a rule.
On cost: LWW is close to free, field merge adds per-field version tracking and a conflict queue, and a CRDT library adds payload weight, migration complexity and a team that has to model data carefully. The real driver of cost is how many distinct data shapes you have and how much of the conflict-review interface supervisors need. The library choice matters much less.
Where to start
- List every entity your field staff touch, and for each field write down who can change it.
- Mark the fields where a silent overwrite costs money or creates legal exposure. Those fields need ledgers or manual resolution.
- Remove device timestamps from conflict ordering and add base versions and device-generated operation IDs.
- Test with real offline durations: put 2 devices in airplane mode for a full simulated shift, edit overlapping records, then sync them in both orders.
- Build the supervisor conflict queue before launch, not after the first disputed count.
If you're scoping or fixing a field app with these problems, email hello@josephleporati.com.
Common questions
Will GUMBO and BEAD broadband funding make offline-first unnecessary?
Not for mobile field work. GUMBO 2.0 brings $1.355 billion in BEAD funding and aims to close Louisiana's digital divide by 2028, and grant projects like the Union Parish SkyRider buildout target households and businesses. Those programs fund fixed connections to homes and businesses, not cell coverage along farm roads. Reps driving between sites will still hit gaps, so the app should treat being offline as normal behavior, not as an error state.
How should a field app handle photos taken offline?
Store each photo locally with a device-generated ID and content hash, and upload it on its own resumable queue, separate from the form data. The form references the photo by ID, so a slow upload never blocks syncing the record. Photos should be immutable: a retake creates a new attachment rather than replacing the old one. That keeps an auditable history and avoids conflicts on large binary files entirely.
What should happen when a device comes back after several days offline?
The server should accept the queued operations in per-device sequence order, check each one against the base version it was made on, and apply what merges cleanly. Anything that collides on a protected field goes to a review queue instead of being dropped. The device then pulls the current server state. Make sure the server keeps enough version history to evaluate stale base versions, or long-offline devices will force full resets.
Is Firebase-style last-write-wins acceptable for a small field team?
It can be for data with a single owner, such as each rep's own stop statuses, where conflicts are rare and easy to fix. It becomes risky once 2 people can touch the same order or stock record, because the losing write disappears without any trace. Even small teams should move quantities to an operations ledger and use field-level merge on shared records, which costs little extra to build.
Sources
- Troop F Information - Louisiana State Police lsp.org
- BEAD (GUMBO 2.0) - Connect LA connect.la.gov
- The Sky's the Limit in Northeast Louisiana - Union Parish - GUMBO Grant Program gumbo.la.gov
- Pelican Broadband Introduces Enhanced Services Following Rebranding sabineparishjournal.com
- Sync conflicts: last write wins, field merge or CRDTs quantumcraft.uk
- Offline-First Data Sync: Local Stores, Conflicts, and Replay (2026) back4app.com
- A Design Guide for Building Offline First Apps hasura.io
- Architecting Offline-First Mobile Apps: Resilient Sync Engines & Conflict Resolution dev.to
- Beyond Offline-First: The Nightmare of Data Synchronization & CRDTs medium.com
- Last-Write-Wins Is Not a Sync Strategy: Handling Conflicts in Offline-First Mobile Apps dev.to
- TypeScript CRDT Toolkits for Offline-First Apps: Conflict-Free Sync Without Tears medium.com