Fintech Internals: Why Financial Data Is Harder to Model Than You Think
Financial data looks simple from the outside. Numbers, dates, amounts, accounts. Clean rows in a table. The kind of thing any junior developer would feel confident building a schema for on a Tuesday afternoon.
Then you actually work with it. And you realise that financial data is one of the most semantically dense, temporally complex, and correctness-critical domains you will ever model. A wrong type in a social media app ships a bad tweet. A wrong type in a financial system ships money to the wrong place.
This is what nobody tells you before you start.
Money Is Not a Float
The first and most important lesson. You do not store monetary values as float or double. Ever.
Floating-point arithmetic is designed for scientific computation where small rounding errors are acceptable. Financial arithmetic is not that. When you add 0.1 + 0.2 in most languages, you do not get 0.3. You get 0.30000000000000004. In a scientific context, that’s fine. In a payments context, that’s a bug that either loses your users money or creates it from nothing — and both are problems.
The correct approach is fixed-point arithmetic. Store amounts as integers in the smallest unit of the currency — pence, cents, paisa — and only convert for display. If you must use a decimal type, use your language’s Decimal or BigDecimalclass, not a native float. The rule is simple: floats are for physics, not finance.
Currency Is a First-Class Attribute
An amount without a currency is meaningless. 500 means nothing. 500 USD means something. 500 JPY means something completely different.
This sounds obvious, but it gets violated constantly. A common mistake is building a system that implicitly assumes one currency — usually whatever the founding team’s home currency is — and then bolting on multi-currency support later when the business expands. That retrofit is painful. The schema fights you at every step.
The right model stores currency alongside every monetary amount from day one. (amount: integer, currency: ISO 4217 code) as an inseparable pair. You don’t store amount in one column and a single system-wide default_currencyconfig somewhere. You store them together, always.
Transactions Are Immutable. Corrections Are New Records.
This is the rule that surprises most developers coming from non-financial domains.
In a typical application, when data is wrong, you update the row. You fix the value and move on. In financial systems, that model breaks down immediately. Once a transaction has been recorded, it is a fact about the world. You do not edit facts. If the fact was wrong, you create a new, corrective record.
This is double-entry bookkeeping, and it predates computers by five hundred years for a reason. Every debit has a corresponding credit. Every correction is a reversal plus a new entry. The ledger is append-only. This isn’t bureaucracy — it’s auditability. You need to be able to reconstruct the state of any account at any point in time, and you can only do that if nothing in your history has been silently overwritten.
The practical implication: your financial tables need created-at timestamps, they need status fields that distinguish pending from settled from reversed, and they need a reference to any corrective entry that supersedes them. The row you inserted yesterday is permanent. Build that assumption into your schema from the start.
Time Is More Complicated Than You Think
Financial data has multiple distinct time dimensions, and confusing them causes serious bugs.
There is the time the event happened in the real world. There is the time the event was recorded in your system. There is the value date — the date on which funds are considered available. There is the settlement date — the date the transfer actually clears. And there is the effective date — the date from which a particular rate, fee, or rule applies.
These are not the same thing, and they often differ by days. A payment initiated on Friday might have an effective date of Friday, a settlement date of Monday, and a recording time of Saturday if batch processing runs overnight. If your system only stores one timestamp and treats it as all of the above, your reporting will be wrong, your reconciliation will fail, and your interest calculations will be off.
Model each of these explicitly. Column names like created_at, event_at, value_date, and settlement_date are not over-engineering. They are the minimum representation of what actually happened.
Precision Rules Change by Currency
Not all currencies use two decimal places. Japanese Yen has zero. Kuwaiti Dinar has three. This is codified in ISO 4217 and it matters for display, rounding, and validation.
A system that hardcodes two decimal places will display ¥500.00 — which is wrong — and will potentially round incorrectly when converting between currencies. The number of minor units for a currency must come from a lookup, not a constant.
Reconciliation Is Not Optional
Any financial system of consequence will eventually need to reconcile its internal records against an external source — a bank, a payment processor, a clearing house. Reconciliation means: take every record in our system, find its counterpart in theirs, verify the amounts match, and flag anything that doesn’t.
This process only works if your data was modelled to support it from the start. You need stable, unique identifiers that can be matched across systems. You need status fields that track what has and hasn’t been reconciled. You need the ability to query account state at any historical point in time.
If your schema wasn’t designed with reconciliation in mind, you will rebuild it under pressure, probably during an incident.
The Stakes Change Everything
Most of software engineering tolerates imprecision. You can ship a feature with rough edges, gather feedback, and iterate. Financial systems don’t work that way. A rounding error that affects one in a million transactions is still a rounding error that moves real money, and depending on which direction it rounds, it’s either fraud or a loss.
The complexity in financial data modelling isn’t accidental. It reflects the genuine complexity of money — the fact that it exists in time, in context, in relationship to other money, and with legal and contractual meaning attached to every movement.
Get the model right before you write the business logic. It is much harder to fix later.
Enjoyed this post?
Stay in the loop
New posts + weekly digest, straight to your inbox.
Create a free account
- Save posts to your vault
- Like posts & build history
- New-post alerts
No comments yet. Be the first to comment!