Katkat

Build a staged banking service: ATM cash, statements, transfers, fees, slow transfers

ValonSoftware Engineer · Onsite

Last updated

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.

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:

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:

account amount
PEACH1111 $5.00
PEACH2222 $10.00
PEACH3333 $15.00

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:

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.

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:

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:

time account amount status type balance
2020-08-01 6:00:00 PEACH1111 $2.00 SUCCESS INSTANT TRANSFER $7.00

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:

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.

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:

time account amount status type balance
2020-08-01 6:00:00 PEACH1111 $2.00 SUCCESS SLOW TRANSFER $7.00

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

Hints

Hint 1

Money first: Decide how amounts are represented before writing any method. The output prints exact cents, and every later part does arithmetic on them.

Hint 2

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.

Hint 3

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.

Hint 4

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.

Hint 5

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.

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?