Confirmations are not a counter you increment. They are a value you recompute.
That distinction decides whether a deposit system survives a reorg or silently credits money that no longer exists.
The tempting design is obvious: see the transaction in a block, store confirmations = 1, bump it on every new block, credit the balance when it crosses the threshold. It reads cleanly and it works — right until the chain reorganizes. Then your stored number describes a history that was discarded, and nothing in the data model knows that. The deposit keeps aging toward final on evidence that no longer exists.
In chain-watch, an open-source watcher I work on, confirmations are never stored as a running count. They are derived on every poll: current tip height minus the height of the block that includes the transaction, recomputed against the chain as it is right now. If that block is no longer on the canonical chain, the deposit falls back to unconfirmed on its own. There is no dedicated reorg handler, because there is no stale counter to repair. Reorg survival stops being a feature and becomes a property of how the state is computed.
The part people underestimate is what happens downstream. If a credited deposit later becomes orphaned, you cannot fix it by deleting the credit. In any ledger that has to be auditable, the original posting stays and you write a compensating entry against it, with a reference back to the transaction and the block that vanished. Otherwise you have a balance that changed with no explanation anyone can reconstruct six months later during a dispute.
So the rule I hold to: treat block inclusion as evidence, not as a fact. Evidence gets re-evaluated against the current tip every cycle. Facts get written once — and on-chain, almost nothing qualifies as a fact on first sight.
I write up the longer version of these design notes on my blog, including how the watcher models deposit state: https://polycratia.com/c/e9fcca71
If you run custodial deposits: where does your system store confirmation state — derived from the tip, or as a number you increment?