Banking Ledgers And Cashback Operations
Asked of: Software Engineer
Last updated

What's being tested
This tests whether you can design a transactionally correct financial system where money movement is durable, auditable, idempotent, and safe under concurrency. Coinbase cares because balances, rewards, transfers, and settlement flows must remain correct even when APIs retry, workers crash, external rails disagree, or users issue simultaneous operations. The interviewer is probing for concrete backend engineering judgment: data modeling, database transactions, concurrency control, delayed operations like cashback, reconciliation, observability, and tradeoffs between strong consistency and scale.
Core knowledge
-
Double-entry ledger accounting is the canonical model for auditable money movement. Every transaction creates at least two immutable ledger entries: one debit and one credit. The invariant is per transaction, often enforced with database constraints and transactional writes.
-
Balances should usually be derived from an immutable ledger, not treated as the source of truth. A cached balance table is acceptable for reads if it is updated atomically with ledger entries or rebuilt from the ledger. The ledger answers “what happened”; the balance table answers “what is current.”
-
Idempotency keys are mandatory for APIs like
POST /transfersorPOST /cashback. Store a client-providedidempotency_keywith the request hash and final response. If the same key is retried, return the original result; if the payload differs, reject with a conflict. -
Database transactions are the simplest correctness boundary. In
Postgres, a transfer should insert ledger entries, update balance projections, and persist the transfer record in oneSERIALIZABLEor carefully designedREAD COMMITTEDtransaction with row locks. Never debit in one transaction and credit later. -
Concurrency control prevents double-spend. Common approaches are
SELECT ... FOR UPDATEon account balance rows, optimistic version checks likeUPDATE accounts SET balance = balance - ? WHERE id = ? AND balance >= ? AND version = ?, or serializable isolation. The key is making “check funds” and “deduct funds” atomic. -
Available balance and settled balance are different. A card authorization may place a hold, reducing available funds before settlement. Model states like
pending,posted,reversed, andfailed; avoid deleting rows when lifecycle transitions occur. -
External payment rails require reconciliation because your ledger and a bank/card network can diverge. Store external IDs, statuses, timestamps, and raw references. Periodic jobs compare internal expected state against external reports and create compensating entries rather than mutating history.
-
Cashback is a delayed, conditional credit. Record the triggering spend transaction, eligibility decision, reward amount, scheduled payout time, and status. The payout worker must be idempotent so a retry cannot issue duplicate rewards; use a unique constraint on
(source_transaction_id, reward_type). -
Delayed scheduling can be implemented with a durable table plus polling workers for modest scale, or with
Kafka,SQS, or a workflow engine likeTemporalfor higher scale and retries. The correctness source should be durable storage, not only an in-memory timer. -
Leaderboards for top spenders or transfer volume should not query the full ledger synchronously. Maintain an aggregation table keyed by account and time window, or use a stream consumer. For millions of accounts, use approximate or precomputed rankings; exact global rankings over raw entries become expensive.
-
Auditability means immutable append-only records, stable identifiers, actor metadata, timestamps, reason codes, and trace IDs. Corrections should be new entries, not updates to old amounts. This supports debugging, compliance review, and deterministic replay.
-
Operational safety includes alerts on ledger imbalance, negative balances, duplicate idempotency keys with different payloads, worker retry storms, reconciliation mismatches, and
p99transaction latency. A good design includes runbooks for stuck pending transfers and safe backfills.
Worked example
For “Design account system with cashback”, a strong candidate should first clarify scope: are accounts single-currency, is cashback immediate or delayed, can transfers fail, and do we need exact real-time leaderboard rankings? Reasonable assumptions might be: one currency, internal transfers only, cashback paid 24 hours after eligible outgoing transfer, and a leaderboard by total outgoing transfer amount.
The answer can be organized around four pillars. First, define APIs like createAccount, transfer, getBalance, getCashbackStatus, and getTopSpenders. Second, model the data: accounts, ledger_transactions, ledger_entries, cashback_rewards, and spend_aggregates. Third, explain the transfer transaction: lock debit account, verify available balance, insert balanced debit/credit ledger entries, update balance projections, create cashback schedule, and update spend aggregate atomically where possible. Fourth, cover async processing: a worker scans due cashback_rewards, inserts a cashback credit transaction idempotently, marks the reward paid, and retries safely after crashes.
A specific tradeoff to flag is whether cashback creation happens synchronously inside the transfer transaction or asynchronously from a transfer event. Synchronous creation is simpler and strongly consistent; asynchronous creation scales better but requires durable event processing and reconciliation to avoid missed rewards. For an interview, start with synchronous metadata creation plus async payout because it is easier to reason about and still supports delayed execution.
Close by saying that, with more time, you would add multi-currency support, reversal handling, fraud holds, external settlement reconciliation, and backfill tooling to recompute balances and leaderboards from the immutable ledger.
A second angle
For “Design a bank account ledger”, the emphasis shifts away from cashback scheduling and toward the ledger as the system of record. The strongest framing is to separate the journal transaction from individual ledger entries, enforce the zero-sum invariant, and make all corrections append-only. You would spend more time on transaction isolation, schema constraints, replayability, and reconciliation with external systems. The same principles still apply: idempotent writes, atomic debits and credits, immutable history, and derived balance projections. The difference is that rewards and rankings are optional consumers of the ledger, not the center of the design.
Common pitfalls
Pitfall: Treating
accounts.balanceas the only source of truth.
A tempting answer is to update two account rows directly: subtract from Alice, add to Bob, and call it done. That misses auditability, replay, correction, and reconciliation. A better answer uses immutable ledger entries as the source of truth and treats balances as projections optimized for reads.
Pitfall: Saying “use a queue” without explaining correctness.
A queue can help with cashback payouts or aggregation, but it does not automatically guarantee exactly-once money movement. Interviewers want to hear how duplicate messages are handled: idempotency keys, unique constraints, transaction records, status transitions, and retry-safe workers.
Pitfall: Over-indexing on scale before correctness.
Sharding, caching, and streaming are useful, but a financial system that scales incorrect balances is a failed design. Start with a single-region transactional database and clear invariants, then discuss how to partition by account, archive historical entries, or move read-heavy analytics to derived stores.
Connections
The interviewer may pivot into distributed transactions, event-driven architecture, database isolation levels, or reconciliation workflows. They may also ask for a coding-style in-memory version, where the same ideas become data structures, ordering rules, scheduled events, and deterministic edge-case handling.
Further reading
-
Designing Data-Intensive Applications — Excellent grounding in transactions, isolation, logs, replication, and derived data systems.
-
Stripe API Idempotent Requests — Practical reference for idempotency-key behavior in payment-style APIs.
-
Martin Fowler: Accounting Patterns — Useful conceptual model for entries, accounts, and immutable financial events.
Featured in interview prep guides
Practice questions
- Design an in-memory banking systemCoinbase · Software Engineer · Take-home Project · medium
- Implement banking ops: transfer, top-k, cashback, mergeCoinbase · Software Engineer · Take-home Project · Medium
- Implement a Simple Banking LedgerCoinbase · Software Engineer · Onsite · Medium
- Design a bank account ledgerCoinbase · Software Engineer · Take-home Project · hard
- Design scheduled payments and cancellationCoinbase · Software Engineer · Take-home Project · hard
- Design a bank account serviceCoinbase · Software Engineer · Take-home Project · hard
- Design a scheduled payments serviceCoinbase · Software Engineer · Take-home Project · hard
- Design a basic banking systemCoinbase · Software Engineer · Onsite · hard
- Implement bank balance operationsCoinbase · Software Engineer · Technical Screen · Medium
- Design account system with cashbackCoinbase · Software Engineer · Take-home Project · medium
- Implement bank account with cashbackCoinbase · Software Engineer · Technical Screen · Medium
Related concepts
- Wallets, Payments, And Refund LedgersSystem Design
- Payment Processing And Ledger SystemsSystem Design
- Distributed System Design For Ledgers And CountersSystem Design
- Payment Systems: Ledgers, Idempotency, and Reconciliation
- Money-Safe Financial ComputationCoding & Algorithms
- Donation And Payment PlatformsSystem Design