I recently had a conversation about joining a startup with a really interesting, but complicated, product mostly actualized in a complex JavaScript spa (react).
They wanted a more senior engineer to join so as to help untangle some of the complications and lead the juniors (that built the thing already? Unclear) out of some kind of rats nest they'd been coded into. I never saw the code so I'm not exactly sure what this meant.
Anyway the founder asked me to look into this xstate platform before our call and asked a lot of questions about my understanding of state machines. The founder had this idea that if the team built out a bunch of state charts using this product and like, diagrammed their entire app, that was their ticket out of whatever sticky situation they were in.
I was pretty skeptical at the time and remain so now. This seems like an interesting analysis method, and implementing SPAs in a state machine way (each component being a state machine) is alright I guess, but, especially for a startup, building out these charts feels like a waste of time. To me it seems like fielding early customer feedback and driving features is more important.
My guess is that the engineers had a pretty good idea what was going on but were struggling to explain it in a way the founder understood, or maybe were slowing down on feature implementation, I don't really know, but I'm always so skeptical of slapping more tools and plugins and diagrams onto the stack of things the engineering team has to worry about.
I'm curious if anyone has been on a team in a situation as I've vaguely described here and found state charts useful in getting them out of a hole?
I have actually just started a process like this in a personal react project.
The project uses password auth and websockets, and I was starting to get bugs around refreshing things that I couldn't easily fix.
I had implemented this originally using a singleton pattern for an AuthService and a MessageBus for the websockets. I created those at app start and put them in a React Context.
Converting things to xstate is working great, but it's nearly a rewrite. The approach I had stashed state in a couple different places and had a bunch of callbacks passed around. Now all that state is just in the state machine's context, and what were methods turn into pure functions.
One thing that's a little non obvious is that if you have async operations (ie waiting for a new token) you probably want a state for waiting for that, where you invoke the async method followed by transitions on success and failure. The docs reflect this, but it's not narratively obvious.
Anyway, liking it so far. Changing to a state machine is going to hurt if your code already sucks.
Interesting use case, because for a react project with lots of state management, were you not using redux? And if so, were you using Redux Toolkit? https://redux-toolkit.js.org/
The last major react project I worked on I found that RTK kinda functioned as a state machine definition for the project, and let us work on requests states in our components, i.e. if loading do this, if not yet loaded do that, etc.
I guess with this state machine framework you'd also be managing DOM state in one spot which could be kind of nice. In the big project we had to consider Formik managed input fields, buttons, modals, etc.
I'm going to do my best @davidkpiano impression and point out that "Redux, as typically used, is _half_ a state machine" [0] , in that a typical reducer responds to actions regardless of what the current state value might be, whereas a true Finite State Machine always checks the current state first before deciding if the current event is relevant.
You _can_ absolutely write Redux reducers as true state machines. With `createSlice`, you'd generally need to have a field in the slice that's some kind of enum, and check that first inside of a reducer:
Very doable, but not the most ideal syntax, since `createSlice` is focused on "here's an action / thing that happened, here's the reducer that handles that".
On the flip side, you can also use XState state machines as Redux reducers. A state machine is, after all, a function that takes a current state value + some event, and returns a new state.... exactly the same as a reducer function!
David and I have been saying for a while that we'd like to have a more official integration between XState and Redux. A while back, Matt Pocock put together an proof of concept for what a `createXStateSlice` might look like [1]. I actually sat down with David a couple weeks ago and we did some further design discussions about the possibility of using the `@xstate/fsm` package (a smaller version of XState's logic) as a starting point, and generating RTK actions based on that. No code yet, but it seems feasible.
That's pretty awesome news regarding your conversations.
I hadn't even thought of the `reducers` slice property. I meant more all the thunky features around the `endpoints` property and the hooks that are autogenerated as a result. In our components this meant we could have for example a form that takes an api model as input and can modify that model on submit. So like,
And then right there is all the state we really need to worry about for a given form as we interact with the API. Though again the missing bits are the stuff managed by Formik, like changed but unsubmitted inputs and etc, but those definitions would also be up top near the RTK stuff and so at least while editing it's visually available. Like you said doesn't necessarily pass muster as an actual state machine unless you have lots of checks in your useEffects or wherever else you're reacting to state changes about the entire state of the component.
Producing visual charts is kind of just a neat side effect of state machines. The real benefit is formalizing a process’s possible state flows in a generally consistent and traceable way. You don’t actually need any tool to do this, disciplined convention can accomplish the same (but a tool is a great forcing function to be sure you’re actually doing it, and a well defined static type system can be useful too).
I agree to your main point, but I think you are underestimating the usefullness of visual charts in giving easy overview for people not invested into the code, to see what is going on.
You probably correctly noted the gap between the founder's understanding and the ability of the engineering team to articulate ongoing concerns.
Stepping past that, which they shouldn't, maybe in that gap, the founder picked a tool, perhaps prematurely. They also sought out a more experienced engineer to bridge the gap. Who knows if the engineering team even noticed the gap and attempted to improve communications somehow.
1. If you go and reverse-engineer an accurate Statecharts model of the current behavior, it will probably be weird, and not exactly what you would've done if you'd been workig form the model. You might also discover problems, but fixing the model might be tricky.
2. You really need buy-in by the engineers (not only the managers), and for them invest enough in learning how how to model systems well this way. (Example from UML, not Statecharts: it seemed almost everyone using UML tools was only visualizing their class inheritance hierarchy, and only in a management-slides kind of way. Which I'd say is at most 1% of the benefit.)
3. If you start working from models, the code and model had better agree.
(Background: I made a pretty complete Statecharts DSL compiler for Java, circa '97, and previously worked on various tools and methods for purposes like you describe.)
In automotive applications you have to use a State chart tool (i.e. Simulink Stateflow) for logic, or you more-or-less can't pass any modern software certifications.
So if you are asking "will state charts make my software more reliable" => then the industry response is a large renouncing yes. The unanimous opinion is that they are the foundation of systems analysis and reliability.
I think it would be a good day when someone builds a React front-end for Simulink Stateflow that exports JS the same way we export C for automotive.
I've been in these situations many times at various levels. Your analysis feels very spot on... imho you should trust your instincts, you seem very well grounded.
I have in practice found state machines to be best at adding foundations and a mental model for everyone to share. They are bad at making people think they can get simple flow charts of their business processes. The reality has always been, the real world is complex and regular code is already the best abstraction for dealing with it, especially when someone (founder) wants it to be more simple than it actually is.
Same here, state machines are fantastic to extend state driven UIs without worrying about breaking existing behavior. If you find XState too verbose consider using a DSL that compiles down to JS like Lucy: https://lucylang.org
Thanks for all your valuable contributions to this space! I have been curious if development on Lucy is continuing? I might like to port some of my existing machines over if the project is still active.
You could also just make a very simple State machine. They don’t take a huge amount of code. you have a variable with the current state, you have a function that takes the previous state and the new State as arguments, now implement whatever logic you need. Not everything needs a library.
Pardon the unnecessary caps, I am using voice dictation.
When I need something lightweight I usually implement state machines using generators/coroutines when the language provides them, as they compose well and I find it easier to follow to control flow anyways.
The main drawback with this approach is that it is easy to blow the stack, but it is not too hard to implement trampolining on top of it (by yielding the continuation to a wrapper generator)
To use the generator I send its inputs using `.next()` and obtain current state information, as shown on your library webpage.
What's the quote about "those who do not do X end up with an unintensional half built reimplementation of X"?
My personal recent example is a wizard in a web app built as a single major component, with some subcomponents, that goes forward and backwards through 3 different major flows depending on use case. It's been somewhat tamed over time, but we still find cases where an error on step X gets us into an unrecoverable state, sometimes with actual longstanding data impact or where closing & reopening the modal doesnt get the user back on course. With a state machine, I feel, things would be far more predictable.
Having some actual Computer Science in your coding can be a huge boon.
> Every program attempts to expand until it can read mail. Those programs which cannot so expand are replaced by ones which can.
And my own take:
> Every program attempts to expand until it can (a) interpret half of Common Lisp; (b) read and write mail; AND (c) implement a nontrivial subset of Quickbooks Online, including double-entry accounting. Those programs which cannot so expand are eventually forced to thus expand by the mythical force known as the Stakeholder.
One must embrace the inevitability of the "rule.trigger_logic" column, of the "how do we make this work like gmail" question, and of the dreaded interplay between "balance due" and "transaction/account adjustments."
I think both are necessary in different circumstances. I use a simple litmus test: where does the state live to provide the best UX for my app if a user moves from their phone to a laptop and back again?
If the state needs to persist across devices it it server side (though a frontend cache can make sense), eg: onboarding flow or "task wizard" state.
Some UI interactions are much easier with state machines, but persisting state isn't a concern even across page refreshes, eg: complex animations or complex form validation states (especially validation that involves time or websockety things).
I find I got the same issues even with state machines. If the problem is complex, you have to consider every edge case with or without state machines. The essential complexity is the same.
Hrm. I used to not understand what state machines were (I don't have a CS degree) and mistakenly think they were adding complexity. I use them regularly now. I make my own, so they don't have default transitions to remove (beyond throwing 'not implemented' errors when I haven't built a particular transition yet), and I'd still say they're less complex than managing state without them.
Thinking about 'remove default transitions' - are you talking about using a particular state machine library? My default model is writing them. It's kind of like using a feature flag library when a hashtable with features for keys and boolean values will do. OK maybe use a library if the library adds realtime control etc, but not for the basic concept of flags - likewise a state variable and a single transition function work very well for state machines.
In my view it's the other way around. If you stick to a state machine for your app logic, you'll end up with an unintentional half built reimplementation of a general purpose programming language.
The fact these state machines aren't the mathematical constructs from CS, but "extended" versions with "context" (this is where all the actual app state goes), should be the giveaway that they aren't a sufficiently capable model for the demands of UI programming. After you add extensions for every possible need, what do you end up with?
I understand the appeal though. UI development is a complicated mess, and people want a formula to make sense of it all. I've went through several iterations of these cookie cutter patterns form MVC to MVVMC++ to flux to redux to redux-saga to immutable.js to Recoil and so on. At best these marginally improve the situation, at worst they end up adding complexity on top of the inherent mess.
At the risk of missing out on the One True Way of doing UI development, I'll pass on this latest installment. I keep open the possibility that state machines "marginally improve the situation" though.
While that's true, there are also limitations and gotchas of Web based UIs that make state management very complex and messy. There are layouts that you _cannot_ express (with HTML/CSS/JS) without intermediate rendering steps and the whole paradigm of separating state and representation (React etc.) falls apart very quickly.
And before anyone asks: Yes, this comes up surprisingly often. Yes, alternatives have always been explored first. Yes, I'm up to date with browser capabilities. No, you can't say "no" to these designs when you're hired specifically because you can make things work.
With Vue and Pinia, one doesn't even have to bother with patterns. Frankly, I think Redux does more harm than good. Their borderline incomprehensible documentation is a testament to that.
Redux shines when you have an actual "app" with a large dynamic "global" client-bound state that pops up in different places and needs undo/redo (Like a video editor, Blender,etc).
And even then you preferably need some small helpers to make the experience smoother (slices from redux toolkit is a good example).
For most people doing a small CRUD pages that's just woefully overkill.
Some people are really creative at creating dozens of boolean flags with lots of possible and impossible combinations. And some don't even correspond to screen in your figma file from designer (but can actually happen due to race condition... etc). You will really want a state machine that just list every possible state and transition in that case.
Hi, I maintain Redux and wrote most of our docs (including our current tutorials).
Can you give some details on which parts of our docs you feel are "incomprehensible"? I'm curious which specific pages you've been looking at, and for what purpose.
We've tried to organize the docs using the "Documentation System" approach described at [0]: Tutorials for teaching step-by-step, Explanations and How-To guides for specific topics, and References for API details.
Generally we want people to go through our "Redux Essentials" tutorial [1] as the primary way to learn how to use Redux correctly. It teaches "modern Redux" patterns with Redux Toolkit as the standard way to write Redux logic (including RTK Query for handling data fetching), and React-Redux hooks in components.
I'm genuinely interested in feedback on what explanations aren't clear and how we can improve things!
I agree with you, the “extended” version of state machines with “context” is the problem with xstate. Modelling state transitions deterministically is great, but context and actions are too closely coupled to machine transitions, making it very hard to write generic or reusable state machines when you actually make use of these features. Had xstate focused on only transitions (like regular FSMs) and left context/data and actions/effects to other application-level abstractions, xstate would be widely compatible with many projects instead of requiring a heavy-duty paradigm shift in terms of how you model your application logic.
I agree that state machines alone are probably not sufficient, but they are frequently a useful way of modelling at least part of the interaction flow. Honestly they should probably be used more in all areas of programming (but they should not be used exclusively).
I think the sweet spot are state machines that don't take "context" as inputs but have richer effects in their transitions (if we're talking about Mealy Machines).
This way, the internal behavior of the machine is still deterministic and easy to reason about.
I found this tiny two lines in the erlang state machine docs that I've kept around for years because it's such a precise definition of how they need to work in practice:
"State(S) x Event(E) -> Actions (A), State(S')"
"If we are in state S and the event E occurs, we should perform the actions A and make a transition to the state S'."
The fn needs to yield both the new state and a collection of side effects of that transition that need to be handled somehow. You'll need side effects no matter what so might as well formally describe them in the SM, then handle them outside of it in a way that fits the language/framework/runtime. I think this is basically exactly what elm does and how a couple of the redux extensions work too.
> Having some actual Computer Science in your coding can be a huge boon.
this is so, so underrated... i could get pretty flame-y in defense of this point, but a lot of said flame, admittedly, "doth protest too much", as someone trained to (allegedly) be a computer "scientist". but what i will say is that if i had a dollar for every time that software i rely on breaks and/or sucks because there is not enough "science"... there would be a lot more software that actually "works" (tm) in the world (xD).
I've always sort of thought of explicit state machines as a sort of failure state when it comes to finding a good abstraction for some logic. Explicit state machines are the sledgehammer that lets you bludgeon through the problem space regardless.
I get that they're an useful intermediate state for parsers and so, but still. I'm no fan.
This is a good point. The state machine abstraction is very low level. I guess what would be interesting would be domain-specific abstractions implemented on top of state machines.
Defining the domain-specific abstractions in the terminology of state machines may make them more approachable also to non-domain experts.
Been there, and you make a terrific and common example.
Yes, you're right, FSM are terrific modeling wizards or complex autocomplete inputs.
That being said, you don't need full blown FSMs, you can often get away simply modeling and leveraging a type system for this purpose and only allow state transitions to happen through specific functions.
Essentially, the case you describe can be well modeled by a reducer-based redux-like flow with actions that lead to precise mutations.
One thing that I've done in the past was to use xState to model the transitions but then not use it in the actual implementation.
I use xstate in a major project, having moved from python's transitions library to this when the project became web focused. XState is fantastic once you get used to it, but the ramp-up is similar to a fairly complex framework.
Onboarding new engineers to it can be quite a chore, especially if they're not used to thinking about different execution paradigms. That said, the ability to autogenerate visualizations of the state graph is super near, and there's so many nice utilities inside of it that make life easy once you get going.
>Onboarding new engineers to it can be quite a chore, especially if they're not used to thinking about different execution paradigms.
Sadly this is what I've run into as well. Having built a serious application from the ground up with XState at the core of the architecture, it's the closest thing to code nirvana I've ever reached. But getting buy-in across the org was an impossible task due to the overhead and boilerplate.
I’m wondering how this would work alongside the History API for web apps. SPAs have complicated flows, but is often good practice to use URLs for routing in case the user hits refresh, restarts the browser, shares the link etc. As long as XState can be coaxed to keep the history sane, this could be a way to make web apps less buggy, with excellent visual documentation of the expected app behaviour to boot
Yes, and also the fragment. But what I’m referring to is that the state machine needs to manage burning the stack (e.g., clicking a ‘Done’ button on wizard would take you back several pages). When the user hits back they should be able to naturally affect the state machine and also to eventually leave the page to its referrer if they go back far enough
State charts are great for the right use cases. I used XState to help build a graphical editor (think Figma) with fairly complex interactions involving mouse and keyboard events, etc.
I’m curious if others have found it worthwhile for more typical web apps too. My instinct is it’s usually overkill.
I've stumbled upon this post about building simple state machines [1] a while ago. I was working on a small game and it helped a lot with the "enemy' behavior.
One quality about state machines that I find overlooked is debuggability. Limiting state updates to transitions makes it so easy to debug weird edge cases and race conditions. This upside is perhaps more relevant on the backend though where state is more long-lived, which is why I've been experimenting with a project for building backends with state machines: https://github.com/fabianlindfors/restate.
I've only used a state machine in code once. Guess what I was writing.
BUT.
Once a state diagram saved my team a whole lot of pain. We were translating a piece of regulation to code. There were a bunch of people directly talking to us that had a little idea (in that case, worse than no idea) of what the persistence of the real system was.
The diagram made wonders: I was able to ask "is this situation the same as this? no? why?" and actually make myself understood when I said "that won't work" instead of getting in a loop.
Best of all: non-technical people got really well what the diagram meant and, after a while playing with it, they liked it.
Probably not exactly what you are thinking of but my project Restate is all about building backends using state machines by saving them and their transitions to the database: https://github.com/fabianlindfors/restate
Would be interesting to see how good LLMs are at generating code for this.
As I see it, state machines are a json serializable representation of logic.
We also have json schema for representations of valid inputs. GraphQL or similar as representations for fetching data. And of course jsx for rendering views.
Put all these as a Venn diagram, what are the overlaps?
Can we use these constraints as a way to generate robust and consistent apps?
Since this thread mostly only seems to get into why state machines are great, I wanted to see what else there is on the other side. You rarely see state machines in application development (backend or frontend). And if I saw it there I'd think someone was making overcomplicating things.
On the other hand, programming in state machines makes more sense when you're dealing with network protocols like TCP or distributed systems like Raft (both the implementation of Raft and the clients of Raft are modelled as state machines).
It's hard for me to explain why state machines feel like overkill/overengineering in application development but feel natural in network and distributed systems programming.
I have seen state machines quite frequently in production code, but rarely formally defined state machines.
For example something I've seen a few times is a db table with the fields "account confirmed bool" "account_deleted bool" and "account_suspended bool." There's a state machine in that app somewhere, even if they wouldn't describe it that way.
I think developers having better awareness of when they're drifting into this approach and intentionally choosing it or not would be a much better situation.
87 comments
[ 3.0 ms ] story [ 134 ms ] threadhttps://docs.viewflow.io/bpmn/index.html :
> Unlike Finite state machine based workflow, BPMN allows parallel task execution, and suitable to model real person collaboration patterns.
BPMN: Business Process Model and Notation: https://en.wikipedia.org/wiki/Business_Process_Model_and_Not...
On the difference between the map and the territory.
> - Can TLA+ find side-channels (which bypass all software memory protection features other than encryption-in-RAM)?
From https://news.ycombinator.com/item?id=31617335 :
>> Can there still be side channel attacks in formally verified systems? Can e.g. TLA+ help with that at all?
From https://news.ycombinator.com/item?id=33563857 :
> - TLA+ Model checker https://en.wikipedia.org/wiki/TLA%2B#Model_checker :
>> The TLC model checker builds a finite state model of TLA+ specifications for checking invariance properties
Not sure what TLA+ has to do with either.
They wanted a more senior engineer to join so as to help untangle some of the complications and lead the juniors (that built the thing already? Unclear) out of some kind of rats nest they'd been coded into. I never saw the code so I'm not exactly sure what this meant.
Anyway the founder asked me to look into this xstate platform before our call and asked a lot of questions about my understanding of state machines. The founder had this idea that if the team built out a bunch of state charts using this product and like, diagrammed their entire app, that was their ticket out of whatever sticky situation they were in.
I was pretty skeptical at the time and remain so now. This seems like an interesting analysis method, and implementing SPAs in a state machine way (each component being a state machine) is alright I guess, but, especially for a startup, building out these charts feels like a waste of time. To me it seems like fielding early customer feedback and driving features is more important.
My guess is that the engineers had a pretty good idea what was going on but were struggling to explain it in a way the founder understood, or maybe were slowing down on feature implementation, I don't really know, but I'm always so skeptical of slapping more tools and plugins and diagrams onto the stack of things the engineering team has to worry about.
I'm curious if anyone has been on a team in a situation as I've vaguely described here and found state charts useful in getting them out of a hole?
I worked in a team that modeled with state machines and it was useful but I was the most junior member at 7 years experience
The project uses password auth and websockets, and I was starting to get bugs around refreshing things that I couldn't easily fix.
I had implemented this originally using a singleton pattern for an AuthService and a MessageBus for the websockets. I created those at app start and put them in a React Context.
Converting things to xstate is working great, but it's nearly a rewrite. The approach I had stashed state in a couple different places and had a bunch of callbacks passed around. Now all that state is just in the state machine's context, and what were methods turn into pure functions.
One thing that's a little non obvious is that if you have async operations (ie waiting for a new token) you probably want a state for waiting for that, where you invoke the async method followed by transitions on success and failure. The docs reflect this, but it's not narratively obvious.
Anyway, liking it so far. Changing to a state machine is going to hurt if your code already sucks.
The last major react project I worked on I found that RTK kinda functioned as a state machine definition for the project, and let us work on requests states in our components, i.e. if loading do this, if not yet loaded do that, etc.
I guess with this state machine framework you'd also be managing DOM state in one spot which could be kind of nice. In the big project we had to consider Formik managed input fields, buttons, modals, etc.
I'm going to do my best @davidkpiano impression and point out that "Redux, as typically used, is _half_ a state machine" [0] , in that a typical reducer responds to actions regardless of what the current state value might be, whereas a true Finite State Machine always checks the current state first before deciding if the current event is relevant.
You _can_ absolutely write Redux reducers as true state machines. With `createSlice`, you'd generally need to have a field in the slice that's some kind of enum, and check that first inside of a reducer:
Very doable, but not the most ideal syntax, since `createSlice` is focused on "here's an action / thing that happened, here's the reducer that handles that".On the flip side, you can also use XState state machines as Redux reducers. A state machine is, after all, a function that takes a current state value + some event, and returns a new state.... exactly the same as a reducer function!
David and I have been saying for a while that we'd like to have a more official integration between XState and Redux. A while back, Matt Pocock put together an proof of concept for what a `createXStateSlice` might look like [1]. I actually sat down with David a couple weeks ago and we did some further design discussions about the possibility of using the `@xstate/fsm` package (a smaller version of XState's logic) as a starting point, and generating RTK actions based on that. No code yet, but it seems feasible.
[0] https://dev.to/davidkpiano/redux-is-half-of-a-pattern-1-2-1h...
[1] https://github.com/mattpocock/redux-xstate-poc
I hadn't even thought of the `reducers` slice property. I meant more all the thunky features around the `endpoints` property and the hooks that are autogenerated as a result. In our components this meant we could have for example a form that takes an api model as input and can modify that model on submit. So like,
And then right there is all the state we really need to worry about for a given form as we interact with the API. Though again the missing bits are the stuff managed by Formik, like changed but unsubmitted inputs and etc, but those definitions would also be up top near the RTK stuff and so at least while editing it's visually available. Like you said doesn't necessarily pass muster as an actual state machine unless you have lots of checks in your useEffects or wherever else you're reacting to state changes about the entire state of the component.Stepping past that, which they shouldn't, maybe in that gap, the founder picked a tool, perhaps prematurely. They also sought out a more experienced engineer to bridge the gap. Who knows if the engineering team even noticed the gap and attempted to improve communications somehow.
1. If you go and reverse-engineer an accurate Statecharts model of the current behavior, it will probably be weird, and not exactly what you would've done if you'd been workig form the model. You might also discover problems, but fixing the model might be tricky.
2. You really need buy-in by the engineers (not only the managers), and for them invest enough in learning how how to model systems well this way. (Example from UML, not Statecharts: it seemed almost everyone using UML tools was only visualizing their class inheritance hierarchy, and only in a management-slides kind of way. Which I'd say is at most 1% of the benefit.)
3. If you start working from models, the code and model had better agree.
(Background: I made a pretty complete Statecharts DSL compiler for Java, circa '97, and previously worked on various tools and methods for purposes like you describe.)
So if you are asking "will state charts make my software more reliable" => then the industry response is a large renouncing yes. The unanimous opinion is that they are the foundation of systems analysis and reliability.
I think it would be a good day when someone builds a React front-end for Simulink Stateflow that exports JS the same way we export C for automotive.
I have in practice found state machines to be best at adding foundations and a mental model for everyone to share. They are bad at making people think they can get simple flow charts of their business processes. The reality has always been, the real world is complex and regular code is already the best abstraction for dealing with it, especially when someone (founder) wants it to be more simple than it actually is.
fwiw, if xstate feels like too much for whatever reason, https://thisrobot.life has seemed like a decent alternative to me.
hmm… seaside… C side… has this been used for anything yet?
Pardon the unnecessary caps, I am using voice dictation.
I created a JavaScript state machine library using them to define each state. https://github.com/JavaScriptRegenerated/yieldmachine
I imagine your approach must be different?
The main drawback with this approach is that it is easy to blow the stack, but it is not too hard to implement trampolining on top of it (by yielding the continuation to a wrapper generator)
To use the generator I send its inputs using `.next()` and obtain current state information, as shown on your library webpage.
My personal recent example is a wizard in a web app built as a single major component, with some subcomponents, that goes forward and backwards through 3 different major flows depending on use case. It's been somewhat tamed over time, but we still find cases where an error on step X gets us into an unrecoverable state, sometimes with actual longstanding data impact or where closing & reopening the modal doesnt get the user back on course. With a state machine, I feel, things would be far more predictable.
Having some actual Computer Science in your coding can be a huge boon.
> Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.
https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule
https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule
> Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.
Cannot for the life of me figure out where I read this.
> Every program attempts to expand until it can read mail. Those programs which cannot so expand are replaced by ones which can.
And my own take:
> Every program attempts to expand until it can (a) interpret half of Common Lisp; (b) read and write mail; AND (c) implement a nontrivial subset of Quickbooks Online, including double-entry accounting. Those programs which cannot so expand are eventually forced to thus expand by the mythical force known as the Stakeholder.
One must embrace the inevitability of the "rule.trigger_logic" column, of the "how do we make this work like gmail" question, and of the dreaded interplay between "balance due" and "transaction/account adjustments."
Only then can one find inner peace.
I’ve worked at a concerning number of places now where needing to do waaaay too much with PDF’s has inevitably become a core part of the product.
The advantage of state machines is that you know it doesn't do anything except for what you put in the state transitions.
If the state needs to persist across devices it it server side (though a frontend cache can make sense), eg: onboarding flow or "task wizard" state.
Some UI interactions are much easier with state machines, but persisting state isn't a concern even across page refreshes, eg: complex animations or complex form validation states (especially validation that involves time or websockety things).
For example, what happens when an enemy gets shot when they are in the ‘dead’ state?
Thinking about 'remove default transitions' - are you talking about using a particular state machine library? My default model is writing them. It's kind of like using a feature flag library when a hashtable with features for keys and boolean values will do. OK maybe use a library if the library adds realtime control etc, but not for the basic concept of flags - likewise a state variable and a single transition function work very well for state machines.
https://mitadmissions.org/blogs/entry/designing-a-calculator...
FSM don't help with the bugs. You need analysis on top to actually make a dent in the bug count
The fact these state machines aren't the mathematical constructs from CS, but "extended" versions with "context" (this is where all the actual app state goes), should be the giveaway that they aren't a sufficiently capable model for the demands of UI programming. After you add extensions for every possible need, what do you end up with?
I understand the appeal though. UI development is a complicated mess, and people want a formula to make sense of it all. I've went through several iterations of these cookie cutter patterns form MVC to MVVMC++ to flux to redux to redux-saga to immutable.js to Recoil and so on. At best these marginally improve the situation, at worst they end up adding complexity on top of the inherent mess.
At the risk of missing out on the One True Way of doing UI development, I'll pass on this latest installment. I keep open the possibility that state machines "marginally improve the situation" though.
I've worked on MVVM and redux. GUIs end up complicated and difficult to understand if you're not familiar with the codebase.
And before anyone asks: Yes, this comes up surprisingly often. Yes, alternatives have always been explored first. Yes, I'm up to date with browser capabilities. No, you can't say "no" to these designs when you're hired specifically because you can make things work.
For most people doing a small CRUD pages that's just woefully overkill.
Simple structure, powerful, what do you want more?
Can you give some details on which parts of our docs you feel are "incomprehensible"? I'm curious which specific pages you've been looking at, and for what purpose.
We've tried to organize the docs using the "Documentation System" approach described at [0]: Tutorials for teaching step-by-step, Explanations and How-To guides for specific topics, and References for API details.
Generally we want people to go through our "Redux Essentials" tutorial [1] as the primary way to learn how to use Redux correctly. It teaches "modern Redux" patterns with Redux Toolkit as the standard way to write Redux logic (including RTK Query for handling data fetching), and React-Redux hooks in components.
I'm genuinely interested in feedback on what explanations aren't clear and how we can improve things!
[0] https://documentation.divio.com/
[1] https://redux.js.org/tutorials/essentials/part-1-overview-co...
This way, the internal behavior of the machine is still deterministic and easy to reason about.
"State(S) x Event(E) -> Actions (A), State(S')"
"If we are in state S and the event E occurs, we should perform the actions A and make a transition to the state S'."
The fn needs to yield both the new state and a collection of side effects of that transition that need to be handled somehow. You'll need side effects no matter what so might as well formally describe them in the SM, then handle them outside of it in a way that fits the language/framework/runtime. I think this is basically exactly what elm does and how a couple of the redux extensions work too.
this is so, so underrated... i could get pretty flame-y in defense of this point, but a lot of said flame, admittedly, "doth protest too much", as someone trained to (allegedly) be a computer "scientist". but what i will say is that if i had a dollar for every time that software i rely on breaks and/or sucks because there is not enough "science"... there would be a lot more software that actually "works" (tm) in the world (xD).
I get that they're an useful intermediate state for parsers and so, but still. I'm no fan.
Defining the domain-specific abstractions in the terminology of state machines may make them more approachable also to non-domain experts.
Yes, you're right, FSM are terrific modeling wizards or complex autocomplete inputs.
That being said, you don't need full blown FSMs, you can often get away simply modeling and leveraging a type system for this purpose and only allow state transitions to happen through specific functions.
Essentially, the case you describe can be well modeled by a reducer-based redux-like flow with actions that lead to precise mutations.
One thing that I've done in the past was to use xState to model the transitions but then not use it in the actual implementation.
Onboarding new engineers to it can be quite a chore, especially if they're not used to thinking about different execution paradigms. That said, the ability to autogenerate visualizations of the state graph is super near, and there's so many nice utilities inside of it that make life easy once you get going.
Sadly this is what I've run into as well. Having built a serious application from the ground up with XState at the core of the architecture, it's the closest thing to code nirvana I've ever reached. But getting buy-in across the org was an impossible task due to the overhead and boilerplate.
I’m curious if others have found it worthwhile for more typical web apps too. My instinct is it’s usually overkill.
[1] - https://kentcdodds.com/blog/implementing-a-simple-state-mach...
BUT.
Once a state diagram saved my team a whole lot of pain. We were translating a piece of regulation to code. There were a bunch of people directly talking to us that had a little idea (in that case, worse than no idea) of what the persistence of the real system was.
The diagram made wonders: I was able to ask "is this situation the same as this? no? why?" and actually make myself understood when I said "that won't work" instead of getting in a loop.
Best of all: non-technical people got really well what the diagram meant and, after a while playing with it, they liked it.
Big fan.
As I see it, state machines are a json serializable representation of logic.
We also have json schema for representations of valid inputs. GraphQL or similar as representations for fetching data. And of course jsx for rendering views.
Put all these as a Venn diagram, what are the overlaps?
Can we use these constraints as a way to generate robust and consistent apps?
https://workflowengine.io/blog/why-developers-never-use-stat...
On the other hand, programming in state machines makes more sense when you're dealing with network protocols like TCP or distributed systems like Raft (both the implementation of Raft and the clients of Raft are modelled as state machines).
It's hard for me to explain why state machines feel like overkill/overengineering in application development but feel natural in network and distributed systems programming.
For example something I've seen a few times is a db table with the fields "account confirmed bool" "account_deleted bool" and "account_suspended bool." There's a state machine in that app somewhere, even if they wouldn't describe it that way.
I think developers having better awareness of when they're drifting into this approach and intentionally choosing it or not would be a much better situation.