Frontend State Management: keep it simple
Tags: frontend, javascript, webdev, programming
Published: 2022-05-05 16:42:06


## TL;DR
State is the data that changes in an app. It refers to the "state" of that dynamic data at a particular point in time.

Those changes (state) are the main cause of complexity in software, and complexity is the main cause of the poor software that causes huge losses and frustrated users every year.

By acting upon the **Accidental complexity** (code implementation, avoidable), while understanding the **Essential complexity** (business logic, unavoidable), and by following some well-established patterns, we'll be able to make simpler apps that make people happier.


## Complexity
Defined as that what makes the programs difficult to reason about, it is considered the main cause of the problems and costs associated with the software industry, $2.08 trillion in the US just during 2020 ([The Cost of Poor Software Quality in the US, CISQ, 2020](https://www.it-cisq.org/pdf/CPSQ-2020-report.pdf)). All that money means frustrated users, people wasting time, confused, and writing to support@fix.it

“The majority of the cost of a software project is in long-term maintenance. As systems become more complex, they take more and more time for a developer to understand” ([Clean Code, Robert C.Martin](https://www.amazon.es/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882). [Summary](https://gist.github.com/wojteklu/73c6914cc446146b8b533c0988cf8d29)).

“The biggest problem in software is complexity, large systems are hard to understand. Unreliability, late delivery, lack of security, poor performance... can all be seen as deriving ultimately from complexity” ([Out of the Tar Pit, Moseley - Marks](http://curtclifton.net/papers/MoseleyMarks06a.pdf). [Summary](http://kmdouglass.github.io/posts/summary-out-of-the-tar-pit/)).

We can start simplifying the complexity by dividing it into 2:
* **Essential** (unavoidable): related to the business logic/problem domain of the software.
* **Accidental** (avoidable): related to the code implementation details.

What is in our hands as developers is just to act upon that **Accidental complexity**. This is our power and responsibility, to translate the business requirements (Essential complexity) into the simplest working software possible (Accidental complexity). In order to do so, we need to understand what is causing so much **Accidental complexity**.


## State, the main culprit
“We believe that the major contributor to complexity... is the state and the burden that this adds when trying to analyze and reason about the system” ([Out of the Tar Pit, Moseley - Marks](http://curtclifton.net/papers/MoseleyMarks06a.pdf). [Summary](http://kmdouglass.github.io/posts/summary-out-of-the-tar-pit/)).

The state can be seen as the dynamic data that flows through an app, like water. Frontend apps can be seen as "state machines" (pipe systems) that the users interact with it order to Create, Read, Update or Delete state.

Is the impermanence of the state, its variability, the source of all that complexity. We need to fetch and distribute the state, but we also need to be aware of any State change and update it.

A State change could come from 3 places:

* **UI**: data coming from the user (input data, monitoring data (e.g. form value, location...)).
* **API**: data coming from APIs (backend, service worker, local storage...).
* **Meta**: data coming from the app itself (routing data, loading data, browser data...).

As a frontend devs, I believe we spent most of the time dealing with **User state** and **API state**, while we frequently rely in frameworks and libs to deal with the **Meta state**, just reacting to it (e.g. ngOnInit, componentDidMount...).

We could say then, that **User & API state** is the main source of the frontend suffering. When managing the communication (state) between the User and the API, is when we add most of the Accidental complexity (code implementation) that makes our brains struggle to understand what is going on.


## Patterns: better to be consistently good than occasionally great
Applying patterns and conventions consistently makes live more predictable and efficient, lowering the effort to understand it. We are able to survive every day because we all know what a red light means, right?

When it comes to state, the following 4 patterns/conventions are really helpful to simplify the state management:

### Single Source of Truth (Store):
The state is kept in just one place, from there it is consumed by the rest app. The app's state can be concentrated in one store (Redux) or sliced into multiple stores (Flux).

Stores improve discoverability and clarity, avoid duplicates and guarantee that the state is correct and updated.

### Unidirectional data flow:
The store has a single way to read and to write the state, and both are separated (Command Query Segregation). This provides a clear mental model where the components are down and emit state change requests up, and the store is up, “performs” the state changes and provides the updated state down to the components.

### Reactivity:
State updates are emitted by the store so the interested parts of the app (subscribers) can react to them asynchronously. The state is not pulled synchronously from the store but subscribed/observed, guaranteeing that the last version of the state is consumed automatically.

### Immutability:
An immutable value can’t be changed, when edited a new copy is returned.

In Javascript, Objects can be edited by reference, opening the door to unpredictable mutations that can have unintended effects on other parts of the app, leading to hard to detect bugs.

Avoiding state mutation makes the code more performant and easier to understand because data changes become explicit and obvious.

### Encapsulation / Single Responsibility
All the state manipulation is concentrated in the Store/s. UI only fetches and emits the UI state, API only fetches and emits API state, all the business logic (format data, POST...) should be concentrated and encapsulated so it can be easily found, modified, extended or replaced.

## Keep it simple
Adopting these patterns keeps the state flow simple, making clear the 5W of any state change (who/what/when/where/why). Once you know that, everything is simpler.

The good news are that most of the state management libs already apply those principles. I believe that the key is to start using them when necessary, this is when the Accidental complexity added by our code is bigger than the Accidental complexity added by the library.

That's it, thanks for reading :)

References:
* [Out of the Tar Pit, Moseley - Marks](http://curtclifton.net/papers/MoseleyMarks06a.pdf). [Summary](http://kmdouglass.github.io/posts/summary-out-of-the-tar-pit/).
* [Clean Code, Robert C.Martin](https://www.amazon.es/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882). [Summary](https://gist.github.com/wojteklu/73c6914cc446146b8b533c0988cf8d29).
* [SOLID book, Khalil Stemmler](https://solidbook.io)