Financial systems are unforgiving. A bug that causes data loss is acceptable in social media; it's a compliance violation in fintech. A missing transaction is a customer support issue in most systems; it's a money problem in banking.
This difference shapes architecture: fintech systems need to be built for auditability from the start, not bolted on afterward.
The ledger is your source of truth
In most systems, you store computed state. In banking, you store transactions, and state is derived from transactions.
System state = sum of all valid transactions
This changes everything:
- If you query account balance, you're summing transactions, not looking up a stored number
- If there's a discrepancy between stored balance and calculated balance, the transactions win
- Every operation is an immutable record before it affects state
- Audit is automatic: it's embedded in the transaction stream
This approach prevents a class of bugs that plague transaction systems: state getting out of sync with what actually happened.
Idempotence and replaying
Financial operations must be idempotent: if you accidentally send the same transaction twice, only one should execute.
This matters because:
- Network timeouts happen (you sent the transaction, but didn't get a response)
- Retries happen (was that successful or should I retry?)
- Disaster recovery means replaying transactions
Idempotent design means: "send transaction X three times and get the same result as sending it once."
Implement this by:
- Requiring unique IDs for each transaction (so duplicates are recognized)
- Checking if a transaction already exists before applying it
- Returning the same result if a duplicate is submitted
Reconciliation must be automatic
In systems that don't have good auditability, reconciliation is a manual, expensive process: accountants comparing reports, finding discrepancies, and figuring out what actually happened.
In well-designed systems, reconciliation is:
- Automatic (comparing the system's ledger to external sources like your bank feed)
- Quick (runs every night, you know by morning if something is wrong)
- Explainable (here's exactly which transaction caused the discrepancy)
This requires that every state-changing operation is logged and reconcilable against external data.
Separate reads from writes
Your read patterns (queries like "show me transactions for this month") are different from write patterns (creating new transactions). They have different performance requirements and consistency requirements.
Write operations need:
- Consistency (every transaction is recorded correctly)
- Durability (transaction isn't lost if the system crashes)
- Sequential ordering (transactions are in the right order)
Read operations need:
- Speed (queries return fast)
- Eventual consistency is OK (you can look at the ledger a moment later)
Design them separately:
- Write path: direct to ledger, verify before committing, return result
- Read path: query pre-computed views, updated asynchronously from ledger
This keeps write operations simple (no complex joins) and read operations fast (no waiting for full consistency).
External integration points need care
Fintech systems are never isolated. You integrate with banks, payment networks, regulatory systems.
When integrating:
- You don't control the external system's behavior or reliability
- Timeouts happen
- Data formats can be ambiguous
- Reconciliation against external data is mandatory
Design for this:
- Assume integrations will fail (build retry logic)
- Log all communication with external systems (for audit)
- Make reconciliation against external data part of normal operations
- Separate integration logic from core financial logic (so core logic stays simple)
Compliance is an architecture problem
Many organizations treat compliance (PCI, SOC2, regulatory reporting) as something bolted on after the system is built. This creates:
- Expensive retrofitting
- Systems that don't quite fit compliance requirements
- Audit findings because the system can't generate required reports
Instead, build compliance into the architecture:
- Audit trails are automatic (every transaction is logged)
- Access controls are enforced at the database level, not in application logic
- Encryption is built in from the start
- Report generation is automated from the same ledger that runs operations
The most compliant systems are the ones designed for compliance from day one.
Testing and verification
Testing financial systems is harder than testing regular software:
- You need deterministic tests (randomization hides bugs)
- You need exhaustive tests (missing one edge case is a problem)
- You need to test error paths (what happens when integration fails?)
- You need to test reconciliation (does our ledger agree with external sources?)
Good practice:
- Unit tests for individual operations
- Integration tests that verify end-to-end transactions
- Reconciliation tests that verify our state matches external data
- Chaos engineering (kill processes, simulate network failures, verify system recovers correctly)
The simplest financial systems are the ones built to be tested thoroughly from the start.