Say you're writing a enterprise blogging app in Ruby on Rails. Should published, trashed, draft, be states? Maybe your answer is yes, like it is for WordPress. Or you might think differently about this problem, given that it's an enterprise application.

Prerequisites for state machine

For now, let's assume that we're not dealing with hierarchical state machines. If you consider our blog post case, you could put published, trashed, and draft into a database column called state and call it a day.

However, the core assumption is that each individual state is mutually exclusive. The blog post can either be published, or be trashed, or be a draft. In normal cases, you'd be okay with that.

Make sure that states really are mutually exclusive

In an enterprise application, I could argue that although published and draft are mutually exclusive, they're not mutually exclusive with trashed.

When I trash a content, but decide to restore it, the system needs to know whether it should put it into draft state or the published state. In essence, a piece of content can be either draft and trashed, or published and trashed.

This is where you could argue that let's construct a state machine of two variables, publish status, and trash status. But it might have combinations that'd never occur, but you'd still have to account for that. The number of states would also grow exponentially with each additional workflow state that you add; say you decide to let people "flag" inappropriate content.

Consider independent workflows

A good way to design this would be to:

  1. Have a column called is_draft
  2. Have a column called is_trashed
  3. Whether the content is published or not depends on both the above fields, and will depend on newer fields too as they're added. Thus add a is_visible_to_user field that indicates whether the content is publicly visible. A single combinational method in the Post model would take all the fields above as input, and output a value for is_visible_to_user.
  4. Workflows that show content only have to depend on the is_visible_to_user field, without caring for how it was computed.

Enterprise applications are not simple

Although we modeled draft content with a is_draft column, in an enterprise application, there would be a lot of audit logs around the change of any of these columns.

Not everyone would be allowed to trash content too. So trash as a functionality could have additional audit tables describing who trashed it, when, and why. The is_trashed field is basically a cache for this decision.

Individual workflows can be owned by different teams

An enterprise application often has a large team, which is often divided into smaller teams responsible for different parts of the application. Separating workflows into columns like these allow different teams to own the drafting and trashing functionality.

The only common denominator becomes the function inside of the Post model that calculates is_visible_to_user from the rest of the fields. Code reviews become easier, and it becomes easier for the team to move ahead with implementing its functionality.

Conclusion

When thinking about using a state machine, make sure that you've fully evaluated the nature of the states, while also considering your team size and the general direction for the development of your feature.