We recently implemented a state machine in the Content Publisher. While this isn't a change many people will notice, it does lay the foundation for further improvements which we hope will benefit our users.
This work also contributed to my ongoing education in computer science concepts and design patterns. Hopefully this post will share some of what I've learned.
Introduction to state machines
A state machine, or finite-state machine, is a computational model. It's designed to manage things which can be in one of several pre-defined states. Items can transition between states, but can only be in one state at a time.
A properly-implemented state machine can define:
- what your states are
- what things can do while they're in a certain state
- when something is allowed to transition from one state to another
- what happens when something transitions between states
A good example of what I mean by 'states' is what happens when you order a product online. Your order goes through several states: 'received', 'processing payment', 'packing', 'out for delivery', 'delivered', possibly even 'returned'.
While the state the order is in changes during the process, it will only ever be in one state at a time. The order can be in a 'packing' state or a 'delivered' state, but never both at the same time.
There are also rules about how the order moves between states – for example, your order won't move from 'delivered' back to 'packing'.
And different things can happen during the states. Maybe you can cancel your order when it's still in 'received' or 'processing payment', but not once it's in 'out for delivery'.
All these rules and processes are set through the state machine.
The state of states in the Content Publisher
States are a key part of the workflow in the Content Publisher. Content items can be in 3 states: 'draft', 'in review' and 'published'. All items start out in 'draft', but as our users work on them they progress on to
But we might want to add more in the future, like 'scheduled for publication' or 'archived'.
While the Content Publisher has states, and items can move between states, we don't manage these in the clearest way. The logic which controls which states items can be in is spread across the codebase, which makes it difficult to read or to modify.
We decided it was time to implement our own state machine.
State machines and Ruby
We could build a state machine ourselves, but there are so many popular state machine gems out there that we wanted to look at those first.
The next most popular gem is AASM, short for 'acts as state machine'. AASM is well-maintained, with an active community and pretty good documentation – all important if we want to welcome it into our codebase.
The prototype confirmed that AASM ticked all our boxes. It was time to implement it properly.
Adding a state machine to the Content Publisher
The next step was to get AASM up and running in the Content Publisher to replace our existing functionality.
Implementing AASM was more complex here than in the prototype. This was mostly because we've been working on the Content Publisher for nearly two years and so had a lot of code to change.
This gave me a good opportunity to get more familiar with a broad range of our code – everything from role-based permissions to version control.
If you're thinking of using a state machine in a Rails application, I'd definitely recommend trying to put it in place as early as possible to minimise the amount of refactoring.
After many commits and an extensive review, we finally shipped to production on 16 January.
What comes next?
While our switch to AASM made no immediate difference to our end users, it does make our code better and more extensible.
We've now laid the foundation for adding new states or transitions, which could enable new features – like scheduling an item to publish later, or unpublishing a live item.
We'll be doing some more research into how we can improve our workflow, so keep an eye out for more changes in the coming months.