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

Read the full interview experience this question came from →

Quick 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.

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

|Home/Software Engineering Fundamentals/Valon
Valon logo
Valon
Jan 16, 2026
mediumSoftware EngineerOnsiteSoftware Engineering Fundamentals
1
0

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 Guidance

  • 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 Guidance

  • 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 Guidance

  • 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 Guidance

  • 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 Guidance

  • 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 Guidance

  • 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 Guidance

  • 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 Guidance

  • 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 Guidance

  • 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 Guidance

  • 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 Guidance

  • 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?
Loading comments...