In-Memory Banking System: Deposits, Transfers, Top-N Activity, Test Harness
Capital OneSoftware Engineer · Technical ScreenLast updated
An object-oriented coding exercise that builds an in-memory banking system in stages: creating accounts and deposits, transfers between accounts, and ranking the top N accounts by activity. It also tests writing a harness that runs string-array test commands, and confirming whether activity means operation count or amount moved.
Implement an in-memory banking system as a class. The interviewer builds it up in three steps, then tests it by feeding in lists of string arrays, one array per operation. You therefore also need a small harness that parses each operation, calls your class and produces the output. In this round the harness can take as long as the class itself, so plan time for it.
Constraints and Clarifications
- All state is in memory, and calls are single-threaded.
- Account IDs are non-empty strings. Assume amounts are whole numbers in the smallest currency unit, so no floating-point money arithmetic is needed.
- The command names and output format come from the interviewer's tests. The ones used below are illustrative.
Clarifying Questions
- What should each operation return on success, and on failure?
- Can a deposit or transfer amount be zero or negative?
- May an account transfer money to itself?
Part 1 — Create accounts and deposit
Implement create_account(account_id), which creates an account with balance 0 and fails if the account already exists, and deposit(account_id, amount), which adds money to an existing account and returns the new balance, and fails if the account does not exist.
What This Part Should Cover
- A clear data model for accounts
- Explicit results for duplicate accounts, missing accounts and invalid amounts
- Constant-time operations
Part 2 — Transfer
Add transfer(source_id, target_id, amount), which moves money between two existing accounts and returns the source account's new balance. It fails if either account is missing or the source does not hold enough money.
What This Part Should Cover
- Every failure case checked before any balance changes
- The self-transfer case
- Extending the Part 1 code without rewriting it
Part 3 — Top accounts by activity
Add top_activity(n), which returns the n accounts with the most activity, most active first.
Clarifying Questions for this Part
- Is activity the number of operations on an account, or the total amount of money moved?
- Do both sides of a transfer count? Do failed operations or account creation count?
- How are ties ordered, and what is returned when
nis larger than the number of accounts?
What This Part Should Cover
- A confirmed definition of activity, maintained incrementally
- A deterministic tie-break, and the case where
nexceeds the number of accounts - Query cost relative to the number of accounts and to
n
Part 4 — Run the interviewer's tests
Each test is a list of operations, and each operation is an array of strings whose first element names the command. Write a harness that runs a test against a fresh instance of your class and produces one output string per operation, in order, so the outputs can be compared with the expected ones. A test might look like this:
[
["CREATE_ACCOUNT", "acc1"],
["CREATE_ACCOUNT", "acc2"],
["DEPOSIT", "acc1", "500"],
["TRANSFER", "acc1", "acc2", "200"],
["TOP_ACTIVITY", "2"]
]
What This Part Should Cover
- Parsing and dispatch that are easy to extend with new commands
- Consistent formatting of results, including failures
- Mismatch reports that name the failing operation
Hints
Hint 1
Pick the core state: Decide what you keep per account so that the later steps, transfers and an activity ranking, can be added without changing how accounts are stored.
Hint 2
Validate before mutating: Make sure a failed transfer leaves both balances exactly as they were.
Hint 3
Keep the ranking cheap: Decide what to update on every deposit and transfer so that the query never replays history, and how the query's cost grows with the number of accounts compared with `n`.
Hint 4
Separate parsing from logic: Keep string handling out of the class. Let the harness convert arguments, dispatch on the command name and format each result, and decide what it does with an unknown command or a malformed number.
Follow-up questions
- How would you add a transaction history, so that a balance can be queried as of an earlier point in time?
- If several threads called `transfer` at once, how would you keep balances correct, and avoid deadlock when two transfers involve the same pair of accounts in opposite directions?
- If activity had to cover only a recent sliding time window, what would you store, and how would the ranking change?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.