polycratia

A rounding direction is the smallest bug you can put in a money system. It is also one of the hardest to unwind.

Take a repayment that has to be split pro-rata across several investors in one deal: one amount in, many shares out. Each share is a percentage of the total, rounded to the minor unit.

Round them independently and the parts stop summing back to the whole. A fraction of a unit sits unassigned, or one unit too many goes out the door. Every row still reads correctly. Every balance looks plausible. The defect lives in the aggregate, which is where nobody is looking.

Then it compounds. The ledger is no longer closed: each distribution leaves a residue nothing owns. Reconciliation against what the bank actually settled starts disagreeing with your internal balances by amounts too small to trigger an alarm and too persistent to dismiss. By the time someone takes it seriously you cannot recompute the truth, because the rounding was applied at write time and the inputs have moved on.

So I make the remainder an explicit decision instead of a byproduct. Shares get computed in integer minor units, and the residual goes to a named party by a stated rule. Then an invariant asserted before commit: the sum of the parts equals the source amount, or the transaction does not happen.

That check costs nothing to write. Reconstructing six months of drift costs weeks.

The dangerous bugs in financial code do not throw. They are correct in every single record and wrong in the set...

I keep a longer set of ledger invariants I apply to this kind of work on my blog, if it is useful: https://polycratia.com/c/2b05f738

For those of you running distribution logic, I'd want to know where you put the residual, and whether that rule is written down anywhere outside the code. Probably not, in most shops.

react

$ new-project --brief

or email hey@polycratia.com