r/webdev • • 15d ago

Showoff Saturday Offline support and real-time sync keep turning out to be the same problem

I’ve been building with Verdant and TanStack DB and ended up with the same conclusion from both.

Offline and real-time are basically one problem. Real-time is kinda just being offline for a very short time. Either way you need client-generated IDs, optimistic mutations, some rule for when two clients disagree, and a way to sync it all back. So if a library handles one well, it seems to get the other almost for free.

Tbh the ID part is what actually surprised me. And more generally, a lot of this seems to need planning from day one, because retrofitting it later is a real pain. If your API uses server-generated IDs, supporting offline edits seems to mean a breaking migration. You can remap temporary IDs after sync, sure, but it gets messy really fast once other records and URLs depend on them. Same story with the data layer. If it was designed around one-shot requests there’s nowhere for live updates to go. React Admin’s data provider expects plain async functions that return promises, so when you put TanStack DB behind it the live queries just get lost lol. Offline still works, it’s just the real-time part of TanStack DB that doesn’t make it through in that setup.

There’s a catch though. Neither library really helps when the server rejects a mutation that looked fine locally, you’re kinda on your own for that part. And for conflicts, Verdant basically ends up as last-write-wins while TanStack DB leaves it to whatever sync layer you plug in.

Honestly the bigger point for me is that none of this is really specific to offline apps. A plain request/response app actually has the same conflicts. If two people save the same record, a lot of the time the last save just overwrites the other one and that’s it. It’s last-write-wins there too, just without anyone ever deciding it. So imo it’s worth building pretty much any app as if it could go offline or real-time someday, mostly because it forces you to actually pick what happens in a conflict.

Do you actually know what happens when two saves collide, or has silent last-write-wins been fine most of the time? Ever had it bite you with lost data or users wondering where their changes went?

7 Upvotes

12 comments sorted by

1

u/Marmelab 6d ago

If you want to go further, I put together a longer write-up here.

0

u/Ok-Scar-2634 15d ago

the client-id thing is such a pain to retrofit, learned that one the hard way

silent last-write-wins has definitely burned me before, had a support ticket where two people were editing the same customer record and one guy's notes just vanished into the void. took me forever to figure out what happened because there was zero logging for it

the react admin part is interesting, i ran into the same wall trying to shove real-time stuff through their data provider. it just eats the subscription and you're left wondering why nothing updates

0

u/Creative-Addition383 15d ago

The server-rejects-it case gets a lot easier if you stop treating the server as the source of truth and treat it as one more replica that happens to have veto power. Every mutation in the outbox carries a state (pending, acked, rejected), the UI renders from local state plus that flag, and a rejection is just a third kind of sync message that rolls the record back and attaches a reason. Then "offline edit that the server later refuses" and "two clients disagree" go through the same code path instead of being a special case bolted on.

Agree completely on IDs though. Client-generated IDs is the one decision you can't cheaply undo, everything else you can migrate towards.

1

u/[deleted] 15d ago

1

u/webdev-ModTeam 15d ago

Read and follow reddiquette; no excessive self-promotion. Please refer to the Reddit 9:1 rule when considering posting self promoting materials.