Drawing a UML statechart, one question at a time

Seven versions of the same statechart for one Order object, each one a question asked of the client — and why the flawed ones are the point.

Statecharts are one of the more interesting UML diagrams, and one of the more misunderstood: they model a single object and the states it can be in. Plenty of resources, including good ones, blur statecharts and activity diagrams together. A statechart is about one thing changing state. An activity diagram is about work flowing.

This is my train of thought while drawing one, in order, including the parts that turn out to be wrong. If you would rather see the finished thing, skip to the last diagram.

The example is an Order object in an e-commerce system, because everyone has met one.

Asking the client is the part of the process people skip. So we ask:

  • When a customer places an order, what happens? — Placed
  • What if there is nothing in stock? — Pending

Two questions, two states. Nothing about how the object moves yet.

Placed Pending
Version 1: two states and no reasons.

Now the interesting part: how does the object move from one state to another? Back to the client.

  • What moves an order from placed to pending? Is it automatic, a stock check, or does someone press something?
  • Does the customer pay before or after it moves?

One of those answers gives us our first transition label.

Placed Pending Payment is made
Version 2: one transition gets a reason. The rest still have none.

We are not done, because the answers to the next questions are not states — they are conditions:

  • What happens if the payment is never made? Can an order sit in placed forever?
  • If there is inventory management, are the products held while the customer decides?
  • When exactly is stock deducted — before payment, or after?
  • What if the payment fails? Can a customer order more than exists?

Every one of those is a condition on the transition from the start, not a state of its own. So the diagram grows a very long label.

Placed Pending Customer places an order, the item must be in stock, item not deducted in inventory yet, no of items must not exceed inventory.
Version 3: the transition from the start carries four conditions at once, which is a smell and not a statechart.

And now the question that undoes it: why does Placed exist at all?

If every one of those conditions has to be true before an order moves on, then Placed is not a state anyone acts on. It is a waiting room nobody waits in. So it goes.

Pending note on Pending - Item must be in stock - Item purchased must not exceed inventory - Item purchased must NOT be deducted yet
Version 4: the conditions become a note on the state that owns them. One state, one note, no waiting room.

With that settled, the next questions are about what comes after pending:

  • Do you want a cap on how many items one customer can buy?
  • What is the state after pending — is it paid, or does the business call it something else?

The cap is a fourth condition, and the answer to the second question is a new state with conditions of its own.

Pending Paid Customer pays for the order note on Pending - Items must be in stock - Items purchased must not exceed inventory - Items purchased must not be deducted yet - Items purchased must not be above the limit set note on Paid - Payment must be successful - Items must still be in stock - Items are now deducted
Version 5: a second state, and the note on Pending grows a fourth line. Deduction now happens at Paid, which will cause trouble shortly.

Two or more states means the next question is always the same: what events move one state back to another? So:

  • What if the customer is unhappy after paying?
  • What if the stock record was wrong and the item is not actually there?
  • Remember that nothing is deducted while an order sits in pending — can two customers both think they have the last one?

That last one is the bug the diagram was hiding, and it needs a transition the shape does not have yet.

Pending Paid Customer pays for the order Item no longer in stock, customer preference change. note on Pending - Items must be in stock - Items purchased must not exceed inventory - Items purchased must not be deducted yet - Items purchased must not be above the limit set note on Paid - Payment must be successful - Items must still be in stock - Items are now deducted
Version 6: Paid can go back to Pending. A dashed return arrow is the cheapest way to show that the first draft of a statechart usually assumes progress is one-way, and that it is not.

The questions keep coming, and they are better questions now:

  • When exactly is stock deducted — before payment or after?
  • If it is after, then between pending and a failed payment the item is not held, so another customer may take it. Is that acceptable?
  • If it is before, how long do you hold it? What if the customer never pays? Is that good business?
  • Does paid going back to pending make sense at all?

And one state is still missing. What happens to an order that is never paid, or that the customer changes their mind about? Cancelled.

Pending Paid Cancelled Customer places an order Customer pays for the order [guards] [guards] note on Pending - Items must be in stock - Items purchased must not exceed inventory - Items purchased must not be deducted yet - Items purchased must not be above the limit set note on Paid - Payment must be successful - Items must still be in stock - Items are now deducted note on Cancelled - Payment not made within a certain number of days - Cancelled as per customer request
Version 7, the first iteration: three states, guards on both routes into Cancelled, and an end marker for each. Not finished — finished is not a thing a statechart gets to be.

What it says

In plain English:

  • An order can sit in pending: the customer has placed it and has not paid.
  • The system moves pending to cancelled on its own if the items are no longer in stock, or if payment has not arrived within a set period.
  • pending can also be the last state an order is ever in. It is not an error path.
  • Stock is only reduced once a customer has paid. Which means two customers can both hold an item in pending while only one of them can pay for it, and the other order simply stays there.
  • pending moves to cancelled either by that timeout or at the customer's request.

And more questions, because there always are:

  • What if a product is withdrawn from sale entirely? Cancel every order automatically, or leave them in pending for a human?

What the exercise is for

Going through this in order — including the two versions that were wrong — is the useful part, not the final diagram. The first version had a state that existed for no reason. The third put four conditions on one transition. The fourth deleted a state AND found where the conditions belonged. The sixth admitted that progress can go backwards.

Two things fall out of that:

  • The requirements get understood. Every question asked of the client is a question that would otherwise have been an assumption somebody made in code. The pending-holds-no-stock problem is a real bug in the system above, and it surfaced because a statechart made the awkward question obvious rather than because anyone thought of it.
  • The enums write themselves. Once the states and transitions are explicit, an OrderStatus enum is not a guess: it is the list of states, and the transitions are the only places status can change. That is what I mean when I say the making of the diagram is doing the engineering.

Statecharts model one object, and requirements are about many. It is never complete either — requirements change, so the diagram changes, which is why this one is a first iteration rather than an answer.

Bye.

Written in 2021, ported from the old Hugo site. The seven diagrams were mermaid state diagrams there and are hand-authored SVG here, one per version, so the post no longer depends on a diagramming library. The prose is lightly edited; the iteration order and every note are as written.

Comments

Discussion lives on GitHub — you'll need a GitHub account to post.