Build Better Apps With State Machines
Using state machines, we can build web apps with low complexity and high confidence. David Khourshid will show us how xstate helps us make sense of state management in web apps.
Links & Resources
Click to expand the full transcript
Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.
Jason: Hello, everyone, and welcome to another episode of Learn with Jason. Today on the show, we're bringing back everybody's favorite state machine master, David Khourshid. David, thank you so much for showing up today.
David: Thank you for having me. Great to be here again.
Jason: I'm super pumped because I was telling you before the show, we did a show on state machines a while back, quite a while back, actually. It was like over a year ago, I think. Since then, I have become more and more of a fan of state machines. I've been finding more and more ways to use them. I think they are an extremely powerful concept. I've fully bought in, right. Like, I think that it is a great solution. Since you and I talked, it sounds like there are even more tools and things that we can be using that I don't even know about yet. So, I'm so, so excited to dig into that today. But before we talk about that, let's talk about you a little bit. For those of us not familiar with your work, do you want to give us a little background on yourself?
David: Yeah, sure. So I'm a web developer, just like many of you here, I'm assuming. I don't know what kind of audience Jason gets. Anyway, I work at Microsoft. I've been at Microsoft for about four years now. I sort of touch everything. So, not just web development, but infrastructure, dev ops, just all sorts of different things. And yeah, my passions are animations and state machines and user interfaces and stuff like that. So, yeah, that's basically me in a nutshell. Also, I play piano. That's what that's about. I went to college for piano. That's why that sort of stuck.
Jason: Yeah, and you have your own stream, right, with Shasha?
David: Yeah, Steven Shaw. We actually do it on YouTube. Actually, we're almost at 10,000 subscribers. So please, check us out. I think for our 10k episode, we're going to do like a code in the dark sort of fun challenge type thing.
Jason: That's super cool. Here's a link to that. Like, I love key framers. It's one of my favorite formats I've seen. One of the things that I especially love is when you set up where like you just kind of start riffing on the piano, and then you build an audio-visualizer. I've seen a couple of these, and it's such a fun format and such a cool way to see things happen. I love that idea of kind of like parallel creation. It's really, really fun. But anyway, if you haven't seen the Key Framers, definitely check it out. It's a really fun format, a really fun show.
Okay. So before we go too deep into this, I'd love to just address the broader scope of -- or maybe we should start with the definition. What is a state machine?
David: So, a state machine -- don't go to Wikipedia. You're going to get a whole bunch of mathematical jargon in your face. It's just going to be really confusing. I guess the best way to describe a finite state machine is a description of the behavior of a system and how it changes over time. So for example, you and I are -- well, finite state machines, or at least conceptually. So, you could be asleep or a wake. You can't be both asleep and awake at the same time. So, that's one of the core principles of a finite state machine. You can only be in one finite state at a time. And like I said, finite state machines describe behaviors. So, you might react differently to something if you're asleep or not react at all because you're asleep. Then if you were to be awake -- so, your behavior when you're asleep versus when you're awake is different. So finite state machines just allow you to encode that in a very deterministic and visual way. You have boxes and arrows, and each box represents a finite state or behavior you could be in. An arrow or a transition tells you how you could go from one behavior to another. We see this all the time in apps. Like you said, the more you play around with it, the more you just start seeing these state machines everywhere, really.
Jason: And I think like a good example that you've given to show why this is so powerful is that if we don't write state machines, we're still writing state machines. They're just not very good ones because we end up having an is loading and is error and is active states. All of those are true and false states. Theoretically, none of them can be true at the same time. If we forget to write one of those if else statements, then we can end up in a situation where they are true at the same time. So, that's something that state machines are kind of built to prevent, right? Like, it's instead of relying on you to be a good programmer, it's relying on the architecture of the thing to automate that.
David: Right, exactly. So you brought up a really good point with boolean variables. Obviously, you could be a very smart developer and say, well, if it's loading and not loading at the same time, so I'll flip the bits and make sure the boolean variables are there. So, you're writing these gnarly if statements. If this and not this or not this and this, it just gets really confusing when all of that could be described with just one enumerated value or finite state.
Jason: Absolutely. Yeah, and we'll show how this works to make it a little more obvious when we get into the code, but that was the lightbulb moment for me. When I realized that I think when I first looked at state machines, I was like, this is a lot of code, right. I think you start to look at it and you're like, man, I'm writing a pretty decent chunk of code here to do something. And you always see the basic example. Like a stoplight or something. When you look at that, you're like, I don't need this much code for a stoplight, but that's not what real code looks like. Real code isn't a stoplight. Real code is really complex. What I've noticed is that the size of my state machines does not grow at the same rate of all of the bespoke logic that I end up writing to manage the if else statements. It actually grows more slowly. So, it starts out as more code, but it stays more tractable and understandable than this kind of, oh, I'm going to write this little if statement here, do this switch statement over here, and you know, all these different things that we do to manage state that gets spread out through your whole code base.
David: Right. And honestly, I think that's why people are actually afraid of state machines because they start with a simple example, and first of all, it's a huge learning curve. You have to learn about states and transitions and events and all the different parts of state machines and state charts. Also, like you said, state machines can look like a lot of code for simple examples, but it's because state machines don't remove complexity. Instead, they bring it to the forefront. They expose it and make it explicit to that you know all of the cases that could possibly happen in an app. It's right there in front of you instead of being hidden in some abstraction or even worse, just having you assume that the happy path is going to happen all the time, which you know, that's pretty optimistic. I actually tweeted about that today.
Jason: I saw that tweet today. I was thinking that was a good one to bring up as we were making this transition.
Jason: Actually, you know what, why don't we do that. Let's switch over into programming, and we can start showing some of this stuff in examples instead of talking in the abstract. And while we're doing that, make sure you head over to Twitter and follow David. It is a great account to follow, full of really good information, and here's the -- let's see, where is -- here, this one. Here's a really good tweet that kind of talks about -- like, this is a very good point. You write code and think it's going to work like this, but there are all these error cases and things that can go wrong. If we don't handle those, which I mean, you can watch me on this show when I'm like, ah, this'll never happen. And it's absolutely just ignoring an error case. So, that I think is one of these major pitfalls that we have where we just get it in our heads that we're like, well, this is simple code, but it's not simple code. It's simple code right until something goes wrong. Then it's extremely complex code.
Jason: But yeah. So keep in mind if you are interested, we have live captioning being done on the home page of learnwithjason.dev. You can follow along live with everything that we're saying. That's being done by Rachel at White Coat Captioning. Thank you so much, Rachel, for being here today. And the captioning is made possible by our sponsors. We have Netlify, Fauna, Auth0, and Hasura all kicking in to make this show more accessible to more people. All of these are linkable, so you can click on one of a these to check out these sponsors. And the library in question today is Xstate. We're going to be working here. While I have it up, check out the Key Framers. This is a very fun site. There's also very fun merch. I'm a big fan of a lot of these. Does Steven do the art?
David: Yeah, he does. Actually, not that I'm saying don't buy the merch, but in a touch episode, we're just going to be doing some giveaways soon.
Jason: Ooh, okay. There's your incentive, people. Watch Key Framers, get your stuff. I specifically love this one. It's one of my favorites. But yeah, go check that out. It's going to be a lot of fun. Okay. So, David, if we are looking to get started here, what should I do if I just want to kind of set something up? Do I --
David: Good question. So go ahead and go to the GitHub, just because I have some -- actually, you could go to the docs. Doesn't matter. Just scroll down. So, I have a bunch of templates because people could be using XState in anyway. In fact, I created XState so it is not framework specific. You could use it with React, whatever. So, one of the simplest ways to get started is to use the XState react template just because you have those visual components there. If you click that, you can fork that. Sometimes the word template throws people off. It sounds like a bunch of setup, but there's actually not much setup in here. It's just a very simple app. It's just using XState to create the machine and XState to use the machine. It's basically a fancy use reducer. There's nothing really that special about it.
Jason: Okay. So, I'm going to set this up as XState Learn with Jason. We'll give it a name, good. Then I'm going to -- can I just share this as in everyone can view? Yeah, so y'all can just check this out. I don't think this does live share.
David: It has its own version of live share, which I think you've done in previous episodes, right? The whole collaborative -- or you could just do the whole thing.
Jason: Yeah, how do you do the collaborative thing on here? I don't think I've tried that one.
David: To the left, all the way to the left. The two little heads.
Jason: Oh, here we go. So, yeah, we can do this. Classroom mode, that seems like what we need. Why don't we do this setup here. So, if y'all want to follow along, you can see this go. Hopefully that'll be manageable for us. So, let's check this out. So we've got in here a machine. Is it doing anything? There we go, okay. So, this is a toggle, right. That seems like something that is pretty common to make.
David: Yeah, it's also one of those potentially overkill things. The purpose of this is just to demonstrate two parts of the machine, which is going between states, inactive and active, and also assigning to context. Context you could think of as your infinite state, like your extended state.
Jason: And context, this is another thing that I think is cool about state machines. A lot of times when I'm thinking about, oh, I might need whatever for this, if we're keeping it to a component or to a particular piece of an interface and nothing needs to escape that interface, like in this case this little section here, we're tracking the state of the button and how many times it's been clicked. In this interface, for sure, but potentially in other ones this never needs to escape this little suite of components. So, why would we need to track this in some global state? Why do we need this to be globally accessible? We can keep it contained and make this very portable, very self-contained, and therefore a little easier to debug and reason about, which I think is a pretty powerful concept.
David: Right, yeah. XState doesn't care whether you're using it locally or component wide. It's also a little bit of a departure from Redux because it's one single global store, whereas XState gets more into what's known as the actor model. So, you can think of that as a store that can spawn other stores. Those stores with communicate with each other. But yeah, that's getting pretty into the advanced XState sort of stuff. Just letting you know you can use this either locally or globally. Doesn't matter.
Jason: Yeah, okay. So, if we want to kind of play with this, it looks like we've got three pieces here. So, I've got a machine. This is coming out of XState. This has context and states. We have two states, active and inactive. Then we've got the on is an action we can perform. And what state it goes to, right?
Jason: This is what I think makes this really powerful. Basically what you're saying here in this code is if I'm inactive, the only thing you can do is toggle, and the only place you can go is active. So, we're being super explicit about what's possible from the inactive state.
Jason: Yeah, that to me feels like a very -- what you said about like how you're not just assuming that things are going to work. You're being very explicit. These are all the states and you're writing it down. So, down here we have the use machine hook, and that comes out of XState react. That gives us current and send, okay. Current --
David: Is the current state.
Jason: Okay. So, we have to adopt matches where we can check if it's the active state or inactive state. Then we have send, which allows us to send the toggle action. And that's really all that XState is doing here. We're checking what state we're in to determine what the UI looks like. We have a function that sends an action. Everything else is just react.
David: Yeah, so people who are used Reducer before or even Redux, this will immediately feel familiar. Think of this as a reducer with rules. You have different things that could happen depending on the event, but it's not just based on the event. It's also based on the state. And you have your way to send events. Redux calls this dispatch, and by extension since Dan works at Facebook, it's called dispatch with use reducer, too. But I like the word send. It's closer to the actor model. So, that's why send is used instead of dispatch. But semantics, doesn't matter.
Jason: And here's what's really cool about this. So, if I want to make like another component, and we want to stay -- let's say we want to have a paragraph that says if this is active, we're going to say something. Like, we'll say, hi, you've activated this paragraph. So, if we want to make this visible, we could have like an is active state or anything like that. But because we've got the current here and we know that we're using the current matches, then we can just do like active.
Jason: Right, and there we go.
David: There's actually an interesting point here. A lot of people think I'm against is loading, is active, is inactive, all those boolean variables. I'm really not. I just think those should not be the source of your state. Those should be derived from something like a state machine. So, that's why we have online 26 const. Now that boolean active variable is not the source of truth. It's just reading from the source of truth.
Jason: Yeah, and the source of truth is this state, which we can see here includes this -- we have a value of what the current state is. When I click it, now the state has updated and we have a value of active. There's a bunch of extra information, but we also have this context, which is super powerful. So, you know, we've got like our count here. It just comes out of the current context. To me, this is just something that it makes sense in my brain. I like to think through what actually happens in software. One of the things that I think is really powerful about XState that I've never seen in anything else is the fact that you can visually think through how state machines work. Instead of drawing a software diagram and then having to figure out, all right, so, how does this part of the code map to this part of the diagram? With XState, you can make the diagram, and it is the code. That part, I think, is really, really exciting. Oh, why is my Slack on? Get out of here. Go away.
David: All right. So, want to take this to the next level?
Jason: Absolutely I do.
David: I'm going to straight up post this in the Twitch chat. So now we have the XState inspect package. That's going to pop something up. If you don't see anything, you might need to allow pop-ups
Jason: Here's my -- yep, preferences, we're going to allow pop-ups. Okay. Do I need to reload this to get it to work?
David: Probably, yeah. Target window is null. Hmm.
Jason: Here we go. Okay. So, this one didn't work but this one does.
David: Actually, I might have sent the normal React -- or the normal Viz thing which doesn't have the React stuff. The React one might be better. So, let me send that one over. But yeah, there's a lot of things going on here. Basically, whatever machine you have there is visualized with this inspector. But I just sent over the -- or in the Twitch chat I put the React State Viz template. It's going to be the same thing before but visualized.
Jason: So, if I close this down and take a look, we have inspect coming out of XState inspect. That sends over to statecharts.io/inspect. So, this is something I don't need to configure or set up an account. It's just going to work if I send to statecharts.io/inspect?
David: Yeah, that's just a React app running and listening for window events. So, just the sort of 30,000-foot view of how this works. When -- so, when you call inspect, it's going to attach something to the window that basically is a place to store machines that are interpreted. So, when you run machines with dev tools true, which I think if you scroll down, you can see that we have the dev tools true setting in the machine hook. That's going to attach that service to the global window object, and the inspector is going to listen for that and send events back and forth between that inspector window and your running application.
Jason: Cool. Okay, this is really, really cool. So, let me just close one of these. I didn't mean to do that. So, if I have this one and I take this one out, are these like synced?
David: Yeah, they're synced.
Jason: I think I maybe need to pop this twice.
David: Might need to reload the first window, and hopefully that works.
Jason: Oh, it might be a size thing.
David: Yeah, maybe zoom out or something.
Jason: No worries. So if I make this one a little bigger, it should work probably. Yeah, it works. So I'm going to close this, then we can take this one. This is reactive, right? So, if I click this, does it --
David: It will.
Jason: Oh, I think I broke it. Okay. We're going to try this one more time. I'm going to reload. Then I'm going to pull this out. Make this bigger so we can see it, and then I'm going to click this button. Look at that. Okay. Now this works the other way too. Oh, my god.
David: It does. It's a bidirectional thing.
Jason: Oh, that's so cool. So, this is incredibly cool. What we were talking about, about this being visual, this is the visual component of this. Check this out. If I want to add another state, I can come in here and I can say, I don't know, we'll call it unused for now. What did I do? Are you helping? Stop helping. Then I can say like on -- and really, it just drops that unused state in there immediately. That's incredible that we can do that. Now I can sort of map this out as I go and say if I was going to do something where I needed to be able to kind of think through my states, so I'm going to have an open then, and then I'm going to have a closed state and then I'm going to have, I don't know, like a closing state and the opening state so that I can do animations. All of that I can just start thinking through these and I get all these states out here. Then I can start thinking about how they move and connect.
David: Exactly, yeah.
Jason: That's so cool. That's incredibly cool. Do you want to do that, actually? Do you want to do a little -- how you would animate with a state machine thing?
David: Yeah, yeah. Sure, we could do that. You might need to give me access to the -- I think you could just make me an editor instead of a viewer.
Jason: Okay, yeah. I think I can do that. Let's put this back over here so we can make this bigger again. Then I think what I can do is I'm going to have to fork this one, I think. Then we've got a new live session. Oops. So, we can drop that in there. Then once --
David: I should be in right now.
Jason: All right. So now you're an editor. Anyone who wants to come in -- oh, no, what button did I push? Am I back? I'm back. No, I'm not. Oh, my god, what have I done? Okay. What if I go to this? Okay, I'm back in. Am I myself? I am. So then I can close these down, and we can see again. So we've got our state machine. Okay.
David: And I think I can type. Yep, I can type. Awesome.
Jason: Perfect. So I think it's going to open a new window every time we reload.
David: Oh, gosh. I think it's supposed to reload the same window, but it might not.
Jason: Honestly, I'm using Firefox, so it might be that it's rebelling. If I need to, I can open Chrome and see.
David: Yeah, people in the chat are mentioning it is working with window events, and that's basically the mechanism that's working. There's actually docs on what those window events are. In fact, like right now, it's pointing to that statecharts.io/inspect, but there's nothing stopping you from making your own local visualizer. A lot of developers are too lazy to do that, which that's fair. It takes hours and hours to do this.
Jason: I'm getting signed into Chrome here so I can see this and hopefully it'll do what we want, which is not open a new window. Here it goes. Do the thing.
David: Yeah, people in the chat, you can definitely point this to local host and receive events that way. You just have to visualize it yourself for now. Which it's actually not too hard to do this. The hardest part by far is the arrows. Like, just connecting all the arrows together. That's something I've been working on for months. It's not an easy problem to solve.
Jason: Okay. So here's state machine. I think I have to refresh it because I had to allow the pop-up. There's our pop-up. There's our state machine. Now I think it should cooperate as we work through this. So we've got our states here. We know that we want to have an open-closed closing and opening. The general idea is that we want to be able to have something that maybe it's an expanding box or something. So, if you click the button, it's going to open a div and show you something. If you toggle it back closed, then it will animate shut.
David: Yeah. So I went ahead and made the initial state closed. We can just assume that it's closed.
Jason: That's so cool.
David: We can see your paragraph thing here. Oh, man, this doesn't do autocomplete? I have to type it in from memory. That's saw I memorized.
Jason: You got four words further than I did.
David: Yeah. So, we can do something like make this paragraph appear or disappear. Just a really simple example.
Jason: Yeah, exactly.
David: All right. So let's see, in the closed state, we can think about how we want it to open. This is something that I enjoy about state machines and state charts. I don't have to think about adding event handlers or doing unclick or whatever. In an abstract sense, I want some sort of trigger to open and close this. So I could just say on open -- and you know what, I could define that later. It doesn't matter where that event comes from. We could say opening. I could just say after --
Jason: I can see it happening, too.
David: We can say after a second it should be in the open state, and I think that's a good starting point right there. So yeah, let's --
Jason: Oh, you're down here.
David: Yeah. So I'm trying to think about how we actually want to --
Jason: Well, before we even write the code, we can actually try this in state charts, right?
David: Oh, yeah, yeah. Absolutely.
Jason: So if I come in here and see we have closed, I hit open, it takes us to opening. Then it waits a second and moves to open. So there we go. We know that our state is now predictable. We don't have a way to get from open to closed, but we know how to get from closed to open, and that's really exciting.
David: Yeah. So I'm just going to copy and paste some logic over here so we can also do the closing thing. We could also say if it is closing and we decide, you know what, never mind, I don't want to actually close it, sort of like, I don't know, stepping in from that little laser beam in your garage.
Jason: Oh, yeah, yeah.
David: Just to make sure it doesn't kill you or anything. That was a weird analogy.
David: But yeah, you're right. We can't really go from -- oh, wait. Open. A-ha, on closed, go to closing. There we go.
Jason: So now we should have a full loop. So we're on closed, we open, it goes to opening, and then it goes back to open. Now if we close it, it goes to closing. Then it ends up back at closed. If we open it but then I close, it immediately goes back to closing. Ah, beautiful. Look at it go. That's such a nice -- like, we just know that's going to work, and then I can open it again as we start. Perfect.
David: Yeah, one other cool thing. I didn't really put it in the docs or anything. Sometimes you want to have documentation for what your states really mean. I want to say open. Let's just say meta description. Then you can put any sort of mark down in here. So I'm just going to say the box or whatever you're trying to animate is fully open at this point.
Jason: Okay, I've never seen this.
David: So now if you go in here, it will show that description in the state machine. So it's like comments on steroids.
Jason: Wow! Look at that go. That is legit. That's super cool. I see the chat is having a good time with my floating dumpster fire. At least they're not putting it on your face, I guess.
David: That's okay.
Jason: So this looks like one thing I'm noticing is this is a little bit jumbly. I think that's because of the order that I initially typed these state us at in. So if we reordered this to be like closed and then I take opening and move opening up here and then I took open and moved that here, that -- yeah, that immediately reorders the state so it's a little easier to read, which is nice. But yeah, this is like -- I just -- chat, I hope that this is really exciting for you. I think what is so cool about this is that we're taking something that is kind of hard to separate, which is the -- man, I said put it on your face, and they immediately put it on your face. Cool, chat. Cool. What did I tell you about harassing the guests? You're not allowed to do that. So the part that I find really exciting about this is that what we're doing is we're basically being able to say I don't have to think about the animation yet. I don't have to think about the UI yet. I can just think about what needs to be true. Then based on that, we're able to like very quickly write that out. We can map that out, how it goes from one place to another and all the certainty that we get from that. Then we get to switch to the next context, which is now that we know we've got an opening state that lasts for a second, we can animate for one second. I'm drowning in boops. Shruti, hello. How are you?
David: Wow, that's a lot. Does it ever fill the screen completely?
Jason: It definitely does. Usually when Cassidy shows up is when enough people rally to really clear the screen. So, okay. Now that we've got this, then what we can do is we can look at it and say, all right, we know that if we've got our paragraph tags here that we want to have -- let's see. So, we would need like a style -- how do you do classes in React when you don't have a library installed for that? We can't just drop a style tag.
David: I'm going to show you something different, something that I like to do. Classes get messy. It's the first thing that we reach for because it's so simple to just do dot whatever. I have a different plan. So, we're going to use data attributes. I'm going to say data state, and I'm going to go ahead and put the current value because since this is a flat state machine, we know that's going to be a string. So we can just jump into the CSS and style that however we want. So let's actually give it a class name, just a single one, equals thing. I'm so terrible with naming. I'm going to go into styles.css. We could say .thing. Maybe give it some padding, a border, two pixels solid. Papaya whip. Actually, that's a terrible color. You can't even see that.
Jason: Tomato in honor of our good friend.
David: Tomato, all right. Awesome. So, this thing can be in a couple of states. We could say data state equals opening
Jason: Oh, I like this.
David: We have opacity .5. When it's fully open, we could say opacity 1. Background, let's make it red, green? I don't know.
David: This is actually something new I discovered. Like if you have four digits, this one stands for the alpha. I don't know how wide the browser support is on that, but it's pretty useful instead of doing the whole RBGA mess. We'll just call that open, and let's also give it a transition. Transition smoothly. We'll just do that.
David: It's really simple. So I think it should -- oh, yeah. Also, we need something to actually trigger those states. We have open. We have closed. So we can just make a couple buttons. I wonder if Emmet works here. No, it doesn't. Button, we could say open, and then we could say on click, send open. So that should just work. I'm going to get rid of the other buttons too.
Jason: So chat, I just -- it blows my mind. First of all, this, the data state is already just like very good idea. But I think the power of what just happened here where we've basically been able to say like I want to send the open event, and we know that when that happens, it's going to step through these things without us having to do -- oh, boy, keep helping, I guess. Look at that. Did you see what just happened? So we hit that open button, and it goes through those transitions. It's still popping me open into that window, but it's there. We've got our styles going. We've got the opening and closing. I'm just in a battle at this point. Okay. I'm going to move this to the side for a second so it stops.
David: Yeah, you can also just disable the inspector. Something like that might be easy.
Jason: So looking at this, we have the thing, we have the open and closing that are going to go to half opacity. When it's open, it has half opacity. When it's closed t has zero opacity. I wonder if we set the background to be zero opacity, I think we'll get a transition in the background. I think.
David: Possibly. I'm playing around with the button right now. The default styling of a button always makes me want to immediately change it.
Jason: Yeah, look at that go. Now we've got this really nice kind of transition. It fades in and out. If we look at our state chart at the same time we click this button -- so if I pop this over, and I'm going to close this down. Then can you come over here? Yes? All right. So check this out. Now when I -- I've got this, and we'll make this a little bit smaller so we can see. I hit open. Close. Oh, I think it dropped my -- oh, I'm looking at the wrong state machine. That's why it's not working. Check this out. Now when I do this, I open. We see it move through. We close, and it moves back. This is just like -- this is such a next-level experience for developing and debugging where I can just very quickly say, okay, what happens when it closes? Now I don't have to build this. I can just open it to get to the point that I want to be. And only build that part of my UI. I don't have to build all these transitional pieces to test something. I can just define the state machine, use this to jump to the state I need to be in, and then work on that section until I'm happy with it. Then kind of transition back. That to me is where this really becomes kind of a game changer because you just can't do this with other state management things without a ton of custom dev.
David: Right. And that's why there's all that structure. A lot of people say that XState is really useful for organizing your logic. A lot of state managers, obviously they try to focus on ease of use. Like oh, yeah, update state whenever you want. Yes, that makes things easier, but that's not necessarily the goal of XState. XState is meant to make things more organized and predictable and let you identify the edge cases before it bites you in the butt later.
Jason: Mm-hmm. Absolutely. There's a question about using this for Vue.
David: There is an XState Vue package. In fact, we recently updated both at XState React and XState Vue package to include a couple new hooks. So yeah, since the last time we talked, a lot has changed. Well, I don't want to say changed, but a lot has been added to XState. So, if you go into the docs or GitHub, yeah, there's a Vue template there. In fact, when I first announced the XState inspector, the example I gave was in Vue.
David: So, you could use the inspector in absolutely any framework. And XState as well, for that matter.
David: Yeah, absolutely. And that's what I love about it, too. State machines and state charts are really a universal concept, and by universal, I mean the computer that you're typing on right now would not exist without state machines. It's all over the place in electronics and basically everything. By describing logic in state machines and state charts, you have that common language. So it's not like learn Redux or learn React Hooks and do it this way, and if we changed frameworks, good luck with that. Try to decipher all the code. Instead, you're using things that are over half a century old, and you could just transport them from framework to framework or even language to language, actually.
Jason: Yeah, I mean, this is really -- like, it's just very exciting stuff. It makes me feel good about writing this kind of code because it doesn't feel like something that, okay, we're going to write this now, and in two years when whatever comes after the stuff we're using now comes in, then we're going to have to tear everything down and do a full rebuild. And this becomes legacy code. The way we write state machines hasn't really changed in a long time, and it's unlikely that it will change going forward.
David: It's never going to change, unless we suddenly all have quantum computers and the paradigms on which the computers are built absolutely change underneath, but that's very unlikely to happen.
Jason: Yeah, I mean, it's just -- it adds new -- kind of a new color to the idea of building future-proof code. It's future-proof in a lot of ways. It's future-proof because this is stuff that goes back to math and like mathematical theorems that can be proven and have been ad nauseam, but it goes back to this really is portable. It's containable. So, something that somebody brought up that I thought was interesting is when you get into this, like what we've talked about is really contained. It's a toggle button. It's one kind of interaction. But that's not the only thing the state machines work for. We can get pretty complex, right? So what happens when we have like dependent states or things like that? What's a workflow that somebody might run into where they would need to have more advanced dependent states set up?
David: Like nested states, you mean?
Jason: Yeah, you know what, chat if you want to clarify that question, why don't you tell me what you would like to see because we've got about 40 minutes left, and we can definitely go and play a little bit.
David: Yeah, I mean, in our example right now, as we add more features to the silly open/close -- not that it's silly, it's just a really simple example. We have a lot of repeated transitions. So we have on close closing for both the opening and the open states. Obviously we don't have it for closing because we're already in the closing state. If you click close, it's not going to really do anything. And if you click close in close, that's not going to do anything either. So one enhancement we could do here is we could group the opening and open states together. I won't do that now just because that's going to mess with the styles. Actually, it doesn't really. Anyway, yeah, that grouping of states is one of the core features of state charts. So right now this is described as a simple state machine, but state charts really take it to the next level and allow you to make states hierarchal or nested, which is what I just talked about. Because they're grouped, you could have common transitions for all of the child states within that state. Yeah, you could also have parallel states, history, and none of this is minted by me. This is going back to 1989 from when David Harold, who's a computer scientist, professor, he first created state charts all the way back then. It's actually really interesting paper. Sorry, I'm going a little off tangent.
Jason: No, go off.
David: I wanted to share something really cool that someone made in the last month or two, I think. Long story short, in the original state chart paper, there was an example of a state chart for a Citizen digital watch. Way old school, even before my time. It's one with the buttons on the side, and you could do timers and turn the display on and off and just all of these sort of functions. So in the original paper, he created a state chart that describes the functionality of that watch. Well, fast forward 30 years later -- actually, now it's 32 years later -- someone took that exact same state chart and made a web version of that watch.
David: It's one of the coolest examples I've seen. Yes, it does use XState, but this is a working Citizen watch using the original state chart from 30 years ago. So if that's not future-proof, I don't know what is.
Jason: That is extremely cool.
Jason: Remove battery. Oh, I love that.
David: Yeah, it's actually funny. This thing handles states where it's like what happens if the battery drains. To test that, he had to basically wait 15 minutes or something for the virtual battery to drain. Or something like that.
Jason: You can see I'm turning the light on right now. This is so cool. I love this stuff. So the state machine -- wow, look at that. Holy crap. Okay. And this is just a complex version of what we just built. Here's a starting point. This is what I love about it. You kind of learn the language of this. You can see how it transitions between things. We have time and weighting. Let's see if we can find a button. We start with the light. It's off, it goes to on. We've got the battery. This is so cool. This is such a cool -- like, it just makes it -- this is still complicated, but what I love about it is this is complexity we can untangle by ourselves. We don't need someone to sit down and do all the brain dumping to explain what was in their head and why they made this decision and why this thing is weird because it's all been written down.
David: Yeah, yeah. It brings the complexity to the front. Like I said, it doesn't remove complexity. Someone might be looking at this diagram and be like, wow, this is a really complex watch, but imagine if you didn't have this and you just had code. Now it becomes ten times more complicated to understand. You have to basically map all of this out in your head. And this provides a standardized way of doing so. The dotted line there is represent regions. So, they're parallel states, and you can just sort of look into just one of those regions and understand how that orthogonal region works. For example, the light. It's very simple. It goes on and off. You can do that with the other one -- the chime, the display. As complex as this is, you could show it to someone nontechnical and say, if we're in one of these boxes and an event happens, then you could change the state. Sorry, my headphones were talking to me.
Jason: The part about this that's really exciting is the way that state charts are visualized is the way we make flowcharts. So if you were going to make a decision tree, you are going to write up a flowchart and it's going to say this is the state and then what happens. You can either say yes or no or a thing happens or a thing doesn't happen. Then you move to a new decision or state. As humans, we're capable, no matter what our level of technical skill, we all know how decision trees work because they're a meme format. We're familiar with interpreting this kind of data. I think that makes for really powerful discussion tool. Another way I'm seeing this having valuable f you go high level and take this to your design team or executive team and you say, this is what we have to build to do the thing that you're asking for because of the states and things like that, and you can show it's not just this page and this page. Here are the intermediate states. Here are the possible error states. Now you can start to show this is what we have to plan for. It becomes a communication tool as much as it is a programming tool. That's something that code doesn't do.
David: Right. And these state charts, like you said, they're a design tool, but they're also a formal spec. It's known as a visual formalism. That's because not only do you have that visual artifact of the state machine diagram, but you also have this generated from code as well. So it's always going to be one to one with the code. I think me and many other developers sort of fear writing documentation and making diagrams because wing make them as beautiful and well-designed and documented with all the right intentions and the most details that we possibly could, but once code changes, what happens? All of those designs and documentation go out of date. They're basically useless. And they're just going to frustrate developers more because they expect things to work one way, things change, and now they're out of date. Even if you version docs, it's like, okay, you still have to write the latest version of the docs in order to map things together. So that's why one of my biggest goals is to just create more tooling so that you could write code that not only lets you organize and express even the most complex logic in your apps but is able to generate these diagrams, generate documentation even, generate tests, just do a whole lot of things where you shouldn't have to manually do a lot of the things that we're doing today. Like writing documentation, generating tests, et cetera. So that's honestly why I'm so passionate about state charts. Not because I'm someone who just likes looking at boxes and arrows, but more because I'm just really lazy and this makes my job a lot easier. If I could just have the computer do most of the things that I do manually.
Jason: I feel like this is another -- this is similar to like what I think makes something like GraphQL exciting. You're taking how you communicate and explain the code and coupling it directly to the code in a way that, as you said, makes it impossible to get out of sync. The artifact is generated by the code that's been written. That, to me, is -- like, that's the game changer, really. As you said, I've spent days where we charted something, went in for review, got changes made, and then we just had to go back and do like multiple days of refactoring all of these documentations and plans and all the stuff, these artifacts that were generated not by code but by us about code. It makes the whole development cycle so much more painful than it needs to be. So I think that there's something really exciting about this idea of not even laziness but just consistency. There's nothing better than knowing everywhere you go in your code base is correct. Nothing is a comment that's like, don't ever touch this, this is broken, but actually that got fixed and now you have this stray comment that you're not sure if you should remove, and the person who did it quit like three generations of programmers ago. So you're like, I don't know, maybe we just work around this instead of trying to change this code.
David: Yeah, that's my biggest fear too. The bus factor, right? When only one developer understands how part of a system works. This is sort of like an insurance policy, right. The cost up front is big, you know. It's a big learning curve, but you're not learning anything specialized. Again, you're learning something that's existed for many, many years. Like some people in the chat mentioned, something that's present in many other areas of technology, especially game development, and now even animations. There's this really cool animation app that I saw called Rive. It allows you to do just these really cool sort of like rubber hose or just really complex animations. They're actually adding a state machine module to it. Not using XState, using their own thing. But yeah, you could transition between animations with that. I think that's really cool. It just goes to show you they're basically everywhere. It's not like -- honestly, I fear learning something line Kubernetes because it's a very proprietary -- you know, it's going to be a large learning curve that's only good for Kubernetes. But if you learn state machines, it's sort of useful in many different places. Sorry, I'm rambling a lot about this. Too many thoughts.
Jason: No, no. I agree. And UmCamilo asked if there have been other episodes on this. Absolutely. We have a few episodes on state machines and using them for different things. I think state machines are one of those things that, you know, they're worth a longer discussion because as David said, the learning curve is a little bit steeper, and what you're initially up against is a lot of -- there's jargon. There's ways that data is linked. There's a spec attached to it. But as you get familiar with that, it starts to -- I guess it's learning something that we're all doing intuitively now. Relying on intuition is perfectly fine, but it introduces risk and silo. As you said, the bus factor. I, as a developer, like my number one thing is I never, ever, ever want to be stuck on a project because I'm the only person who knows how it works. I never want to get paged because I'm the only person who can maintain it. And that means that code needs to be communicated about. We need to be able to hand off all the things in our head. The best way to get promoted, in my opinion, is to program yourself out of a job. Do it so well and communicate it so well that other people can step in and do it. Then you get to advocate for going and doing the next fun thing. You know, I think the idea that you should make yourself indispensable by making your code hard to read or making yourself the only person who can maintain it, that's an idea -- that one makes me sad. I really wish that people would think about it the other way. Automate yourself out of a job by doing such a good job that anybody can do it. And by making your code so understandable. I feel like state machines are a step in that direction. You're taking something out of your head and putting it on paper in a way that does not require nuance to understand. You can just look at it and step through it and get to where you want to go.
David: Mm-hmm, yeah. In the chat they were asking what the bus factor was. I guess it's a sort of morbid description of what we're talking about. If your teammates get run over by a base and put in the hospital, in stable condition -- let's hope for that -- how many of them can get hit without having the project absolutely sabotaged?
Jason: A less morbid way of putting that is vacation tolerance. How many members of your team can go out before all work grinds to a halt? You know, if everybody goes to a conference and two people are left on your team, can you continue to get work done, or are those people hamstrung until everybody else gets back? That's a bummer because I've been on team where is I had all this institutional knowledge in my head. I would go speak at a conference or go on vacation, and when I got back, nobody had gotten anything done. I felt guilty because I was blocking. They felt guilty because they didn't know how to move forward because they needed something that was only in my brain. We all got in trouble because our velocity slowed down. Like everybody felt bad about it. As I've moved more and more to this model of socializing code and making sure code is transferable and not building knowledge silos, everybody just feels better. I've noticed everybody on my team, we trust each other more. We work better together. We're more collaborative. And everybody is getting promoted. Like the last team I was on at IBM, we were really good at this. Every single person I worked with on that team has been promoted at least once since then. And that is such a cool feeling because it means that everybody had access to the knowledge and access -- the autonomy to make changes that let them demonstrate to leadership that let them move forward. That relies on this type of programming, of making things accessible to people.
David: Yeah, yeah. So, I guess it is accessibility in a different way. Just making sure that everyone is on the same page at all times.
Jason: Yeah, yeah. This became a very philosophical episode in a big hurry.
David: It did, it did. Honestly, that's the whole purpose of this. I didn't do this XState project, you know, for the last few years just to be like, you know what, the world needs another state management library. That wasn't my intention. My intention was more about, you know, what we were talking about, the people factor, just generating documentation and tests and all of that. And being able to communicate changes and logic in a way that's really clear for everyone involved. And we're not doing that now. I think state machines are still pretty niche. That's why I appreciate everyone watching this right now because at least there's some interest in state machines. Actually, I realize that I know that state machines are becoming more popular because people are starting to joke about it and make fun of it, which is a good sign. I didn't invent them. I'm not hurt about it. People are like, oh, man, we have to learn all these things and front-end developers are hyped about state machines. All right, that's funny, but I'm glad that means it's getting more attention. Any news is good news.
Jason: Yeah, I mean, that's the best sign, right. When people start teasing you about it, that means you're making an impact.
David: Yeah, go back to three years ago. No one was really talking about state machines. So, it's really enlightening to see. I'm actually really happy when people tell me, listen, I'm not using XState, but I'm definitely using those ideas of state machines and state charts, and I wrote my own thing. I'm like, that's awesome. That's my goal. And that goes to show this is not a proprietary concept. This is something that you could learn once and use everywhere.
Jason: Mm-hmm, yeah. So, a couple -- moving away from the philosophical into some of the practical things. I saw a couple questions come by in chat while we were talking about implementation details. So as we look to roll this out, what are we looking at adding to our site when -- like when we take on XState as a dependency, what type of bundle size and what are we really looking at in terms of what changes functionally in our app?
David: So XState does a lot, first of all. It has to adhere to the entire spec, which is pretty large. It's doing a lot more than a typical state container would do, just because -- I mean, even if you go to the w3.org/scxml, there's a lot going on there. Right now the package size of XState is about the same as MobX. It's going to be about 14 or 15 kilobytes. Yes, it is a lot, but it's also doing a lot of things that you don't have to do manually anymore. For many projects, especially the projects that necessitate the complexity to use XState, 15 kilobytes is really a small piece of the pie.
Jason: If you look at Redux -- but you also add in thunk. Then you also add redux-sagas. Then you have all of these different pieces that start coming in.
David: It is going to be a little smaller, but I don't think it's actually -- oh, wow. Yeah, that's already like 15 kilobytes. I don't think it's directly comparable because XState is not solely a state management library. It's a state orchestration library. So comparing XState to Redux doesn't make sense, but comparing XState to Redux-saga, that makes sense. That's because the primary purpose of Redux-saga is to orchestrate state changes. So if there's one takeaway for your viewers, there are two different types of state libraries. There's state management, which is like here's a nice container, do whatever you want with it. And there's state orchestration where it's like let's formalize how your state changes over time due to events or things that might happen. Again, Redux-saga is a great example of this. MobX is also in that area. You have just MobX, which -- honestly, it's magical -- but you have MobX-state-tree, which has these async flows as well. This is 21.7 kilobytes. That's why it's a different category. With that said, there's also XState/fsm. If you're using just flat machines, it's under 2 kilobytes. If your complexity does not necessitate XState, use XState-fsm. It's going to be absolutely tiny, under 2 kilobytes.
Jason: And everything we did in here, this all works in FSM, right?
David: Yes, that stuff does. The after with the timers and everything, that stuff does not. So what we did, you know, with the animation, that's not going to work in FSM. Anything else --
Jason: But we could do the same thing with like an entry, right? We could do an entry, set time-out, and transition from there or something?
Jason: We lose a little bit of the built-in stuff, but the flexibility is still there to handle a lot of these types of things. Actually, I didn't know that after existed. I ended up using entry to do a set time-out in my state machines so I could do time transitions like that. Those are all like -- but anyway, if you're really trying to code this down, FSM is going to be pretty good to get you going. Also, I found the things you get to cut out of a code base when you move to state machines typically will more than make up for the 16 kilobytes you'll add with XState. Just from my own refactoring experiences. I've noticed I got to drop several modules that made my life a lot easier.
David: Yeah, I also posted a link in the chat. You don't need a library for state machines, is the title of that. That's because you don't. At least for simple state machines. That's because you can express a state machine in any language, and people do. You go to any language, there's going to be many tutorials, articles, and even libraries on making state machines just because they are actually a common pattern. So this article talks about just the progression of as your state machine gets more and more complex, here are different patterns you could use in code without having to introduce a library such as XState in order to create a state machine. But as you get more complex, I describe why you would actually want to use a library such as XState. The reason is because when you're getting into nested and orthogonal or parallel states, it gets complex to implement yourself. It's like state functions. You want to reach for a state library so you don't reinvent the wheel painfully.
Jason: Yeah, it starts easy and gets really hard really fast. That's been my experience with a lot of things. Oh, we don't need that, let's just build a simple version. Then it falls off a cliff the first time you hit an edge case. Oh, that's why people use this library.
Jason: So I think that is -- but this is really interesting because I like how you're building up to it, right, and kind of showing where does the complexity come in and at what point does this make the most sense. But I -- yeah, I love this. I also just like that, you know, even as you're not using XState, you see familiar things in here. We see states. We see an initial state. We see what the actions are. So, even without using XState the library, we can still reason about this using the same set of logical deduction that we would use if we were using XState. So it still creates this common layer of communication.
David: Yeah, yeah. And as a bonus, you could copy/paste into the visualizer and have that just work. That's also the reason -- a lot of people, for whatever reason, don't like the objects and text of XState, which I understand it feels a lot like configuration, but that object syntax, like you see here, you could use without XState and it's just reading key value pairs from this machine dictionary or however you want to consider it. You know, you could just write a simple function for that.
Jason: Very cool.
David: If you didn't want to use an object and create your own functions, you're basically reinventing XState and doing all that. So, you know, that's why I like the object syntax. It's copy/pastable.
Jason: Yeah, the ability to make it copy/pastable like that, you're describing your data or your state in such a portable way, I guess. That feels really good. Here's what I like about it. What I like about it is that in previous episodes on this show, I've been able to go to the visualizer, build my state machine, and then copy/paste it into my code and not have to do a bunch of fiddling with it to get it to work. You can really do that flow of like work in Code Pen and copy/paste it into the component at work and then you can kind of plug it in there. Then it gets more complex, but if you need to share it with another thing, you copy/paste that into a shared component library that gets published as a package. Then you can -- it just becomes so much more portable than like, okay, I've got 15 utility functions that do different boolean states and a bunch of transition logic tucked away in this utility package and a bunch of show/hide stuff over here. It all gets very hard to manage very quickly.
David: Yeah, or maybe in the future, even having a visual creator where you just drag/drop nodes, make your state machine visually, and have that JSON spit out. Which, by the way, is something I'm working on. Even if you dislike the whole object syntax keep in mind it's a necessary evil for something like a visual editor.
Jason: That -- I'm excited about that. How does one get on to the mailing list for that?
David: Well, I have a newsletter, stately.dev. I send out newsletters very infrequently. So only for very important stuff. I think there's five so far. The next newsletter that's going to come out is going to be like a teaser/preview of the upcoming creator.
Jason: I've just -- I don't know what happened.
David: No, I think it worked. It goes through button down email. I think you were already registered or something like that.
Jason: Hm, okay. (Laughter) So I'm in. I want to see this. It sounds like exactly what I'm looking for. That makes it collaborative even further. So now our design team can work with us to help define app states. We can -- you know, that unlocks -- holy crap, what if we plug in from there to figma. Now we get clickable prototypes based on state machines that translates into the final product code. This is some next-level stuff. There's so much potential here.
David: Right, yeah. I have way too many ideas about this. Including something I realized. I wanted this years ago, but all the reducers we make for Redux or VueX or whatever, I just want some registry of them where it's like, oh, I want to patch a reducer or some authentication reducer, substitute reducers for machines, I would love to just pull one off the shelf, customize it to my needs, and use it, knowing that someone has already worked on it and made it bullet proof, instead of having to write all that logic from scratch. I think 99% of the reducers we do write and 99% of the logic -- number one, we write manually, number two, it's probably been written thousands of times before in some form in other projects. So, yeah.
Jason: Nice, yeah. I think I just realized in this exact moment that I broke my email address. I was like, why did it say there's no mx record? I know why. I transferred my domain. My email is broken and has been for like a week now. Hope nothing important happened. Great, exciting. Okay. So David, I am so pumped about this. I'm so excited to see more about kind of just where this goes as this grows in popularity and more and more people start to understand the power of what this unlocks. With that in mind, where should someone go if they want to take next steps with XState or with state machines in general?
David: Well, there's a few places. Of course, there's the XState documentation. You could follow me on Twitter. I tweet about state machines way too much. We also have a Discord community. So discord.gg/xstate. Was very happy to get one of those vanity URLs. And yeah, actually, that's a really -- I've been surprised. It's a community that has -- we've only started like, I don't know, about a month ago. It's already grown to over 600 people just really curious about this stuff. There's many sections for help, whether you're using TypeScript, React, just anything. There's actually a lot of people there besides me who are willing to help. We also have ideas showcased, lots of resources. There's always an interesting discussion happening in Discord. I'm really trying to sell this Discord community. It's great, though.
Jason: I'm real excited about it. I love Discord. I'm part of the Party Corgi Discord, which is a great place if you're just trying to learn how to -- if you're just learning, creating things, any of that stuff, it's super fun. I don't remember what the command is, if somebody wants to drop that link. I've loved it as a place to go that's like low stakes, not a Slack group. It doesn't -- you know, the content is moderatable. There's voice rooms. I'm a big fan of discord. I guess a lot of people are since they just got acquired by Microsoft.
David: They're not acquired just yet. There's talks to, you know, make it part of Microsoft. I work for Microsoft. I don't like Teams. So hopefully it's sort of -- I don't know if it's going to replace it, but make it better or something like that.
Jason: (Laughter) Yeah, and it would be good to see some real competition in the work force to Slack. So I'm excited to see what kind of innovation that drives. Good on them for doing such a good job that they could get, what, a $10 billion acquisition. Holy crap.
David: Hey, that's a bargain. Remember Slack was acquired for $28 billion.
Jason: Oh, my god. I can't even. Those numbers don't process in my head.
David: That's a buy one, get one offer.
Jason: (Laughter) Oh, man. Oh, wow. Excellent. All right. So with that, I think we are just about out of time, and I think we've hit a pretty good natural stopping point. The chat is -- here we go. So many Corgis. But yeah, y'all, thank you so much for hanging out today. This has been super fun. David, I really appreciate you coming on again. This is always great. I love talking about not just the code but the why of the code, and I always enjoy talking about that with you. Do you have any parting words or final websites you want people to check out?
David: No, I think I said everything. Thank you so much for having me on the episode. It's been fun, as always. More Corgis than last time, but you know.
Jason: A lot more. That's for sure. Chat, remember as always, this show has been live captioned. We've had Rachel here with us all day doing the live captioning, which is on the learnwithjason.dev home page. She's here from White Coat Captioning. That's made possible by our sponsors, Netlify, Fauna, Auth0, and Hasura. Check out the schedule. We have some good stuff. We're going to do more state machines, but this time we're going to do it on Kubernetes. Let's see how that goes. Then we're going to do builder io, which is a visual builder for doing Shopify and Next. I'm really excited about that. We have Prince coming back to do real-time Twitch notifications with the event sub-API. That's going to be a lot of fun. So much good stuff coming. Check that out, add to Google calendar button. It's going to be so much fun. All right, y'all. We're going to go find somebody to raid. David, thank you for hanging out. We'll see you next time.
David: All right. Thank you for having me.
Closed captioning and more are made possible by our sponsors: