Building a Local-First Task App From Scratch· Part 4 of 4
July 6, 20252 min read

The Sync Engine

Part 4 — the component that carries commands between devices and the canonical server.

SystemsSyncPostgres

What the sync engine does

The sync engine is small and boring on purpose. It has one job: push local commands up and pull new commands down, without ever losing or duplicating one. Idempotency is the whole game — the server must accept a command it has already seen and respond as if it were the first time.

Rendering diagram…

Backpressure as probability

Sync runs on a schedule, so the queue depth is a random variable. If commands arrive at rate λ\lambda and each sync drains up to kk, the steady-state chance that a command waits longer than one sync interval drops fast with kk:

P(wait>Δt)eλΔtP(\text{wait} > \Delta t) \approx e^{-\lambda \Delta t}

That exponential tail is why batching a few extra commands per sync buys you a lot — each additional command in the batch trims the tail sharply.

Idempotency, concretely

Every command carries a client-generated id. The server stores seen ids in a deduplicated set, so re-delivery is a no-op:

-- insert a command only if we haven't seen its id
insert into commands (id, type, payload, at)
values ($1, $2, $3, $4)
on conflict (id) do nothing;

With that one statement, retries become free, and the engine can be aggressive about pushing whenever the network is available.

Closing the series

Across the three parts we went from why local-first, to a command log as the source of truth, to a sync engine that moves that log around safely. The shape is deliberately dull — and that's exactly what makes it hold up under flaky networks and concurrent edits.

More articles