When Two Systems Are Both Right

A field tech is in a truck with no signal, closing out a job on their phone. At the same time, back in the office, someone opens the same job in the CRM and adds a note. Neither of them did anything wrong. Neither of them even knows about the other one. And in about twenty minutes, when the phone finds a signal again, your integration has to decide what "the truth" is for a record that two different systems, and two different people, both legitimately touched at the same time.

Most integration work — mine included, most of the time — quietly assumes this doesn't happen. One system is the source of truth, the other is downstream, and if they disagree, downstream is wrong and gets overwritten. That assumption is doing a lot of invisible work. Local-first software is built specifically to break it.

What "local-first" actually means

The term comes from a well-known 2019 essay out of Ink & Switch, and the idea is simple to state and awkward to sit with: the app on your device is not a thin window into a server's data, it's a full local copy. It works completely offline. When a connection comes back, it syncs — not by asking permission from a central authority, but by merging its changes with whatever else happened while it was gone.

The mechanism that makes this work — CRDTs, conflict-free replicated data types — is the part that matters for integration work. A CRDT is a data structure built so that two divergent copies can always be merged back together automatically, without a referee, and without losing either side's changes. Not "last write wins." Both writes win, merged.

Field mapping assumed a referee

Field mapping, the work I actually get hired to do, has always had an implicit referee built in. Your "Customer ID" and their "Account Number" are the same thing — I map one to the other, and when they conflict, there's a rule for who's right: usually whichever system is closer to the transaction, or whichever one someone decided to trust. That rule is the whole game. It's why "field mapping is the real work" and not the API calls around it.

Local-first apps don't give you a referee to defer to. There isn't a moment where the CRM is "more current" than the tech's phone — they were both current, for different slices of the same record, at the same time. The old question was: which system is right? The honest new question is: which parts of this record can even conflict, and what does merging them correctly look like?

That's a different kind of field mapping. It's not "map field A to field B." It's "field A is a status enum, so last-write-by-timestamp is fine; field B is a note, so both notes should survive; field C is a dollar amount, and if two systems both changed it offline, that's not a merge, that's a conflict a human needs to see." You're not mapping fields anymore. You're mapping how each field is allowed to disagree with itself.

What this does to "integration"

Most integration architecture — webhooks, polling, ETL jobs, the stuff most of my other posts here are actually about — assumes synchronous-ish reality: a change happens, it propagates, downstream catches up, and at any given moment there's a state you could call current if you asked. Local-first design assumes the opposite: change happens in disconnected pockets, for hours or days, and "current" isn't a meaningful question until sync actually happens.

If any of the systems you're integrating are local-first — and more of them are every year, because offline-capable software is a real product advantage, not just an engineering exercise — the integration layer has to absorb that same assumption. Not "sync the current state." Sync the history of changes, and merge it the way the source app already promised its users it would.

The uncomfortable part is that this isn't optional once one side of an integration is local-first. You can't bolt "one source of truth" logic onto a system that was built to not have one. The merge semantics live in the app already; the integration has to speak the same language or it'll silently throw away exactly the kind of change local-first was built to protect — the one made offline, by someone who had every right to expect it wouldn't get overwritten by someone else's edit made twenty minutes earlier, from a truck with no signal.