What Building Fintech Taught Me About Correctness
At LXME — India's first financial platform built for women — I worked as a Java/Spring Boot engineer on services that touched real money. It was my first time building fintech, and it permanently changed how I judge a piece of backend code.
In most domains, a bug is an inconvenience. In fintech, a bug is someone's balance being wrong. That difference in stakes reshapes everything.
Money is never a double
This is the first thing any fintech engineer learns, and it's worth repeating: floating-point arithmetic is not for money.
0.1 + 0.2 is not 0.3 in binary floating point. Over millions of transactions that drift becomes real discrepancies. Use integer minor units (paise) or a proper decimal type — never double, never float.
// Wrong
double balance = 100.10;
balance += 0.20; // 100.30000000000001
// Right
BigDecimal balance = new BigDecimal("100.10");
balance = balance.add(new BigDecimal("0.20")); // 100.30Idempotency is not optional
Networks retry. Clients double-submit. A mobile app on a flaky connection will happily send the same investment request twice. If your endpoint isn't idempotent, you've just debited someone twice.
The pattern that saved us repeatedly: an idempotency key supplied by the client, stored alongside the operation, so a retry returns the original result instead of repeating the side effect.
@PostMapping("/transactions")
public ResponseEntity<Transaction> create(
@RequestHeader("Idempotency-Key") String key,
@RequestBody TransactionRequest request) {
return idempotencyStore.find(key)
.map(existing -> ResponseEntity.ok(existing))
.orElseGet(() -> {
Transaction created = service.process(request);
idempotencyStore.save(key, created);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
});
}Transactions must be boring
In fintech, the transaction boundary is sacred. Either the whole operation commits, or none of it does. Partial writes are how you get money that exists in two places at once — or nowhere.
@Transactional in Spring made this easy to apply, but the discipline was in deciding where the boundary goes. Too broad and you hold locks longer than needed; too narrow and you can crash between two writes that belonged together.
Audit everything, mutate nothing
Money systems are append-mostly. Instead of updating a balance column in place, prefer recording immutable ledger entries and deriving the balance. It costs storage, but it buys you something priceless: a complete, replayable history of how every rupee got where it is.
The mindset shift
Before fintech I optimised for shipping fast. After fintech I optimise for shipping correctly, then fast. The two aren't enemies — but when they conflict, correctness wins, every time.
The habits I carried from LXME into my .NET services at CarWale are simple:
- Make side effects explicit and idempotent.
- Make money a decimal, never a float.
- Make transactions atomic or fail loudly.
- Make history append-only.
Clever code impresses reviewers. Correct code protects users.
Six months of fintech in Java and Spring Boot was worth more than any tutorial — because the platform made the cost of being wrong visible.