Fintech Engineering Handbook
Fintech Engineering Handbook
Author: Voytek Pitula
Last Updated: June 27, 2026
Welcome to the Fintech Engineering Handbook. This guide is designed to outline the essential architectural patterns required when building software where the primary objective is the movement and management of money.
Depending on your needs, you can read this as a comprehensive manual or jump to specific sections to solve an immediate technical hurdle.
Goals of this Handbook
- Onboarding: Help engineers understand the domain and the patterns that ensure financial systems remain trustworthy.
- Reference: Provide a shared technical vocabulary and a go-to resource for common problems.
- Education: Explain the fundamental differences between "standard" software engineering and engineering for money.
[!IMPORTANT] This is a living document. Contributions and updates are encouraged to keep the patterns current.
Core Principles
Every pattern described in this handbook serves one of these three foundational pillars:
- No Invented Data: Money cannot be created from nothing. We must prevent duplicates or arbitrary balance adjustments.
- Enforcement: Use idempotency, deduplication, and reconciliation.
- Total Traceability: Every single movement of money must be recorded and persisted.
- Enforcement: Use full precision, at-least-once delivery, event sourcing, audit trails, and immutability.
- Zero Trust: Do not trust external APIs, internal microservices, or the general state of the world.
- Enforcement: Verify all webhooks, cross-reference data across multiple sources, and ensure the system fails loudly when assumptions are violated.
Representing Money
Before money can be transferred or logged, it must be modeled. Decisions made at this layer are critical; any error here propagates through every subsequent layer of the application.
Precision Handling
Choosing how to represent monetary values is a foundational decision.
| Method | Pros | Cons | Best Use Case |
|---|---|---|---|
| Fast, memory efficient | Unpredictable precision loss | Never use for money | |
BigDecimal | Precise control, predictable rounding | Slower, requires specific libraries | Intermediate FX or pricing math |
| Integers | Fast, no precision loss, simple | Requires tracking the "smallest unit" | Final storage of fiat/crypto |
The Integer Approach
For most fiat currencies, it is best to store values as integers in the smallest possible unit. For example, €12.34 is stored as 1234.
- Standard: Refer to ISO 4217 for the number of decimals (do not assume it is always 2).
- Crypto: Uses the same logic (e.g., satoshis for BTC, wei for ETH). However, crypto has two unique challenges:
- Precision is defined by the token (e.g., an ERC-20 token might have 18 decimals).
- Values often exceed 64-bit integer limits, requiring arbitrary-width integers.
Hybrid Strategies
Storage and computation are separate decisions. A common pattern is:
Integer Storage BigDecimal for Calculation Integer Storage
Serialization and Data Transfer
When sending money over the wire (e.g., via JSON), avoid using raw numbers. Most JSON parsers treat numbers as IEEE-754 doubles, which reintroduces the floating-point problem.
Correct ways to serialize money:
- As a string:
"12.34" - As an integer in the smallest unit:
1234
{
"transaction_id": "tx_123",
"amount": "1234",
"currency": "EUR",
"precision": 2
}
The Art of Rounding
Rounding must be explicit. It should occur during division, currency conversion, fee application, or when shifting between precisions.
- Conservative (Round Down): Used to ensure you don't spend money you don't have.
- Statistical (Half-Even): Used to minimize cumulative bias over many transactions.
Warning: Rounding can lead to the "sum of parts" problem. If a total is split and each part is rounded, the sum of those parts may not equal the original total: This often requires explicit handling (e.g., assigning the remaining penny to the last recipient).
Currency Handling
Money is not just a number; it is a pair of (Value, Currency).
Implementation Best Practices
- Use a
Moneynewtype (a dedicated struct or class) to prevent mixing values. - Prohibit the addition of two different currencies (e.g.,
USD + EURshould throw an error). - Ensure conversions are explicit and use a strictly controlled exchange rate.
- Validate all currency codes at the system boundary.
Fiat vs. Crypto Identifiers
While fiat currencies use unique codes (USD, GBP), cryptocurrencies require more detail:
- Fiat:
CurrencyCode - Crypto:
(Network, ContractAddress)
Note: Pegged, wrapped, or bridged assets are not identical to the underlying asset. Treating them as interchangeable effectively "creates value" out of thin air, violating our core principles.
FX Rates
Foreign Exchange (FX) rates facilitate the conversion between currencies.
Key Nuances
- Non-Invertibility: The rate for
EUR/USDis not simply the inverse ofUSD/EUR. - The Spread: In real markets, there is a bid/ask spread (the difference between the buying and selling price).
Rate Types
- Current-time Rate: Used for real-time valuation of holdings. This is typically not stored but derived from the result and original amounts.
- Stored Rate: A specific rate locked in at the time of a transaction for audit purposes.