[HN Gopher] Xstate: State machines and statecharts for the moder...
       ___________________________________________________________________
        
       Xstate: State machines and statecharts for the modern web
        
       Author : thunderbong
       Score  : 128 points
       Date   : 2023-03-27 16:48 UTC (6 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | lenkite wrote:
       | I always found it strange how state machines work so nicely in
       | the small but tend to be difficult to scale up to large-scale,
       | distributed systems.
       | 
       | Orchestrators like Kubernetes discourage designing state machines
       | and transitions inside resource controllers. Instead a list of
       | conditions associated with the object's status define a "phase"
       | (state in k8s term). But there are no formal event-based
       | transitions between states. Depending on the conditions and their
       | tri-boolean values: True/False/Unknown, the object is effectively
       | in a different phase.
       | 
       | Effectively, kubernetes recommends treating Resources like a game
       | ECS - the controller is the system, the resource is the entity
       | and the conditions are the state components.
        
       | instagary wrote:
       | I tried XState for a mobile app.
       | 
       | Pros:
       | 
       | - The concept of states, events, and actions are an intuitive way
       | to model state and the learning curve for me wasn't too bad.
       | 
       | - I enjoyed using the visual editor inside VSCode and after
       | setting up TS, the codegen was pretty cool.
       | 
       | - The library supports parallel states.
       | 
       | Cons:
       | 
       | - The codegen was sometimes pretty bad for TS. There was some
       | jank between the editor and the codegen, leading to frustrating
       | DX.
        
       | Rapzid wrote:
       | I'm a simple man. I just want to embed a state machine in my
       | objects. I want to let it know when an event occurs. I want it to
       | let me know when an event occurs. Sigh..
       | 
       | Found xstates desire too control everything made a simple
       | embedding use case really awkward. The hoops to jump through for
       | good typescript support while breaking the config down into
       | reusable components...
       | 
       | Really wanted to like xstates but it felt like it was trying to
       | own the entire app and was sacrificing usability for some version
       | of academic purity. Also, feels like a huge marketing effort is
       | under way to over fit it as a solution to any problem containing
       | the word "state"; ie as a React state management solution..
        
         | sanitycheck wrote:
         | I had a similar reaction after trying it a couple of years ago.
         | I only needed a fairly straightforward FSM for a video player
         | UI, and I like to properly understand any libraries I use.
         | Xstate seemed to have waay too much going on.
         | 
         | I ended up writing my own in about 80 lines of typescript in
         | less time than it would have taken to finish going through the
         | Xstate docs. Doubtless Xstate is exactly what's needed for some
         | projects, but I suspect it's unnecessarily adding to the
         | complexity of many more.
        
         | davidkpiano wrote:
         | Would love to know more about an example "simple embedding use-
         | case" that you may have in mind.
         | 
         | We're not trying to force state machines as the solution for
         | any state-related problem; they're not a panacea, but instead a
         | useful tool for the appropriate use-cases.
         | 
         | And we're definitely making a huge effort to improve the types.
        
       | joshribakoff wrote:
       | I used it in the past and really disliked how it encouraged
       | things like defining which functions to call by specifying a
       | string with the function name. I would have much rather had it be
       | more opinionated and require passing a reference to the function
       | so it forces you to write code that benefits from typescript
        
         | phpnode wrote:
         | There's many benefits in having a state machine configuration
         | that can be serialized, especially if you can store that config
         | in a database and load it at runtime. And actually xstate does
         | allow you to pass the functions directly if you want to
        
           | joshribakoff wrote:
           | Yes it allows strings or references to the function. My issue
           | is that since the former is allowed, either people start
           | using that method, or you have to start a "debate" within
           | your team about patterns to avoid.
           | 
           | The serializability of your state itself has nothing to do
           | with how events are bound to callback functions. If you
           | serialize a string name of a function and not the source code
           | of that function this buys you nothing. The source code
           | itself is all text, so its serializable no matter whether the
           | code is suitable for being statically analyzed
        
       | gooseus wrote:
       | I've gotten good results from asking ChatGPT to give me Xstate
       | representations of machines that I describe, very cool to copy-
       | paste* into the visualizer as a starting point.
       | 
       | * Only issue is that with so much active development since the
       | training cut-off it usually outputs deprecated structure /
       | patterns.
        
       | paultarvydas wrote:
       | There is a HUGE difference between StateCharts and State
       | Machines, yet, the difference is subtle.
       | 
       | StateCharts are the "Strucured Programming" version of State
       | Machines.
       | 
       | StateCharts use parental authority instead of inheritance (my
       | words).
       | 
       | A child state machine cannot override the operation of a parent.
       | A parent can yank children out of their current states back to
       | some known state.
       | 
       | OOP and FP don't encourage this kind of thinking.
       | 
       | The Big Deal concerning state machines is the "state explosion
       | problem". Harel's StateChart notation conquers this problem.
       | 
       | My reading of the original paper is here:
       | https://guitarvydas.github.io/2020/12/09/StateCharts.html
        
       | davidkpiano wrote:
       | Hey, creator of XState here!
       | 
       | Just want to mention that we're very close to releasing XState v5
       | beta, which brings even more features to the state machines &
       | statecharts you can create, and greatly improves the developer
       | experience, but also makes it more usable as a general-purpose
       | state management library (or orchestration "framework" if you
       | want to consider it that), whether on the frontend or backend.
        
         | ar-nelson wrote:
         | Would you consider supporting Deno? (The npm module can
         | probably be imported as-is in newer Deno versions, but a
         | dedicated module is always nice and likely wouldn't be much
         | work)
        
         | [deleted]
        
         | tobr wrote:
         | Hello! I've looked at XState a bunch of times but always felt
         | it seemed very involved for what it does. A simple "vanilla"
         | state machine isn't particularly complicated.
         | 
         | Are you sure you need to bring "even more features", or rather,
         | what are your thoughts on how feature-rich a state machine
         | library needs to be?
        
           | julenx wrote:
           | For vanilla state machines there's also @xstate/fsm. In their
           | docs[1] you have a feature comparison.
           | 
           | [1] https://xstate.js.org/docs/packages/xstate-fsm/
        
         | fallingmeat wrote:
         | do you foresee sysml like diagrams in your roadmap? if you
         | could do activity diagrams with swimlanes...dude. that'd be
         | huge.
        
           | davidkpiano wrote:
           | Yes! Visualizing how different actors communicate with each
           | other in general is a big goal for us this year. We're going
           | to start with sequence diagrams.
        
       | satvikpendem wrote:
       | I've seen XState more and more, recently, what are the pros and
       | cons versus other state management solutions? Anything that
       | XState fails at particularly that potential users should be aware
       | of?
        
         | epolanski wrote:
         | Verbosity, complexity. It's probably overkill for 99.9% of the
         | things you imagine.
         | 
         | Learning curve is more similar to rx-js or fp-ts than a state
         | manager.
        
           | phpnode wrote:
           | when you do get over the learning curve it's really excellent
           | though.
        
         | brigadier132 wrote:
         | The story for composition and reusability of multiple state
         | machines is not good and it's not compatible with existing
         | solutions for certain things (react query for example.)
         | Supposedly this will all be fixed to some extent in v5 but that
         | hasn't launched yet.
        
       | davnicwil wrote:
       | The readme gives the example of a traffic light to demo a state
       | machine usecase.
       | 
       | Fair enough, but what's a rudimentary usecase for 'the modern
       | web'?
       | 
       | What everyday UI thing is better done with this than other
       | methods? Why is it better?
        
         | epolanski wrote:
         | The usual "the examples are so trivial to implement safely in
         | other ways that I don't see the benefits".
        
         | andy_ppp wrote:
         | Almost everything can be written as a state machine and
         | arguably it's very useful to do so, for example let's take a
         | note taking app, you can probably list a set of transitions
         | that can happen to change the state of the app. Keeping the
         | state changes separate from the UI is a good thing. In a way
         | redux is a state machine too. Try it out on a project and see
         | if it works for you!
        
           | davnicwil wrote:
           | > Keeping the state changes separate from the UI is a good
           | thing
           | 
           | Why? Can you give an example?
        
             | lioeters wrote:
             | For example, the browser extension Redux DevTools
             | implements "time travelling" by taking snapshots of the
             | entire state on every state change. This is enabled by the
             | fact that in Redux, state changes are separate from the UI.
             | This same principle can be used for undo/redo history.
             | 
             | Another example is sending and receiving state changes from
             | anywhere other than the UI, such as via WebSockets in a
             | collaborative editing environment.
        
               | davnicwil wrote:
               | Got it, makes sense. At least in React this has been the
               | long-understood default expected pattern though right?
               | Like, you pass down props from something higher up. Is
               | this a property of state machines or a library like this
               | one?
               | 
               | Not trying to be pedantic I promise, I really want to see
               | the value of this, but not sure I can without an example
               | clearly comparing this against just a few if statements
               | or something in pure JS, for something 'everyday'.
               | 
               | State machines make sense to me in a theoretical sense
               | for something with a pretty complex tree of state
               | transitions like a game but for the 99% run of the mill
               | UI work you get in building web products I am not sure I
               | see anything that brings value worth the tradeoff of
               | having to learn yet another library.
        
               | lioeters wrote:
               | That's true, keeping state and state changes separate
               | from the UI is the default expected pattern in React,
               | particularly with Redux. Or at least it was before hooks,
               | which obfuscated the whole situation in my opinion.
               | 
               | And it's true that this separation of state and UI is
               | just a general design pattern, which can be implemented
               | with a plain object and switch statement, or a small
               | class with a few methods - it doesn't have to be state
               | machines.
               | 
               | So I'm with you in your conclusion, or skepticism perhaps
               | - I prefer to apply the minimal pattern that achieves the
               | same thing, rather than having to learn another state
               | management library like Xstate or even Redux. But then
               | again, I can see the value of such a library in a
               | group/company environment, where you want everyone to
               | learn and follow certain ways of organizing things, where
               | new members can read the documentation, and understand
               | how to add and change existing code in a predictable
               | pattern.
        
               | the_gipsy wrote:
               | React expects and recommends you to use side-effects.
        
               | davnicwil wrote:
               | This is not my understanding of React - can you
               | elaborate?
               | 
               | Its original value prop was one way data flow and pure
               | functional UI based on passed in props. It allows for
               | side effects, to get certain things done pragmatically,
               | but in my view does not encourage them in any way.
        
               | the_gipsy wrote:
               | https://react.dev/reference/react/useEffect#what-are-
               | good-al...
        
             | andy_ppp wrote:
             | ChatGPT it, it'll do a better job than I will and it'll be
             | relevant to your requirements.
             | 
             | It's the same situation as redux though but with more
             | formality and it'll draw you a nice diagram too.
             | 
             | "Hey ChatGPT, can you show me an example of using XState
             | typescript library to separate business logic from UI code
             | in a computer program and discuss why XState might be
             | useful for this purpose?"
             | 
             | The example it gives is really incredible... and obviously
             | you can refine what it's doing in context for your own
             | understanding.
        
               | [deleted]
        
         | sonicrocketman wrote:
         | The real benefit for our project was being able to statically
         | define, and therefore export the business process the app would
         | follow. Once it's exportable, it's validatable and
         | configurable.
         | 
         | IMO the JSON was hard to read, but we had a plan to build a GUI
         | config builder that exported the state flows.
        
           | davnicwil wrote:
           | Ah yes, I can see this. I've also seen this kind of
           | 'universally understandable spec of behaviour' attempted from
           | the other direction with things like plain-language cucumber
           | test definitions.
           | 
           | Emphasis on _attempted_ , not sure I've ever seen it actually
           | work as envisioned. Can see how bringing this directly into
           | the code could remove some hurdles though.
           | 
           | Must say though I am a little skeptical on the readability /
           | comprehensibility of the JSON format as you point out, but
           | it's certainly a cool idea.
        
         | simplotek wrote:
         | > What everyday UI thing is better done with this than other
         | methods?
         | 
         | All of them.
         | 
         | > Why is it better?
         | 
         | Because it specifies a workflow that actually matches what you
         | want to do instead of employing a pile of ad-hoc rules that
         | quickly become an unmaintainable mess.
         | 
         | If you have animations and async calls and multiple
         | screens/pages, you have an application with application states
         | which transition in response to inputs. That's what state
         | machines are designed to handle.
         | 
         | Storyboards are state machines. Navigation components are state
         | machines. UI binding layers like redux are state machines.
        
           | davnicwil wrote:
           | I hope you take this in good faith, but one often encounters
           | claims in programming that a particular solution approach is
           | universally better than alternatives for all usecases, often
           | without accompanying concrete usecase examples, and the
           | latter is exactly what I'm trying to dig out.
           | 
           | I am not saying you're wrong, but if you're right this would
           | be the first case I've seen of it in my career so far :-)
        
         | joemckenney wrote:
         | User onboarding, setup wizards, feature announcements, or, more
         | generally, the _modeling_ (and maybe implementation) of
         | incremental, progressive user flows. The only caveat is that
         | they tend to require the machine 's state to be persisted so
         | that the user doesn't experience the same state(s) again.
         | 
         | Truthfully, using state machines at _runtime_ for the use cases
         | above is sometimes too heavy. That distinction is actually a
         | big input to what I've been building at Dopt.
         | 
         | Our platform lets you build state machines that are initialized
         | per user in your application via our SDKs--you design the
         | machines in the platform, and we provide APIs to let you
         | transition them based on user interaction/input. We've taken a
         | bunch of inspiration from Xstate and statecharts. I actually
         | wrote up a blog about how we took inspiration from the latter
         | https://blog.dopt.com/state-machines-and-their-influence-on-...
        
         | mananaysiempre wrote:
         | This is only incidental to your actual question (unless you're
         | into making widget toolkits), but have you seen the state
         | diagram for a button widget[1]?
         | 
         | [1]
         | https://web.stanford.edu/class/archive/cs/cs103/cs103.1142/b...
         | (requires pointer and not touch input)
        
         | dakom wrote:
         | The ubiquitous waiting/loading/error(msg)/success(data)
         | 
         | Lots of frameworks and state management libraries already lean
         | towards helpful design, in languages with async and ADTs as
         | first-class citizens you've got a real leg up, but at the end
         | of the day it comes down to preventing impossible states.
         | 
         | You can't have an error message when in the success state, you
         | can't have success data while loading or waiting.
         | 
         | By preventing this at compiletime, it also means the UI can't
         | ever show bogus data (i.e. it's impossible to have a success
         | screen with the error message dangling at the bottom because
         | somebody forgot to clear it)
        
           | joshribakoff wrote:
           | I think this is solved more generally with a discriminated
           | union in typescript.
           | 
           | A state machine is the wrong solution unless you also need to
           | restrict transitions themselves in my opinion
        
             | capableweb wrote:
             | Statecharts are language-agnostic, whereas your proposed
             | solution appears to be tailored specifically for TypeScript
             | or other strongly typed languages. In this context,
             | Statecharts offer a more versatile and generalized solution
             | compared to your suggestion.
        
               | davnicwil wrote:
               | general is not always better though. There's a tradeoff
               | to other things like readability.
               | 
               | If a TS typed switch statement, say, offers the same
               | guarantees of covering all options yet is just run of the
               | mill JS that anyone can read and instantly grok, and
               | covers 99% of usecases in run of the mill web dev product
               | work, then to me that's an argument that it's the better
               | solution.
        
               | capableweb wrote:
               | I agree with you, as mention in another comment
               | (https://news.ycombinator.com/item?id=35331199), that if
               | you can cover all the cases with conditionals and you
               | don't have nested states, don't use Statecharts. Just
               | like distributed architectures are not for everything but
               | more advanced use cases, so is Statecharts. It's a tool
               | to help you manage great number of states, not just a
               | couple.
        
           | davnicwil wrote:
           | I think a switch or an if/else expresses this really clearly
           | though and also prevents impossible states.
        
             | capableweb wrote:
             | As a huge proponent of Statecharts, I agree. If what you're
             | doing is really simple and only have a few amount of states
             | (as conditionals introduce those states no matter if you
             | use state machines, statecharts or conditions), it makes
             | sense to keep things simple.
             | 
             | But once you start having much more than that, or nested
             | states, Statecharts makes it really easy to build reliable,
             | performant and easy to change flows.
        
               | davnicwil wrote:
               | I can see that. Do you have some examples of common UI
               | usecases which are the other side of the line here and
               | for which statechart based code is better?
        
               | henrydark wrote:
               | the original paper by David Harel has a marvelous long
               | example of his own digital watch, highly recommended
        
               | davnicwil wrote:
               | thanks, I'll check this out!
        
         | strangeattractr wrote:
         | I use it for in component logic in vue. Used to have a
         | collection of boolean or enum variables that represent the
         | state of the component and change according to actions e.g.
         | const isLoadingUser = ref(true). This works but often leads to
         | unforeseen states being possible and just isn't very clear. In
         | contrast, with xstate you define all states and legal
         | transitions and wire everything up to send events, you can
         | write guards that prevent transitions. I transitioned all my
         | complicated multi stage forms to xstate and since doing so
         | they've become substantially more robust. I highly recommend
         | trying to out.
        
         | illiarian wrote:
         | Ultimately every UI is a collection of various (often
         | contradictory) states.
         | 
         | Example: Even typing into a text box on Hacker News is a new UI
         | state: you have a different set of shortcuts, the keyboard
         | works slightly differently, the browser has to keep track of
         | the input if you suddenly press back and then forward again
         | (some browsers maintain input in these cases), undo has to work
         | within the scope of the textbox etc.
         | 
         | The more complex your UI becomes, the more contradictory states
         | become, and need to be tracked. We're posting something over
         | XHR/WebSockets? X amount of elements on the page have to be
         | disabled, some state has to updated with save progress etc. We
         | are logged in/logged out? We need to show a different state.
         | There's an error in some part of the app? We need to block user
         | from doing something and show errors etc.
         | 
         | If you can split those into actual states and allow interface
         | and actions based on the current state, it becomes easier to
         | reason about what is going on ini your app
        
       | sonicrocketman wrote:
       | I was one half of a team that heavily leveraged XState in 2020.
       | We used it to build a general-purpose kiosk app (in Electron)
       | that could be updated remotely using an Xstate configuration
       | served by a graphene-django app. One of the most technically
       | impressive pieces of software I've ever had the pleasure to work
       | on. (I mostly did server/backend stuff).
       | 
       | The entire kiosk device could be repurposed on the fly and Xstate
       | + JSON schema form allowed for completely customizable &
       | swappable UI.
       | 
       | Shame the project died.
        
         | yagodragon wrote:
         | I'm also exploring xstate for a similar use case and would love
         | to know more about your experience with it. I'm particularly
         | interested in understanding the level of customizability you
         | allowed in your implementation.
         | 
         | My use case involves implementing various user flows in my app
         | and I'm currently weighing the benefits of using xstate versus
         | react-router.
         | 
         | Also, I'm curious about how you integrated graphql into this
         | project. Could you share some details on that?
         | 
         | Lastly, I wanted to ask if you stored all the different
         | configurations on a database or on git? If you used a database,
         | how did you manage different schema versions?
        
         | jrvarela56 wrote:
         | Sounds awesome, given it died could you publish the source?
         | Would be great to see how something like this is structured.
        
           | sonicrocketman wrote:
           | Unfortunately no. It was proprietary.
           | 
           | (we pushed to open source the guts, but to no avail)
           | 
           | Both of us reminisce about it though. We literally retooled
           | the fleet of devices in a weekend to adapt to COVID and it
           | worked. We interfaced with tons of hardware and even ran a
           | customized debian image, all powered by RPis. Truly a nerd's
           | dream project.
        
             | jjclane wrote:
             | We're currently building something that sounds
             | superficially similar, would you be interested in having a
             | chat?
        
               | sonicrocketman wrote:
               | Sure. Check bio. Email in blog's about page.
        
       | kidfiji wrote:
       | Had the pleasure of working with XState at my last role. Where I
       | found it greatly shined was being able to jump into a UI/machine
       | I have never worked on before and contribute almost immediately
       | after digesting the machine's definition
        
       | a-dub wrote:
       | any thoughts on a custom DSL for defining and verifying abstract
       | state machines and then machinery for generators for different
       | languages/libraries/environments/visualizations?
        
         | lerchmo wrote:
         | I have been using this XState alternative
         | https://github.com/StoneCypher/jssm
         | 
         | I find the DSL much easier to wrap my head around than the json
         | format of Xstate.
        
       | swyx wrote:
       | love how xstate makes implicit state machines explicit with a
       | time tested (one might say Lindy) format. badly/ad hoc defined
       | state machines are the source of so many bugs - David has been
       | banging this particular drum the entire time i've known him
       | (https://www.youtube.com/watch?v=HPoC-k7Rxwo&list=PLRvKvw42Rc...)
        
         | a4isms wrote:
         | " _Any sufficiently complicated model class contains an ad-hoc,
         | informally-specified, bug-ridden, slow implementation of half
         | of a state machine._ "
         | 
         | -a former colleague
        
           | baq wrote:
           | "I don't need a state machine library" - everyone when
           | starting a new project
        
             | xavdid wrote:
             | I came across this code from 2-years-ago-me today:
             | {/* the fact that i'm doing this means I probably should
             | have used x-state or something */}
             | {stages.filter(Boolean).length > 1 && (             <div>
             | WARNING! More than one assumption is true!
             | {JSON.stringify(stages)}             </div>           )}
             | 
             | One day I'll learn...
        
         | vaughan wrote:
         | I wonder if we could build better implicit state machines if we
         | could more easily visualize our imperative code by annotating
         | it with visualization hints and parsing the AST. Also easier to
         | debug/step-through this way.
        
           | gherkinnn wrote:
           | Then why not just make it explicit?
        
             | rektide wrote:
             | Having really good tracing/observability on an explicit
             | state machine like Xstate would be such a great superpower.
        
       | mattgreenrocks wrote:
       | I need to write a post on this, but I've been using this pattern
       | to great effect:
       | 
       | https://gist.github.com/andymatuschak/d5f0a8730ad601bcccae97...
       | 
       | It doesn't require a library, it binds states and effects
       | together, it is completely pure, and it can be implemented in
       | most any language, preferably one with sum types and
       | exhaustiveness checking. It works equally well with both complex,
       | time-dependent business logic as well as code that controls UI
       | states.
       | 
       | I love state machines for help in writing robust code. The act of
       | laying them out this way forces you to consider lots of
       | intermediate states that are ignore or forget. Plus, the logic
       | stays away from all the gunk of the platform, state machines are
       | trivial to test, and, once you get used to them, are easy to
       | read.
        
         | mhoad wrote:
         | Seems you're not alone. This was ripped out of Google's Stadia
         | platform I believe but might be wrong
         | https://github.com/google/hierarchical-state-machine.dart
        
       ___________________________________________________________________
       (page generated 2023-03-27 23:01 UTC)