Build a staged banking service: ATM cash, statements, transfers, fees, slow transfers
Company: Valon
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: medium
Interview Round: Onsite
Build a banking service in five parts, adding one capability per part. Each part comes with a sample input file of actions and the expected output, and your code should produce that output. Only the service class is evaluated; a small file-handling class exists to feed it actions and write statements.
The starting skeleton:
```python
class BankService:
"""Code to be evaluated."""
def get_current_balance(...):
pass
def deposit_cash(...):
pass
def withdraw_cash(...):
pass
class FileInterface:
"""Used for debugging and testing only. This class is not evaluated;
you may even hardcode the file paths."""
def perform_bank_actions():
# reads in the input file
# calls BankService methods
pass
def write_bank_statements():
# calls BankService methods
# writes the output file (its format depends on the current part)
pass
FileInterface.perform_bank_actions()
FileInterface.write_bank_statements()
```
The parameter lists are left for you to choose.
### Constraints and Clarifications
- You get about two hours to implement: roughly the first 30 minutes with an engineer, reviewing the problem, discussing the design and starting to code, then about 90 minutes coding alone. In a final 30 minutes a second engineer joins to walk through your code and discuss your design decisions.
- Finishing all five parts is not required, but completing at least Part 3 or Part 4 appears to be expected.
- The input file format is not reproduced here. Assume each line describes one timestamped action (its time, its kind, the account or accounts involved, and the amount), and choose a concrete format.
- Amounts are dollars and cents, printed with a dollar sign and two decimal places as in the samples. Times print as in the samples, with the hour not zero-padded.
### Clarifying Questions
- Are actions in the input file sorted by time, and must the service handle them out of order?
- Do accounts have to be opened explicitly, or does an account exist from its first action?
- Can a balance go negative, or does every debit that exceeds the balance fail?
- Is a malformed action, such as a zero or negative amount, a FAILED transaction in the statement or an input error?
- Should the output contain one statement per account, or one file for all accounts?
### Part 1 — ATM balance, deposits and withdrawals
Support ATM operations: view an account's balance, deposit cash, and withdraw cash. After processing the input actions, write each account's balance.
Sample output:
```text
account amount
PEACH1111 $5.00
PEACH2222 $10.00
PEACH3333 $15.00
```
```hint Money first
Decide how amounts are represented before writing any method. The output prints exact cents, and every later part does arithmetic on them.
```
#### What This Part Should Cover
- An exact representation of money, and formatting that reproduces the sample
- Defined behavior for a withdrawal larger than the balance and for an account never seen before
- Service logic kept separate from file reading and writing
### Part 2 — Transaction history per account, including failures
Display the transactions of each account. Track every transaction, including those that failed. Each statement row has these columns: time, account, credit, debit, status, type, and the balance after the transaction.
Sample statement:
```text
time account credit debit status type balance
2020-08-01 1:00:00 PEACH1111 $10.00 SUCCESS ATM $10.00
2020-08-01 7:00:00 PEACH1111 $5.00 SUCCESS ATM $5.00
2020-08-01 5:00:00 PEACH2222 $20.00 SUCCESS ATM $20.00
2020-08-01 6:00:00 PEACH2222 $25.00 FAILED ATM $20.00
```
The last row is a withdrawal larger than the balance: it is recorded as FAILED and leaves the balance unchanged. Rows are grouped by account and ordered by time within an account.
```hint Record it when it happens
Every column, including the balance after the action, is known at the moment the action is processed. Decide what to store at that moment rather than reconstructing it later.
```
#### What This Part Should Cover
- A transaction record that carries every statement column
- Failed attempts recorded without changing the balance
- A deterministic order within an account, including rows that share a timestamp
### Part 3 — Instant transfers between accounts
Add a function that transfers funds from one account to another:
```python
def instant_transfer(...):
pass
```
A sample statement row for one side of a transfer, with the credit and debit columns collapsed into a single amount column:
```text
time account amount status type balance
2020-08-01 6:00:00 PEACH1111 $2.00 SUCCESS INSTANT TRANSFER $7.00
```
```hint Two accounts, one action
A transfer changes two balances. Work out what must be checked before either one changes, so that a failure cannot leave money half-moved.
```
#### Clarifying Questions for this Part
- Should the transfer appear on both accounts' statements, as a debit for the sender and a credit for the receiver?
- What happens when the destination account does not exist, or is the same as the source?
#### What This Part Should Cover
- An all-or-nothing transfer: no state in which money has left one account without reaching the other
- Failure cases (insufficient funds, invalid destination) recorded consistently with Part 2
- Reuse of the debit and credit logic from earlier parts instead of a parallel code path
### Part 4 — Transfers to and from an external bank, with a fee
Support transfers to and from accounts at an external bank. Each external transfer incurs a fee, recorded as its own statement row. A sample, again showing one account's side with a single amount column:
```text
time account amount status type balance
2020-08-01 7:00:00 PEACH2222 $5.00 SUCCESS INSTANT TRANSFER $13.00
2020-08-01 7:00:00 PEACH2222 $0.01 SUCCESS FEE $12.99
```
The fee row reduces the balance, so it is a debit.
```hint The account you do not hold
One side of an external transfer has no balance in your system. Decide how that side is represented, and where in the flow the fee is computed and charged.
```
#### Clarifying Questions for this Part
- Is the fee a flat amount or a percentage of the transfer, and how is a fraction of a cent rounded?
- Is the fee charged on incoming external transfers as well as outgoing ones, and always to the account at this bank?
- If the balance covers the transfer but not the transfer plus the fee, does the whole transfer fail?
#### What This Part Should Cover
- The fee rule isolated so that it can change without touching the transfer logic
- A funds check that includes the fee, and consistent recording of the transfer and fee rows
- Distinct handling of outgoing and incoming external transfers
### Part 5 — Slow transfers
Add a function that supports slow fund transfers. Once a slow transfer is initiated, the funds are no longer usable until they reach the destination account. A sample row:
```text
time account amount status type balance
2020-08-01 6:00:00 PEACH1111 $2.00 SUCCESS SLOW TRANSFER $7.00
```
```hint Money in between
Between initiation and arrival the amount is usable by neither account. Decide where it lives during that time, and what event moves it to the destination.
```
#### Clarifying Questions for this Part
- How is the arrival time determined: given in the input, a fixed delay, or an explicit settlement action?
- Does the displayed balance include funds in transit, and should a statement show a pending state?
- Can a slow transfer be cancelled or fail after initiation, and what happens to the held funds?
- Does a slow transfer to an external bank also carry the Part 4 fee?
#### What This Part Should Cover
- A clear split between funds that are usable and funds that are committed but not yet delivered
- A time-driven settlement mechanism that processes transfers in arrival order
- Statement rows at initiation and at arrival, and later balance checks that respect the held funds
### What a Strong Answer Covers
- Working, runnable code through at least the middle parts, with output that matches the sample formats
- A design that absorbs each new part without rewriting the earlier ones
- Exact money arithmetic throughout, with no floating-point drift
- One append-only transaction record per action as the source of truth for statements and balances
- A clear walkthrough explanation of the design decisions, the assumptions made where the specification was silent, and what was deferred
### Follow-up Questions
- Two threads run opposite transfers between the same two accounts at the same time. How do you keep balances correct without deadlocking?
- The service crashes halfway through the input file and the file is replayed. How do you avoid applying actions twice?
- How would you support cancelling a slow transfer, or reversing one that the external bank rejects?
- Accounts grow to millions of transactions. How would you answer "what was this balance at a given time" efficiently?
Overview: A staged, roughly two-hour coding exercise to build a banking service: ATM balance, deposits and withdrawals, per-account statements that include failed transactions, instant transfers, external transfers that charge a fee, and slow transfers that hold funds until they arrive. It tests exact money handling, ledger design and extensible code.
Read the full Valon Software Engineer interview experience this question came from