Build a bank ledger

One spec extended twice while every earlier test keeps passing: accounts and transfers, then the biggest spenders, then payments scheduled for later and cancelled before they run.

Should a failure raise an exception or come back as False?

Both, in different places. Inside the rules, a refusal raises a named exception, because the code that noticed the problem is rarely the code that knows what to do about it, and an exception carries the reason in its type. At the surface the spec usually wants a value, so one thin layer turns each exception into the False or None the caller was promised. Writing it that way means the reporting style is four lines in one place rather than a convention spread over every method, and when the interviewer asks for the other style in a follow-up, those four lines are the whole change.

Why keep money in integer cents rather than floats?

Because a float cannot hold a tenth exactly. Add 0.1 ten times in Python and you get 0.9999999999999999, which is not a dollar, and a balance that is out by a fraction of a cent will not reconcile with anything. Integers of the smallest unit you handle, usually cents, are exact under addition and subtraction, they compare exactly, and the only place you divide is when you display them. Say this out loud when you choose the type: it takes five seconds and it is the kind of thing a reviewer of financial code checks first.

How do you cancel work that is already sitting in a heap?

You do not take it out. A heap gives you the smallest item cheaply and nothing else cheaply, so removing an arbitrary entry means a linear search and a repair. Instead keep the truth in a dictionary of the payments still wanted, and let a cancelled entry stay in the heap until it surfaces, at which point the lookup fails and the entry is thrown away. That is called lazy deletion: the cancel costs one dictionary pop, and the only price is a heap that can hold entries that will never run.

The interviewer extended the spec and my whole file changed. What went wrong?

Usually one of two things. Either the state was spread over several dictionaries keyed by the same id, so a new fact about an account meant a new dictionary and a new place to keep in step, or the operations each did their own validating and their own bookkeeping, so a new rule had to be written into every one of them. Put one object per entity and one place where each kind of change happens, and the next requirement lands as an addition. On this page stage two adds 55 lines and removes none, and stage three adds 131 and removes none.

Do I have to validate the timestamps if the spec says they increase?

You do not have to, and saying what you would do costs nothing. A documented promise from the caller is worth one guard clause, because the cost is a comparison and the alternative is a balance that silently depends on the order calls arrived in. It also gives you somewhere to put the question in minute two: ask whether two operations can share a timestamp, and whether a payment due at exactly this instant has already happened. Both answers turn into one character of code and one test.