I rewrote bodega’s sync engine three times
Version one was clever. Version two was correct. Version three ships.
bodega started as a weekend hack in 2021: a key-value store for the browser that treats being offline as normal weather, not an emergency. The storage half has barely changed since. The sync half I have now written three times, and each version taught me something I would happily have learned cheaper.
Round one: CRDTs all the way down
I read the papers. All of them, I think. Version one gave every key a delta-CRDT with per-replica lamport clocks — 2,900 lines that passed every property test I could invent. It was correct in the way a proof is correct, and about as easy to debug. The bundle went from 11 kB to 38 kB, and the first real bug report took me four evenings to even reproduce.
If your merge logic needs a bibliography, your users need a different library.
Round two: the event log
Version two was the respectable one: an append-only log of operations, rebased on sync. Every change is a record like this, and the server never resolves anything — it just orders.
{ op: "set", key: "booking:214", field: "status",
value: "confirmed",
hlc: "2026-03-02T14:11:08.412Z-0007-owen" }It worked. It also never stopped growing. Rockaway Surf Club’s booking table hit 40 MB of log for 3 MB of actual state, and I spent a Saturday writing compaction, tombstones, and an apology. Storage is a feature until it’s a bill.
Round three: boring on purpose
Version three is per-field last-write-wins over hybrid logical clocks. Four hundred lines, no bibliography. It resolves conflicts the way a bodega resolves disputes: last hand on the shelf wins, and everyone moves on with their day.
function merge(local, remote) {
const out = clone(local);
for (const f of Object.keys(remote.fields)) {
if (hlc.after(remote.at[f], out.at[f])) {
out.fields[f] = remote.fields[f];
out.at[f] = remote.at[f];
}
}
return out;
}Is it “eventually consistent with per-field granularity and no causal history”? Yes. Has anyone filed a bug about a lost write since the RC went out in January? Also no. The property tests from round one still run against it, which is the one thing I kept.
What I’d tell past me
- Pick the merge semantics your users can predict, not the ones your reading list can defend.
- Any design where storage grows without a ceiling is a countdown, not an architecture.
- Keep the property tests. They outlive every implementation they embarrass.
bodega v3.0.0-rc.2 is on npm now. File issues — I read all of them, usually the same day. And if blogs-without-comments annoy you, the reply-by-email link on the left is real.
— O.P., FORT GREENE, WITH A SELTZER