Bank Account Simulation Class With Self-Written Tests at 90% Coverage
Company: Shopify
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: medium
Interview Round: Technical Screen
In a pair-programming round you receive a small repository, clone it, and work in your own IDE while sharing your screen. The task is to write a class that simulates a banking system's account operations. You also write your own automated tests, which must reach at least 90% code coverage. If time remains after all requirements are implemented, the interviewer picks one bonus requirement, and you extend your existing code to meet it.
The full requirement list comes with the repository and is not reproduced here. For practice, assume this baseline for an in-memory bank:
- open an account with a unique id and a zero balance;
- deposit a positive amount into an account;
- withdraw a positive amount from an account;
- transfer a positive amount from one account to another;
- query an account's balance.
### Clarifying Questions
- What should happen on a withdrawal or transfer that exceeds the balance: reject it, or allow an overdraft up to some limit?
- Should failures be reported by raising exceptions or by returning a status value?
- How should money be represented: integer minor units, a decimal type, or floating point?
- Is a transfer to the same account, or to or from an unknown account, an error?
- Which language and test framework does the repository use, and is coverage measured by line or by branch?
### Part 1 — Implement the Account Class
Implement the baseline operations with clear invariants, in code you would be comfortable extending under time pressure.
```hint Protect the invariants first
List what must always be true of the bank's state, such as balances never becoming invalid and a failed transfer changing nothing. Then make every operation validate its inputs before it mutates anything.
```
#### What This Part Should Cover
- Money representation and input validation.
- All-or-nothing behavior for transfers and consistent error reporting.
- Readable structure that leaves room for a later requirement.
### Part 2 — Test to at Least 90% Coverage
Write automated tests for your class and show that they reach at least 90% coverage.
```hint Let the error paths drive coverage
The branches a happy-path test never executes are mostly the validation and failure paths. Plan one test per rule in your invariants, and read the coverage report's list of missed lines rather than only its percentage.
```
#### What This Part Should Cover
- Tests for the normal flow, every rejection path, and the state after a failed operation.
- How coverage is measured and reported, and what the number does and does not prove.
- Test structure that stays fast and readable during a live session.
### Part 3 — Extend With a Bonus Requirement
The interviewer picks one bonus requirement, which you must implement on top of your existing code. The bonus options are not listed here. For practice, assume the chosen requirement is a per-account transaction history that records every successful deposit, withdrawal, and transfer and can be queried in order.
```hint Change the write paths in one place
Look for the single point where every successful balance change already passes, and consider recording history there instead of in each public method.
```
#### What This Part Should Cover
- Integrating the new requirement without breaking existing behavior or tests.
- What each history record contains, including how a transfer appears on both accounts.
- Additional tests that keep coverage at or above the target.
### What a Strong Answer Covers
- Correct, validated operations whose failures leave the state unchanged.
- A test suite that verifies behavior rather than only executing lines, with the coverage target met and demonstrated.
- Incremental, communicative work suited to a screen-shared session: small steps, frequent test runs, and stated trade-offs.
- An extension that fits the existing design instead of being bolted onto every method.
### Follow-up Questions
1. How would you make transfers safe if several threads call the bank at the same time?
2. Your suite reaches 95% line coverage, but a bug remains in the overdraft check. How could that happen, and what testing technique would catch it?
3. How would you persist the accounts and history so that a crash in the middle of a transfer cannot lose or duplicate money?
Overview: A pair-programming exercise: implement a class that simulates bank account operations, write your own tests reaching at least 90% coverage, and then extend the working code with a new requirement. It tests invariant-driven validation, all-or-nothing transfers, meaningful test design, and clean incremental changes.