Live data from Hacker News

Designing state machines

drivy.engineering

11–20 of 58 posts

Re: Designing state machines

#11

I can't recommend Akka's state machines enough. It's an incredibly simple and rock solid state machines built right into the library. ( http://doc.akka.io/docs/akka/snapshot/scala/fsm.html ) We're already on a Play/Scala backend so adding these was incredibly easy for us, but I think they're one of the hidden amazing features of the akka/scala world.

I use it with akka.net, it's pretty cool

Re: Designing state machines

#12
I have recently created some dead simple code to explain the concept to colleagues whom, as the article notes, were not using this very simple and effective construct at all to control state transitions in business apps.

As the article does not really explain how state machines work let me try to do it here so maybe you will go back to your code and put one in place :) In general, if anywhere in your code you have some property/field called status/state/etc... that you update depending on events then chances are that a state machine would improve the robustness of your code.

Ok, so you have some states. For business apps this usually comes from business. For our dummy example we will simulate a day (using Haskell here for conciseness, but no worry, it's trivial and you don't need to know any Haskell to understand it):

    data State = Awake | Dressed | Fed | Ready | Work | Cafe | Home | Asleep
Now most apps stop here and thus they don't have a machinery to control how to move between these. If we were to draw a graph where vertices are the above states and edges are how we go from one to another then, without any restriction, we would implicitly get a complete graph where you can go from any state to any state. Eg. from "Ready" I could go straight to "Asleep" without "Work" :) Our aim is thus to restrict transitions from one state to another to transitions that actually make sense. Making sense obviously depends on the problem we are solving. For our example let's introduce the following events:

    data Event = Alarm | Dress | Eat | GoToWork | Think | Yawn | Coffee | GoHome | Read | WatchTV
Now that we have states and corresponding events we can draw a graph where vertices are states and edges are events. The graph is now explicit in that we can only move between states (vertices) via events (edges). To put this graph into code, let's create a function that takes a pair of state and event, eg. (Asleep, Alarm) and returns a new state, eg. Awake:

    myDay :: (State, Event) -> State
As said above, myDay is a function that takes a (state, event) pair, which we can also call a transition, and returns a state. This function is nothing more than a lookup table, aka. transition table, where we will write down how to transition between states:

    myDay (Asleep,  Alarm)    = Awake
    myDay (Awake,   Dress)    = Dressed
    myDay (Awake,   Eat)      = Fed
    myDay (Dressed, Eat)      = Ready
    myDay (Fed,     Dress)    = Ready
    myDay (Ready,   GoToWork) = Work
    myDay (Work,    Think)    = Work
    myDay (Work,    Yawn)     = Cafe
    myDay (Cafe,    Coffee)   = Work
    myDay (Work,    GoHome)   = Home
    myDay (Home,    Read)     = Asleep
    myDay (Home,    WatchTV)  = Asleep
    myDay (_, _)              = error "invalid state transition"
That's it, we have codified our graph. We explicitly stated the valid transitions and anything else is by definition invalid. We are pretty much done at this point. A state machine itself can be thought of as a generic function that takes a transition table, a transition and returns a state. This is only to decouple a specific transition table from the general machine itself, but it literally does nothing other than lookup whether the transition is in the table. If yes then it gives back the corresponding new state otherwise we get and error.

You can see a pretty picture of the above graph and some code here: https://github.com/pwm/fsm

Re: Designing state machines

#13
post #5

For anyone else working in ruby, I've grown to love the "Workflow" gem[1] for my state machines. It really helps to streamline things, and also has a built-in way to generate a visual of your class's states and transitions. [1] https://github.com/geekq/workflow

Looks interesting! I've always used https://github.com/aasm/aasm, but this looks pretty good.

More generally I get worried about my ActiveRecord objects getting too bulky and business logic getting all wound up in post-transition callbacks and such. But that's something that can be managed with service objects and being disciplined about triggering state changes.

Re: Designing state machines

#15
post #12

I have recently created some dead simple code to explain the concept to colleagues whom, as the article notes, were not using this very simple and effective construct at all to control state transitions in business apps. As the article does not really explain how state machines work let me try to do it here so maybe you will go back to your code and put one in place :) In general, if anywhere in your code you have so…

Excellent explanation. Thanks!

Re: Designing state machines

#16
What about the DB side of things ? How do you model the state graph in the DB ?

I have been thinking of using Postgresql CTE to do this. When a cycle occurs, I just create a new step to the dB.

Anyone from CRM startups who have interesting learnings here ?

Re: Designing state machines

#17

What about the DB side of things ? How do you model the state graph in the DB ? I have been thinking of using Postgresql CTE to do this. When a cycle occurs, I just create a new step to the dB. Anyone from CRM startups who have interesting learnings here ?

If you mean modeling the state graph itself, it's usually not modeled in the db but only in the code. It could indeed be interesting to store the graph itself if it evolves often, or at least a version number.

If you were speaking about storing the instances lifecycles, a simple model using a RDBS is to store one row per transition event in a separate table. This is what the papertrail[1] gem does for example.

[1] https://github.com/airblade/paper_trail

Re: Designing state machines

#18

What about the DB side of things ? How do you model the state graph in the DB ? I have been thinking of using Postgresql CTE to do this. When a cycle occurs, I just create a new step to the dB. Anyone from CRM startups who have interesting learnings here ?

You can use user defined aggregates in postgres to model state machines. I'm using this technique quite successfully and have been meaning to write a blog post about it.

Re: Designing state machines

#19
post #5

For anyone else working in ruby, I've grown to love the "Workflow" gem[1] for my state machines. It really helps to streamline things, and also has a built-in way to generate a visual of your class's states and transitions. [1] https://github.com/geekq/workflow

Looks interesting! I've always used https://github.com/aasm/aasm , but this looks pretty good. More generally I get worried about my ActiveRecord objects getting too bulky and business logic getting all wound up in post-transition callbacks and such. But that's something that can be managed with service objects and being disciplined about triggering state changes.

I used this for a huge project with a lot of states. Separated my workflow out into individual files for each class so they could just be included at the top. Then for each transition method, I created a service object (Interactor gem[1]) to handle all of my complicated transition logic, so the object-level transition methods would just check for a success or failure and perform the transition appropriately. Really love how it turned out.

[1] https://github.com/collectiveidea/interactor

Re: Designing state machines

#20
There was an article posted here a while back on how to implement a state machine in Rust, purely using its type system to facilitate state transitions. It's a neat concept, even if it's a bit more verbose than what you'll find in other languages.

https://hoverbear.org/2016/10/12/rust-state-machine-pattern/

Post reply on HN