Design banking with scheduled transfers and merges
Company: Klaviyo
Role: Software Engineer
Category: System Design
Difficulty: hard
Interview Round: Take-home Project
Design and implement an in-memory banking system with scheduled payments and account merges. Provide these APIs and semantics:
1) transfer(timestamp, source_account_id, target_account_id, amount) -> str | None
- At 'timestamp', deduct 'amount' from the source account’s available balance and freeze it until the target accepts or the transfer expires exactly 24 hours after initiation (upon expiry, funds automatically return to the source and the transfer can no longer be accepted).
- If source == target, either account does not exist, or the source has insufficient available balance (excluding already frozen funds), return None.
- On success, return a unique transfer id like 'transfer1', 'transfer2'.
- Frozen funds contribute to both parties’ pending (unsettled) totals and do not enter either account’s transaction history until accepted.
2) accept_transfer(timestamp, account_id, transfer_id) -> bool
- Return True iff the transfer exists, is not expired, not already accepted, and 'account_id' is exactly the transfer’s target; otherwise return False.
3) merge_accounts(timestamp, account_id_1, account_id_
2) -> bool
- At 'timestamp', merge 'account_id_2' into 'account_id_1'. If the IDs are equal or either account does not exist, return False.
- Cancel all pending transfers originating from 'account_id_2'; also cancel all pending transfers from 'account_id_1' to 'account_id_2'.
- Redirect all other pending inbound transfers whose target is 'account_id_2' so their target becomes 'account_id_1'.
- Increase 'account_id_1' balance by 'account_id_2' balance; aggregate both accounts’ historical totals as combined; delete 'account_id_2'; return True.
4) get_balance(timestamp, account_id, time_at) -> int | None
- Return the balance of 'account_id' immediately after processing all operations at 'time_at'. If the account did not yet exist by 'time_at' or had been deleted by then, return None; otherwise return the correct historical balance.
Constraints and expectations:
- Amounts and balances are integers; timestamps are integer units; 24 hours means exactly 24*60*60 units after initiation; acceptance at or after the expiry moment is invalid.
- Operations may arrive in any order of 'timestamp'; you must answer historical queries correctly for arbitrary 'time_at'.
- Describe the data structures/algorithms (e.g., hash maps, event log/ledger, min-heap or timer wheel for expirations, indices for per-account state, and any persistence/persistence-like strategy for time travel), their time/space complexity, and how you would generate unique IDs.
- Provide a working single-threaded in-memory implementation and justify design choices; briefly discuss how you would extend to handle concurrency and scale.
Quick Answer: This question evaluates system design and implementation skills for stateful in-memory services, covering temporal event handling, transfer semantics with freezes and expirations, account merges, and historical (time-travel) consistency; it is in the System Design domain and emphasizes practical application with design-level conceptual considerations. Interviews commonly include this problem to assess the ability to model time-dependent state and correctness under out-of-order operations and expirations, reason about data structures, indices and their time/space complexity, and articulate scalability and concurrency trade-offs starting from a single-threaded baseline.