Implement a Time-Aware Banking System
Company: Airbnb
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: hard
Interview Round: Take-home Project
## Implement a Time-Aware Banking System
Design an in-memory banking component in four increments. It must create accounts, accept deposits, transfer funds, rank accounts by outgoing spend, schedule and cancel future payments, merge accounts, and answer historical-balance queries.
The original exercise leaves several ordering and merge rules open. State a deterministic contract before implementing them. The following assumptions form one valid practice contract; call out any rule you would change in a real requirements discussion.
### Constraints & Assumptions
- Account IDs are stable strings, and monetary amounts are positive integers in minor currency units.
- Every operation has a nondecreasing logical timestamp. Before handling an operation at time `t`, the system processes every scheduled payment due at or before `t`.
- A deposit, transfer, or scheduled payment changes state only if the whole operation succeeds. Insufficient funds must not create a partial debit.
- Outgoing-spend totals include successful transfers and successful scheduled payments, but not rejected or canceled payments.
- Scheduled payments due at the same timestamp use a deterministic creation order.
- The merge contract must define which account ID survives and how balances, spend totals, pending payments, and historical queries are carried forward.
### Clarifying Questions to Ask
- What should duplicate account creation and references to missing accounts return?
- May a transfer send money to the same account, and does it count as outgoing spend?
- If a scheduled payment lacks funds at its due time, is it skipped permanently or retried?
- Does cancellation at the exact due timestamp happen before or after settlement?
- After a merge, may callers use the retired account ID, and what should its historical balance mean?
### Part 1 — Accounts, Deposits, and Transfers
Define the public operations and internal state for creating an account, depositing money, and transferring money between two accounts. Preserve atomicity and deterministic error behavior.
#### What This Part Should Cover
- Account existence and duplicate-creation rules.
- Balance validation and an all-or-nothing transfer.
- A consistent return contract for success and failure.
- Invariants for conservation of money during transfers.
```hint Keep validation ahead of mutation
List every condition that can reject a transfer, then change both balances only after all conditions pass.
```
### Part 2 — Rank the Top Spenders
Add an operation that returns the top `N` accounts by cumulative successful outgoing spend. Define the tie-breaker and explain whether ranking is computed on demand or maintained incrementally.
#### What This Part Should Cover
- Exactly which operations contribute to outgoing spend.
- Descending spend order with a stable account-ID tie-breaker.
- Behavior when `N` exceeds the number of active accounts.
- The update-versus-query cost of the chosen index.
```hint Treat ranking as derived state
The balance answers how much remains; a separate counter is needed to answer how much successfully left the account.
```
### Part 3 — Schedule and Cancel Payments
Support creation of a payment for a future timestamp and cancellation by payment ID. Add one settlement routine that earlier operations call so due work is applied before they inspect or mutate account state.
#### What This Part Should Cover
- Unique payment identity, due-time ordering, and cancellation state.
- Efficient discovery of due payments without scanning every scheduled item.
- The insufficient-funds rule at execution time.
- Why every timestamped public operation shares the same settlement boundary.
```hint Centralize time advancement
If each public method handles due work differently, two calls at the same logical time can observe inconsistent balances.
```
### Part 4 — Merge Accounts and Query Historical Balances
Add account merging and a query for an account's balance at a specified historical time. Explain how a merge affects active identity, pending payments, outgoing-spend ranking, and history on both sides of the merge.
#### What This Part Should Cover
- A precise surviving-account and retired-account policy.
- No loss or double counting of balance and outgoing spend.
- Rebinding or otherwise resolving pending scheduled payments.
- An append-only history representation and an efficient as-of lookup.
```hint Separate current identity from past events
A merge changes which account is active now, but it should not rewrite the timestamps at which earlier balance changes occurred.
```
### What a Strong Answer Covers
- Explicit contracts for every ambiguous ordering, failure, cancellation, and merge case.
- Atomic balance changes and one consistent mechanism for settling due work.
- Correct derived spend totals, deterministic ranking, and time-indexed history.
- Complexity analysis and tests spanning multiple levels rather than testing each feature in isolation.
### Follow-up Questions
1. How would you make settlement safe if two operations with the same timestamp execute concurrently?
2. How could top-spender queries avoid sorting every account on every request?
3. What test proves that a canceled payment never affects either balance or spend totals?
4. How would you persist the system so a crash during settlement cannot apply one payment twice?
Quick Answer: Build an incremental in-memory banking system with accounts, transfers, spender rankings, scheduled payments, cancellation, merging, and historical balances. Across all levels, the design must preserve deterministic ordering, atomic money movement, settlement boundaries, idempotency, derived state, pending-work rebinding, as-of history, and cross-level tests.