skip to content

When is a UML state machine the right way to model an object's lifecycle instead of a status field?

level: seniorimportance: should knowfreq 44%

answer

  1. Count the named conditions first
  2. Illegal moves keep reaching production
  3. Status checks scattered across many call sites
  4. History-dependent behaviour is the strongest tell
  5. The model must earn its maintenance

basics

~20 s

Model explicitly when an object has several named conditions, when the legal moves between them matter, and when the same rules are being re-checked in scattered places. Two or three values with no ordering rules need no diagram at all.

solid answer

~40 s

Reach for a state machine when three things are true: the object has a bounded set of **named behavioural conditions**, some moves between them are **illegal** and it matters, and the code currently re-checks those rules **wherever the status happens to be read**. That combination is what produces the recognisable defect class — an object reaching a condition nobody thought reachable. The model earns its place by making the legal moves a single artefact instead of an emergent property of scattered conditionals. It does not earn its place when the object simply carries a flag, when every transition is legal, or when the diagram will be drawn once and never opened again. The honest cost is maintenance: an out-of-date state machine is worse than none, because readers trust it.

go deeper

for a junior

Be able to say what an explicit lifecycle model buys over a plain status field: the legal moves are written down once instead of being re-checked wherever the status happens to be read.

for a middle

Show the evidence you would gather before arguing either way — how many states exist, how many call sites branch on the value, and which moves are supposed to be impossible.

for a senior

Expect a judgement scenario. Name the defect class the model removes, estimate what keeping it current costs, and be willing to conclude that the answer for this object is no.

for a principal

Own the strategy: whether the machine becomes the single enforced place transitions are decided, how it stays honest across teams, and when a lightweight transition table is the whole answer.

## The question behind the question An interviewer asking this is not testing whether you can draw the notation — that is the earlier question. They want to know whether you can tell a lifecycle that deserves a model from one that does not, because the failure mode in both directions is expensive. Model nothing and illegal transitions ship; model everything and you maintain a folder of diagrams that quietly drift away from the code. ## Signals that a lifecycle deserves an explicit machine - **The status values keep multiplying.** One or two values is a flag. Eleven values is a lifecycle whether anyone drew it or not. - **Some moves are illegal and it matters.** If any value can follow any other, there is no machine to draw — only a label. - **The same rules are re-checked in many places.** When dozens of call sites each ask *is it in a condition where this is allowed?*, the rules exist in the codebase as an emergent property rather than as an artefact. - **Behaviour depends on how the object got here.** History-dependent behaviour is the strongest tell: a plain field cannot express it, so people encode it in extra booleans that then need their own rules. - **Concurrent independent aspects.** Payment condition and document condition moving independently is naturally two regions and unnaturally a combinatorial set of status values. - **The recurring defect is an impossible condition.** Bug reports that begin *how did it even get into this state* are the diagnostic. ## Signals it does not - The field is genuinely binary, or a label people filter on rather than a condition that gates behaviour. - Every transition is legal, so the diagram would be a complete graph and say nothing. - The lifecycle lives entirely inside one short function, where a reader can see all of it at once. - Nobody will own the diagram. A model with no owner becomes misinformation on a schedule. ## What the model costs A state machine is not free. It has to be kept in step with the code, reviewed when transitions change, and explained to every newcomer. The cheapest honest version is not a drawing at all but a **transition table**: states down the side, events across the top, target or *illegal* in each cell. The table is small, diffable, reviewable in the same change as the code, and it is usually where the defects are found — because the empty cells are the illegal moves, and writing them down forces someone to say what happens when the event arrives anyway. ## A worked judgement call A freight-booking portal carried a booking status with eleven values, checked at 37 call sites across four modules. Over one quarter, four production defects were all the same shape: a booking reached a condition the code did not expect, and downstream logic behaved as if it were in another. The team's on-call rotation was eating roughly half its engineering capacity, so the argument for modelling could not be *it would be cleaner* — it had to be *this removes a defect class that is generating pages*. The first step taken was the transition table, not the diagram. Eleven states by nine events is 99 cells; 23 were legal, and the exercise found two moves the code permitted that the business considered impossible. Only then was the machine drawn, with `In Transit` as a composite state, and the transition rules moved to a single place every caller went through. The measure of success was not the diagram existing: it was that no defect of that shape recurred, while the median nine days a booking took to reach `Confirmed` was unchanged — the model removed illegal moves, not latency, and claiming otherwise would have been dishonest. ## Making the model pay 1. **Enumerate the states first**, using behavioural conditions and not data values. If two states have identical behaviour, they are one state with a data difference. 2. **Build the transition table** and mark the illegal cells explicitly. 3. **Route every change of state through one place**, so the table is enforced rather than documented. 4. **Write one test per illegal move you care about**, which is what keeps the table honest as the code moves. 5. **Decide the diagram's status out loud.** Either it is derived from the enforced rules and maintained, or it is a design aid that gets deleted after the conversation. The failure mode is the third option, where it is neither and everyone assumes someone else is checking. ## What a strong answer sounds like A strong candidate does not answer *yes, always model it*. They ask how many states there are, how many places branch on the value, whether any move is genuinely illegal, and who will own the artefact — and they are willing to conclude that a status field with a comment is the right call for the object in front of them.

  • What is the cheapest first step when you suspect an object's lifecycle is out of control?
    Build the transition table before drawing anything: states down the side, events across the top, target or illegal in each cell. It is small, diffable and reviewable, and filling it forces someone to say what happens when an event arrives in a condition nobody considered. Most of the defects surface at that point, and the diagram — if you still want one — becomes a rendering of a table you already trust.
  • How do you keep a state machine model from drifting out of date with the code?
    Make it the enforced source rather than a picture beside the code: route every change of state through one place that consults the transition rules, and add a test per illegal move you care about. Review the model in the same change as the behaviour it describes. If you are not willing to do that, treat it as a disposable design aid and delete it after the conversation rather than leaving stale guidance behind.

saying these in an interview costs you the question

  • Draws a state machine for every entity regardless of lifecycle complexity
  • Says a status field is always enough because the code currently works
  • Ignores that the same status rules are re-checked at many call sites
  • Treats the diagram as documentation nobody has to keep current
  • Adds states for data values rather than for behavioural conditions