Writing

What is declarative accounting software?

This is a short intro — a seed post while the fuller write-up takes shape. It exists to put one idea plainly.

Most accounting software is imperative: someone posts each journal entry by hand, and the ledger is those stored rows. The source of truth is a pile of rows you mutate.

Declarative accounting flips that. You declare the inputs and the rules — the way you declare infrastructure in Terraform, or a UI in React — and the ledger is derived from them. You never hand-edit a ledger row; you change an input or a rule and regenerate the whole thing.

The piece that does the deriving is a generator: a pure function from immutable inputs to a full double-entry ledger.

function generateLedger(inputs: Input[]): LedgerRow[] {
	const rows: LedgerRow[] = []
	for (const input of inputs) {
		rows.push(...rulesFor(input).map((rule) => rule.apply(input)))
	}
	return rows
}

Because the ledger is derived, it is reproducible (same inputs + same rules → the same ledger, every time), every row keeps its provenance (which input and rule produced it), and the whole history is versionable.

Here is the shape in one domain — crypto, where the inputs are raw on-chain events:

On-chain eventDebitCredit
deposit 2 ETHETH (asset)Capital
buy 1 ETH @ $3,000ETH (asset)USD (asset)
sell 1 ETH @ $3,400USD (asset)ETH + Gain

The longer version — FIFO lots, multi-entity consolidation, what happens when an exchange collapses under you — is still to come.