Tier-1 banking · 12 yrs · KYC/AML and workflow transformation
Arik spent 12 years in tier-1 banking, leading compliance and workflow transformation programs. He built Rust Finance, a full-stack retail banking app on Axum, Tokio and SQLx against Postgres, around the properties that matter in a regulated domain: transfers that cannot race, a ledger that can prove every balance, and a database that rejects invalid state on its own.
The app ships with role-based access, PDF invoices, savings goals, a Tauri v2 shell for desktop and mobile, and a tracing layer that persists server errors as bug reports. Alongside it, he built CSharp-to-Rust-Assistant, an LLM translation pipeline where the Rust compiler, not the model, decides what is done.
12 yrs
Tier-1 banking, compliance and workflow transformation
KYC/AML
Compliance programs led before moving into engineering
6-10
Engineers and analysts led on banking transformation teams
EMBA
Executive MBA, business and engineering in one profile
LinkedIn rebranding
Feature 01
Feature 02
Feature 03
The challenge
A banking app has to be correct, not just working
Most demo banking apps store a balance column and update it in place. That works until two transfers hit the same account at once, or a failed write leaves money debited but never credited. Arik wanted the app to hold the guarantees a compliance team would ask for: atomic transfers, an auditable trail, and balances that can be checked against their history.
That meant pushing correctness down into Postgres itself, with locks, constraints and triggers, and keeping the Rust layer typed end to end so every failure mode is an explicit variant.
•Concurrent transfers must never overdraw an account
•Every balance must be explainable from ledger entries
•The API must not reveal which accounts exist
The outcome
Rust Finance: a ledger that can prove its balances
Arik shipped Rust Finance behind Docker, Nginx and GitHub Actions CI/CD: row-locked transfers, a double-entry ledger in exact decimals, and a drift check that reconciles cached balances against it.
His CSharp-to-Rust-Assistant adds the other half of the profile: an LLM pipeline gated by cargo check, aimed at teams migrating legacy .NET code to Rust.
✓Transfers serialized by FOR UPDATE locks in one transaction
✓Double-entry ledger with SQL-level drift reconciliation
✓Compiler-verified C# to Rust translation loop
[ Transfers ]
Money moves inside one transaction, under row locks
A transfer is a single Postgres transaction that takes SELECT ... FOR UPDATE locks on the source and destination accounts before reading a balance. Two concurrent transfers out of the same account serialize on the lock instead of both passing the funds check.
Failures are a typed TransferError (InsufficientFunds, AccountNotFound, RecipientNotFound, SameAccount, Db), so the handler maps each one to a precise response. An account owned by someone else returns AccountNotFound, never a permission error, so the API cannot be used to probe which account numbers exist.
[ Ledger ]
Balances are derived from a double-entry ledger, then reconciled
Every transfer writes a transaction row plus a CREDIT and a DEBIT entry in ledger_entries, stored as NUMERIC(19,4) and handled as rust_decimal on the Rust side. The ledger is the source of truth, an account_balances view computes balances from it.
The accounts table still caches a balance for fast reads, so Arik added a check_balance_drift() SQL function that compares the cached value with the ledger-computed one. Drift becomes a query you can run and alert on, the same reconciliation discipline a bank applies at end of day.
[ Invariants ]
The database refuses bad state, not just the handlers
Roles, account types, transaction types and statuses are guarded by CHECK constraints, emails must be lowercase, and a partial unique index enforces uniqueness only across active accounts. A buggy handler or a manual SQL session hits the same wall.
updated_at is set by a trigger rather than in application code, because a query that forgets to touch it would otherwise fail silently. Each domain has #[sqlx::test] integration tests running against real migrations, and ts-rs exports the Rust types to the TanStack frontend so the contract cannot drift.
[ AI tooling ]
An LLM translator that the compiler grades
CSharp-to-Rust-Assistant parses C# with tree-sitter one declaration at a time, lets an LLM translate each unit, and runs cargo check on the output. Compiler errors are fed back to the model until the code compiles, so nothing is trusted on the model's word.
Translating per class, struct, enum or interface keeps each prompt small and each failure local. The repair loop is capped at MAX_REPAIRS so a unit that never converges stops instead of burning tokens, and a built-in mock translator lets the whole pipeline run offline without an API key.
The journey
One clear path through the build
Step 011 / 5
Schema first
Money as NUMERIC(19,4) and rust_decimal, enums and emails guarded by CHECK constraints.
Step 022 / 5
Transfers
One Postgres transaction, FOR UPDATE locks on both accounts, typed errors.
Step 033 / 5
Ledger
Every transfer writes CREDIT and DEBIT entries, balances derived from the ledger.
Step 044 / 5
Reconciliation
check_balance_drift() compares cached balances against the ledger.
Step 055 / 5
Release
Rust Finance behind Docker, Nginx and CI/CD, Tauri v2 shell, C# to Rust assistant.
Drag to explore · Select a milestone to read the full story