A balance is not a number you edit. It's a number you derive.
The moment a ledger stores balance as a column and updates it in place, every reservation becomes an UPDATE — a pending payout, an investor commitment waiting on moderation, an on-ramp order not yet confirmed. You get a correct-looking number and no way to answer why it is that number.
So I treat a hold as its own journal object. Written once, with an amount, an owner, and a state. Later it settles into a real posting, or it releases. Nothing in between rewrites history, and available balance becomes derived: posted entries minus active holds.
The case that breaks naive implementations is partial settlement. A hold rarely settles for exactly what was reserved — an authorization captures less, a payout goes out net of fees. That is two facts, not one edit: post the actual amount, release the remainder, both traceable to the same hold.
The honest trade-off is read cost. Deriving a balance from full history is slow, so you need checkpoints — and a checkpoint is a cache, with all the invalidation problems caches have.
I pulled the pattern into a small reference implementation at github.com/polycratia/ledger-core, mostly because I kept re-explaining the same design in reviews.
If you run holds in production: what closes them — the provider callback, or a sweeper you trust more than the callback?