A practice prompt we wrote. No company or candidate report names it, so it carries no company tag.

How to answer

Show the business rules as data, and errors that tell a caller what to do instead. Open with the diagram, not the class.

  1. Draw the states and events first. Pending, paid, shipped, delivered, canceled, refunded; mark the terminal ones. Then ask the questions that are really business decisions: is canceling a shipped order a return? Each answer is a row in your table.
  2. Make the table the code. A mapping from (state, event) to the next state, and one apply function that looks up the pair; no if chain per handler. The table documents the rules and drives the tests.
  3. Make the illegal case useful. Raise an error naming the order, state, event and the events allowed from here: Cannot ship order 812 in state pending; allowed: pay, cancel. Over HTTP it is a 409 Conflict (RFC 9110, section 15.5.10) with the current state in the body.
  4. Decide what a repeated event means. A retried webhook resends a payment, sometimes after the order has shipped. Dedupe on the event’s id, stored with the transition, not on the current state: a repeat returns the recorded result, whatever the state is now. A second, different payment on a paid order is a double charge to raise, not a no-op.
  5. Name what the single-process version hides. In a database, a transition is a conditional update that sets the new state only where the old one still holds, UPDATE orders SET state = 'shipped' WHERE id = 812 AND state = 'paid', and zero rows changed is a conflict. Record each transition with a time and an actor. Write side effects, such as the confirmation email, to an outbox in the same transaction and send after commit, so a failed send retries and a rollback sends nothing.

The trap is testing only the happy path: loop over every state and event and assert that each pair missing from the table raises.

Follow-ups

What the interviewer may ask next, once your first answer is on the table.

  • A payment webhook delivers the same “paid” event twice, and the second arrives after the order has shipped. What does your code return, and how does it tell that apart from a second real payment?
  • A cancel and a ship request reach two servers at the same moment. What stops both from succeeding?
  • Product adds a partially shipped state. What changes in your code, and what in your tests?
  • Where does sending the confirmation email go relative to the state change, and what if it fails?

Where answers go wrong

  • Boolean flags such as ispaid and isshipped instead of one state field, so an order can be canceled and shipped at once.
  • Transition checks spread across handlers as if and else branches, so the allowed moves are written down nowhere and no test can list them.
  • Reading the current state, checking it in memory and writing the new one without a conditional update, so two concurrent requests both pass the check.

Answer this in two minutes

Write the answer you would say out loud. The clock starts with your first word.

Two minutes

Model answer

“Before any code, the diagram, and two questions for product, because each answer is a row. Can a customer cancel after paying?