In part I, we covered state chart diagrams in a basic way. Usually, this is good enough to describe your states. You also want to keep thing simple (KISS). On the other hand, it can be convenient to be able to express some more situations, which is what we’re going to do in this post. We will see how we can specify actions when a certain event occurs, we’ll see how we can describe conditions and then we’ll see how to describe concurrent states in a State Chart Diagram.
Actions on Transitions
We already talked about actions in the previous post. We put actions in the states themselves as entry actions, exit actions or do actions. This is usually the right place to put them!

The syntax for a transition is: eventTrigger
[guard condition] / action
The action is what will be executed when the transition occurs.
All three parts (eventTrigger
, guard condition, action) are optional. In the diagram above, we omitted the guard condition, only putting the actions.
I’d like to put a remark here: let’s take the first expired action. When the state changes from “Accepted
” to “1st reminder
”, the first reminder will be sent. There is only 1 arrow arriving in the “1st reminder
” state, so no problem.
But it is clear that often this action will always need to be performed when we get into the “1st reminder
” state. So it would be a better idea to put “Send Reminder
” as an entry action for this state. That way, when in the future we find more transitions to “1st Reminder
”, we’re always certain that a reminder will be sent in any case!
Of course, the same goes for the “2nd reminder
”.
And in that way, we can remove some clutter from the diagram as well. Let’s keep things as simple as possible!

Guard Conditions
The syntax for a transition is: eventTrigger
[guard condition] / action
This event will be executed when the guard condition is true
.

When we look at the “Paid
” state, we see that it can be left in 2 ways:
Either the invoice is completely paid ( [total amount paid] ) and we’re happy, or the invoice was only partially paid ( [invoice not completely paid] ) and we go back to the “Accepted
” state.
The conditions can be expressed in whichever way you like. We could have put something like [amount paid = invoice total] instead of [total amount paid]. This may depend on your audience (who is reading this diagram?). If it is a final diagram for your developers, then the more explicit way of putting the guard condition may be better. If you’re still discussing with your end-users, then the first (more abstract) form may be better.
Concurrent States
When there are concurrent actions to be executed, each action can have its own state.

Let’s say that we’re going to start developing our invoice. We’ll need to develop a header for the invoice, where we’ll enter the client details and some invoice details (invoice number, date, expiration date, …). As you see, the expiration date follows from our previous analysis.
Then, we need a part where we will enter the invoice details. We need to be able to create the invoice lines, modify them, etc.
And finally, we need to create the invoice footer, where we calculate totals, taxes, …
But we don’t necessarily need to do all these 3 tasks one after the other. Maybe we have 3 developers, who can work simultaneously on the software. In that case, each developer can run through his states (backend – UI – unit tests), and the invoice software will be complete when all the 3 trajectories are ended.
Of course, this is a very naïve example of software development. The 3 lanes are never so straightforward, but for the sake of simplicity, we keep it this way in this example. I put a more realistic diagram of TDD at the end of this post.
The Complete Diagram
I have put most of the principles that we have discussed in one fine diagram. While discussing this with an end-user, she immediately noticed that we have some work to do for the “Rejected
” state, hence the comment on that state.
I used colors in this diagram to make things clearer. You don’t have to do that in your diagrams, unless you think that it adds clarity.
I also had to rearrange the diagram so that it would fit on the page. In our paperless times, with large screens that should not be a concern in most of the cases. But sometimes, these diagrams can become quite big, so it may be a good idea to model composed states on a separate diagram.
In this diagram, we see that everything flows towards the end states. Because I had to rearrange the diagram, there are 2 directions: one horizontal direction for the “Rejected
” flow, and one vertical direction for the “Accepted
” flow. I normally create diagrams in such a way that everything either flows horizontally (and from left to right) or vertically (top down). That makes the diagrams more clear.

A Word of Caution
We have seen many ways to express things in a state chart diagram. But it is not because we have all these possibilities that we need to use them. Try to keep things simple!
One More Example
Development of a Web Page
In this example, I want to work out the “Invoice development
” a bit better. I’m concentrating on only the development of one unit, such as the invoice header.

We start by writing the backend, after which we write the front end. We then perform the necessary integration tests and if all goes well the unit is finished. So that is the (sequential) happy path.
For the development, we practice TDD (Test-driven development). So we start by writing a test, for which we implement the functionality. We then test what we have written and hope that the test passes. If it doesn’t, we continue the development until the test is OK. We then verify if all the cases are covered. If not, we add a new unit test (or more than 1) and we repeat the whole cycle until we’re complete with the backend.
For the front end, we run through the same process, as indicated by the comment. When we’re happy with the front end, we can test the whole. If the integration tests don’t pass, we enter the backend development again, starting by writing some more unit tests. We repeat the cycle until all the integration tests pass.
Conclusion
This concludes my explanations about state analysis. I hope that in these 2 articles, you have seen that by defining state correctly already during the analysis phase, you’re creating a lot of advantages:
- It gives you another angle on your models, which is a good way to verify if nothing was missed in the analysis.
- It’s a good way to discuss with your clients / users
- Developers and testers usually understand it really well so development will be faster, and more accurate
Do you use state chart diagrams? Let me know in the comments!
