Local-first sync, conflict handling and why your CLI should work on a plane. Practical notes on designing developer tools that do not depend on a connection.
Developers work in all sorts of places. Trains, cafes, conference halls and countries with patchy internet are all normal. A tool that freezes when the network drops feels fragile, and people stop trusting it.
Offline-first is a design approach where the tool works fully on local data and treats the network as a bonus, not a requirement.
The core idea
The app reads and writes to a local store on the device. A background process syncs changes with the server when a connection is available. The user never waits for the network to do their work.
A sync queue in practice
Every change is saved locally first and added to a queue. When the connection returns, the queue is sent in order. If a send fails, it stays in the queue and is retried later.
const queue = loadQueueFromDisk();
async function save(change) {
applyLocally(change); // user sees the result immediately
queue.push(change);
persistQueue(queue); // survives a restart
trySync();
}
async function trySync() {
while (queue.length && navigator.onLine) {
try {
await sendToServer(queue[0]);
queue.shift();
persistQueue(queue);
} catch {
break; // keep the change, try again later
}
}
}The hard part: conflicts
Two people edit the same thing while offline. When both sync, the server receives two different versions. You need a rule for what happens.
| Strategy | How it works | Good for | Downside |
|---|---|---|---|
| Last write wins | The newest change replaces the older | Simple settings and preferences | One person’s edit can be silently lost |
| Merge by field | Combine changes to different fields | Records with many independent fields | Same-field edits still conflict |
| Ask the user | Show both versions and let a person choose | Important content | Interrupts the user |
| CRDTs | Data structures designed to merge automatically | Collaborative text and lists | More complex to build and reason about |
Choose the simplest strategy that is safe for your data. Many tools never need anything beyond last write wins plus a visible history.
Design tips
- Make offline status visible. A small “working offline” indicator avoids confusion.
- Never block on the network. Show local results first, and update when fresh data arrives.
- Handle retries safely. Make sync operations idempotent so a repeated send does not duplicate data.
- Keep a version or timestamp on every record. It makes conflict detection possible.
- Test with the network off. Use airplane mode as part of your normal testing.