A number that represents money is only trustworthy if it can never quietly change without leaving a trace. That sounds obvious once it is said out loud, yet it is one of the easiest rules to break by accident. A support agent fixes a customer's balance directly in the database after a complaint. A backend job updates an account total to correct a bug. A well meaning developer runs a one off script to patch a number that looked wrong. Every one of these actions feels reasonable in the moment, and every one of them destroys the one thing a financial system actually depends on, the ability to prove, later, exactly how a number became what it is.
This is the real discipline behind a trustworthy financial backend, and it has very little to do with clever code and everything to do with refusing to ever let a balance be edited directly. It is also one of the least glamorous parts of building financial software, which is exactly why it gets skipped so often. Nobody sets out to build a system that lies about money. It happens gradually, one convenient shortcut at a time, until the system has quietly drifted into a place where nobody can fully explain how a number got to be what it is.
Why a stored balance cannot be the source of truth
Most systems that are not built with this discipline store an account balance as a single number and update it in place whenever money moves. The moment that number can be changed directly, by a script, an admin panel, or a rushed bug fix, it stops being a fact and becomes an opinion, something that reflects whatever the last write happened to be, with no way to reconstruct how it got there. If a customer disputes their balance six months from now, or a regulator asks how a number was reached, a system built this way has no real answer beyond trusting that everything along the way was done correctly.
This is not a hypothetical concern. Financial disputes are common, regulators do ask these questions, and the answer a bank gives in that moment either comes from a system that can reconstruct its own history with certainty, or from a system that is essentially asking everyone to take its word for it.
Treating every change as a new fact, never an edit
The discipline that fixes this is refusing to ever update a balance directly. Instead, every single change to an account is recorded as its own permanent entry, and the balance itself is only ever calculated by adding up every entry that has ever happened. Nothing is ever overwritten. A correction is not an edit to history, it is a new entry that says exactly what was corrected and why, sitting right alongside everything that came before it.
In NestJS, this discipline lives naturally inside the service layer, where a balance is never something you fetch and mutate, it is something you compute fresh, every time, from the full history of entries tied to that account. The service becomes the one place this rule is actually enforced, rather than something every developer has to remember on their own.
async getAccountBalance(accountId: string): Promise<string> {
const result = await this.dataSource
.createQueryBuilder()
.select(
"SUM(CASE WHEN entry.type = 'CREDIT' THEN entry.amount ELSE -entry.amount END)",
'balance',
)
.from(LedgerEntry, 'entry')
.where('entry.accountId = :accountId', { accountId })
.getRawOne();
return result?.balance ?? '0.0000';
}
There is no balance column being read here, and nothing being written to correct one either. The number is simply the honest sum of everything that has ever happened to that account, which means it cannot drift away from reality without every single entry behind it also being wrong.
What happens when a mistake genuinely needs fixing
This discipline does not mean mistakes never get corrected, they absolutely do. What changes is how a correction is made. Instead of reaching into the database and changing an existing entry, a correction is made by adding a brand new entry that reverses or adjusts the original one, clearly labeled as a correction, tied back to whatever it is fixing. If a customer was charged the wrong amount, the fix is not deleting the wrong entry and quietly typing in the right number. The fix is leaving the original entry exactly as it happened, and adding a second entry beside it that corrects the balance going forward while preserving an honest record of what actually occurred, including the mistake itself.
This might feel slower or more cautious than simply editing a number, and it is, deliberately so. The extra step is what makes the difference between a system that can fully explain itself later and one that can only ever hope nobody asks.
Making it structurally hard to cheat
The other half of this discipline is making sure nothing in the system can bypass it, even under pressure. That means the database itself should never allow a ledger entry to be updated or deleted once it has been created, only ever inserted. Combined with the fact that the balance is always computed rather than stored, there is simply no direct path left for a rushed fix or a one off script to quietly rewrite history, because there is no editable number sitting anywhere to rewrite.
The actual value of this discipline
None of this is about distrusting the people who work on the system. It is about removing the need to trust anyone's good intentions in the first place. A bank does not want to be in a position where the honesty of its numbers depends on nobody ever taking a shortcut under deadline pressure. Building the discipline directly into how the backend is structured, immutable entries, computed balances, corrections instead of edits, means the numbers stay honest by default, not because everyone remembered to be careful, but because the system never gave anyone the option not to be.
If you are working on a system where the numbers need to hold up under real scrutiny, not just in a demo, I would be glad to talk through how to structure it this way from the start.
I am Peace Melodi, a backend software engineer. If you want your business to scale big, comfortably handling millions of users without breaking, with strong scalability and security in place, feel free to reach out.
LinkedIn: https://www.linkedin.com/in/melodi-peace-406494368
GitHub: https://github.com/PeaceMelodi
Top comments (0)