Dockyard

Engineering

Reconciliation without a nightly batch

A nightly batch is a bet that nothing will go wrong overnight, and that if it does, someone will be awake. It is a bet worth getting out of.

Make the ledger the source of truth

The move that made everything else possible was giving up on mutable balances. Every event appends a row; the balance is a fold over those rows. Recomputing is cheap, replays are safe, and correcting a mistake becomes another append rather than an UPDATE nobody can audit.

src/ledger/replay.ts
export async function replay(accountId: string, upTo: Date) {
  const events = await db.query.ledgerEvents.findMany({
    where: (e, { and, eq, lte }) => and(eq(e.accountId, accountId), lte(e.occurredAt, upTo)),
    orderBy: (e, { asc }) => [asc(e.sequence)],
  })

  // Folding is pure, so the same inputs always produce the same balance —
  // which is what makes a replay safe to run in production.
  return events.reduce(applyEvent, emptyBalance(accountId))
}

Idempotency keys everywhere

Every write carries a key derived from the upstream event. Re-delivering a message is then a no-op rather than a double charge — which means the pipeline can be replayed from any point without a human deciding whether it is safe.

The month-end close stopped being a computation and became a read. It now takes about forty minutes, most of which is waiting on a third-party export.

Maya OkonkwoPrincipal Engineer