XState changed how I think applications

I first heard about state machines in December 2019. It was on episode no. 206 of „Syntax", a podcast about web development. I immediately fell in love with the idea behind it: Having an explicit state to rely on. But building my first project last month with XState I realized it can be much more than that.

What is a state machine / XState?

The basic of the xState ecosystem is a core library to create and interpret state machines. It sounds fancy at first, but it follows a set of simple rules, everyone can understand - not only developers but also every other stakeholder on a (software) project (Designers, product owners, managers or QA or testers)

Let's start by giving more meaning to the term state machine itself.

State means a condition that is distinguishable from another or multiple other conditions. For example, you can either be awake or you can be asleep, not both at the same time. You can sit, stay or walk, but only one of those at once. When we think about software products we see the same patterns. Data can either be saved or not saved. A form can either be valid or invalid. A user can either be logged in or logged out.

The machine part of the term state machine indicates two things. First of all, it means that states can be changed. One state can transition into another. For example, the machine can transition the state of asleep out into being awake.
Second, the term machine indicates it transitions state in a predictable and reproducible way. ... Predictable? Reproducible? Sorry... We will get to it.

Tackling complexity in app development

Building a simple application is not the challenging part of software development - that is the fun part.

The hard part maintaining code quality while scaling the application. That's why we introduce tests and all sorts of quality standards to not lose track of which functionality we implemented. If we have the time and resources we write documentation to let others and our future self know how it was intended to work when we first build it. The problems we solve are complex and we know that.

What state machines can do is guide you through this complexity. They can not only tell you what a current state is but also how to get from one to another. The way they do that is by clear communication of which state changes are possible from a certain starting point. If you are in the state of being asleep, going directly to commuting to work is quite unimaginable. There must be at least states like making coffee or brushing teeth in between.

That's what I earlier expressed as being predictable. State machines give you a fixed set of possibilities that a process can do. You can furthermore rely on the behaviour to be the same every time you start a new process alike. You must go through making coffee and brushing teeth before being ready to commute to work. The behaviour is consistently reproducible.

All these features of state machines make them perfect for developing applications. But it starts way earlier.

State machines from conception to deployment and back

When developing a new feature we often start by transforming a business logic into a flow chart representing an application logic which then is transformed into variables, functions and data models. The resulting code is hard to read and reverse-engineering the original business logic is often a tedious task - especially after some time and with wrong or no documentation. Which is - let's be honest - the usual case.

I think XState can be a good tool to bridge this huge gap between planning, implementation and later customization of a feature. With state machines at its core, XState builds a whole ecosystem of tools around the work and visualization of flow and state charts. The packages they offer makes it easy to use the power of state machines in every modern web tech stack.

However, I think the most valuable integration of XState is not its versatility in use cases or integrations. It is the visualizer that unleashes the superpower of state machines. As mentioned earlier I think that every stakeholder of a software development process can think and work in state machines. In fact, often they already do! Designers think in states like invalid, valid, active, disabled or loading. Product people think in states like todo, in progress, done. Writing a state machine in code looks complicated, but having it in a visualizer eliminates almost all complexity and makes it actually playful to work with.

We can start with a new feature of an application like we always do. We begin by planning it as a flow chart. In the past, we would have used LucidChart or whimsical here. Now we XState visualizer.
We can mock the entire business process as a state chart and see all the logic in a playful way. If we want we can also add real functionality to it and invoke functions and services along the way.

When we are done, we can take the same code we created in the state machine visualizer and drop it into our application. We might need to tweak it and handle some edge cases still, but the logic part is ready to use. Furthermore, we can take the same state chart and go to a designer to provide visual designs for the different states of the application. There is now a single source of truth for the application logic in all departments.

And the best thing as a developer is: If at any time after ages a new feature needs to be added to this existing program or if anything needs to be changed, you can always just throw your state machine code into a visualizer and see exactly how the individual parts of the program are interconnected. Code becomes so much more intuitive to understand and to reverse engineer.

I'm not saying that we don't need to test anything anymore or that this solves all the problems of scalable application development. But it is certainly a tool that can help us to improve the communication between many participants and foster a unified understanding of the software we build. Often we add new technology only to later find out that complexity did not shrink but got even more unmanageable. From my experience state machines and especially xState have been the exact opposite. Just like typescript, it gives me so much more confidence in the shipped functionality with just a few lines of structured code added.

I would like my team at elvah to try it out in the near future.

Where to go from here?