Taming complexity: our move to state machines with XState

Enlarged view: Taming complexity: our move to state machines with XState

Taming complexity: our move to state machines with XState

A few months ago, our frontend was like a well-organized closet: simple, functional, no complications. Someone uploaded photos of their documents, we validated them, done. A simple, direct flow with no problems.

But, as every developer knows, things tend to get complicated.

Over time, that closet went from spotless to a messy teenager's bedroom. We kept adding authentication, digital signature flows and more scattered logic. Chaos took over our application.

That's when we decided to clean house, and XState was our magic tool.

From an MVP to a maze in Vue

When we shipped the first version of our product, things were pretty simple. We used conditional rendering in Vue to handle the application's different states and, while it wasn't elegant, it worked. Everything ran from a single route. But as the project grew, what had been an effective solution turned into an uncontrollable tangle of ifs and elses.

That tangle hurt more than readability; it hurt maintainability too. Every new requirement meant adding more conditional logic, which increased the complexity of the system exponentially. Bugs became more frequent, the time to ship new features shot up, and any small change could break critical parts of the flow. The system became fragile and clearly unsustainable to scale.

When the new flows for identity reuse and digital signature arrived, we realized it was no longer viable to keep patching the existing logic. The system had reached a tipping point where incremental fixes would not be enough. That was when it became clear: we needed a deep change in our architecture to keep moving forward sustainably.

XState to the rescue: a GPS for our application

When we looked at solutions, we considered options like Redux or simply adding more custom logic in Vue. We chose XState because it gave us a much clearer structure for handling our application's growing complexity.

If you haven't come across it, XState is a library that implements state machines, a concept that lets you model a system's possible states (for example: "initial", "processing", "completed") and the transitions between them in a very clear, controlled way. Instead of scattered conditionals, state machines let you define those flows declaratively and precisely, which makes the application's logic much easier to understand and maintain.

Redux is excellent for global state management, but it takes a lot of extra code to handle complex transitions between states. And Vue's custom logic, flexible as it is, had led us straight into the original problem: it was hard to maintain, hard to scale and hard for new team members to understand.

XState, on the other hand, let us model the application's states clearly, removing the chaos of conditionals and giving us a precise map of how states and transitions flowed. Its native TypeScript support also cut down on errors, and being framework-independent gave us the flexibility to move to other frameworks in the future without rewriting the core state logic.

Transformation: from chaos to clarity

Adopting XState let us restructure our authentication and validation flow from the ground up. We split each sub-flow into its own state machine. Then we created a “master” machine that orchestrated those sub-machines. Something like a conductor leading a symphony.

Best of all, every state and transition is now clearly defined.

The difference was night and day, not only because of the clarity in the code but also because of tools like the Stately inspector, which gave us full visibility into how our application's states behaved in real time.

With that inspector we could visualize each state machine and its transitions graphically, and get a detailed history of how the actors in our application communicate. That let us trace the events sent between actors precisely, spot potential bottlenecks and debug complex flows easily.

What about performance?

Beyond improving the clarity of the code, XState proved to be extremely efficient.

It let us define specific actions on each transition, save and restore states easily, and even handle events asynchronously.

And best of all, with no external dependencies, keeping the bundle light. A real relief.

A few bumps along the way

Implementing XState wasn't a completely obstacle-free path, and that's worth knowing if you're considering adopting it in your project.

Like any new technology, it came with a learning curve, especially for team members who weren't familiar with the state machine paradigm.

XState offers plenty of advantages, but learning to use it effectively took time and will probably take more time and effort as new developers join the team.

Even so, what we gained in clarity and organization far outweighed the initial challenges.

What we learned

Looking back, the biggest lesson was that good architecture can transform a project completely. Moving from scattered, chaotic logic to a clear, organized structure let us take back control of our application.

We also realized that investing time in learning a solid tool like XState pays off in the long run, even if it looks expensive at first.

The decisions we made back then still benefit us today, not only in efficiency but also in how easy it is to add new features without compromising the stability of the system.

Our map to a chaos-free frontend

Adopting XState completely changed the way we handle complex flows in Vue. It gave us back visibility and control over our application's state.

If you find yourself navigating an ocean of complexity in your frontend application, I'd recommend giving XState a try. It's not just a tool; it's an approach that changes how you see and structure your code.

At Soyio we love taking on this kind of challenge and looking for solutions that don't just fix the immediate problem but build a more solid, scalable future.

For us, the real key is creating clear, robust solutions that are easy to maintain. Solving complex problems is part of who we are.

Need privacy with high standards?

Contact us