Check out a free preview of the full State Machines in JavaScript with XState course:
The "The Problem with Adding Features" Lesson is part of the full, State Machines in JavaScript with XState course featured in this preview video. Here's what you'd learn in this lesson:

David explores the practice of writing bottom-up code, which leads to jumping straight into the code instead of properly modeling an application. Bottom-up code is difficult to test and the introduction of bugs is more common.

Get Unlimited Access Now

Transcript from the "The Problem with Adding Features" Lesson

>> The way that we typically code, is by doing what's called the bottom-up technique. And what that means is, we put all of our application logic inside of the event handlers, or wherever the pertinent user action occurs. For example, if we have a button, and in fact, we're going to just go ahead and make a button over here.

[00:00:22] So in this welcome screen, let's add a button. And it's just going to say click me, and you'll see that the button is there. We'll give it an ID button just to track it, all right. So you'll see a click me button over here. Now, generally what we do, is we add event handlers to things where we want things to happen.

[00:00:52] So I'm gonna go ahead and do that over here. I'm going to first get the button, so const elButton equals documents.querySelector, and I'm gonna just get that by. And then we're going to add an event listener to that button. So elButton.addEventListener, click. One minute, we are going to, for example, console.log I am clicked.

[00:01:25] So when we do that, let's open up our console and see. We see that the button says I am clicked. And so, this is generally how we manage our states. We put all of our application logic, inside of the event handlers themselves. Now, this isn't very maintainable for a few reasons, but I'm going to demonstrate one of the reasons why over here.

[00:01:48] So let's say that this button, we want it to track, or we want it to actually load something from an API, or just do something in general. So we could console.log, loading data, and let's also log So what this is going to do is when we click the button, you could just pretend that we're loading some data, and that's going to show up right here.

[00:02:14] But immediately you see the problem, when we click that button, we see that we might be loading data more times than we need to. And in fact, when data is loading, we really don't want that button to be clickable again. So one of the normal things that we do, is we will disable the button.

[00:02:35] So I believe it's elButton.setAttribute disabled, we could just set that to true. And so, now once the button is clicked, and I think there needs to be a string. It will be disabled, and so we can no longer click it. In fact, You could go ahead and style the button so that when it is disabled, the opacity is lower.

[00:03:09] So now you'll see when we click it, nothing happens. But the problem with this is that, now your application logic lives inside the event handler. And when you have more event handlers, and more things you could do in the app, all of that logic is going to be spread out throughout your app, instead of located in one central place.

[00:03:31] And so that's what bottom-up code is about. It's about jumping straight into the code, instead of properly modeling our application logic, so that we could have flexible, maintainable, and reusable logic. And so this means that code that's bottom-up, is really difficult to test because we have to know exactly where all the logic lives.

[00:03:53] And we have to make sure that we activate that logic in the event handler, instead of doing something as simple as sending any events. So this also means that bottom-up code is difficult to understand. And that is because when a developer on your team asks you, what is the logic of this, what's the flow of this?

[00:04:14] They have to look at all of those event handlers and all of that spread out logic, and sort of piece it together in their mind, instead of having a central place where that logic can live. And so this means that your code, almost guaranteed, will contain bugs. And that's because it's very hard to find the central source of making sure the logic is correct, and making sure that impossible states and impossible transitions can't happen.

[00:04:40] In fact, this means that the code is also difficult to enhance. So if you have bottom-up code where your logic is spread out through all of these event handlers and all of these ad hoc places, like at the .then callback of the promise, or anywhere else. Then, in order to add a feature, you have to find all of those event handlers and all those places where the logic lives, and make sure that they don't interfere with the logic you currently have.

[00:05:07] You've probably run into this before where you add a feature, and all of a sudden five other features don't work anymore because of unseen bugs. So this is why features make it worse. The more features you have, the more ways that a user or services with your app can interact with your app, and the more things that can happen.

[00:05:30] In fact, it's combinatorially more. So there's this concept called combinatorial explosion, where basically if you have two choices, then that means that you really have four possibilities, because it's two to the power of two. If you have three choices, that's eight possibilities or two to the power of three, and that just gets exponentially bigger and bigger.

[00:05:53] So you could consider each one of those features, exponentially in more ways that the app could be represented, and that the app could work. And so it's very hard to test every single one of those flows. And you have to really program defensively, in order to make sure that you're not accidentally introducing bugs.