Any fool can write code that a computer can understand. Good programmers write code that humans can understand. - Martin Flower
Many UIs are lousy. I'm not referring to the fact that the User Experience is bad or that the website doesn't look great, I'm referring to the fact that they crash, lock up, mislead the user and are a nightmare for the programmers maintaining them.
I strongly believe that this happens because many developers are using an imperative, event-driven programming approach which results in code riddled with huge number of convoluted conditional logic. If one could somehow reduce the number of conditional behaviours, the code can get easier to grasp, test and maintain.
Techniques based on declarative example can help achieving this.
In event-driven programming you have an UI component that generates an event, for example input onchange, which triggers an action that is directly attached to the component. The component implementation decides how to react to that action, making the action tightly coupled to the event. Basically, such a component is waiting for the occurrence of some external or internal event, such as button pressed or data arrived, and they react by performing the appropriate computation.
The problem is rooted in the difficulty of describing this behaviour in ways that are clear, easy to understand, enhance and test.
What is the problem with this approach?
First, such a system neglects the context or current state in which the event takes places and because of this it generate a huge number of convoluted conditional logic, making it hard to handle all the states our application can be in.
Second, the actions actually executed are determined by events which largely are in an unpredictable timing and order making the path through the code to differ each and every time.
I think the easier way of describing the difference between declarative programming and imperative programming is through a real life analogy.
Suppose you have to go to the headquarters of a company for an interview. Let's describe how you are going to reach their office through each of these techniques.
The imperative approach of going to the interview is: Exist your house, turn right, at the forth traffic light make left and then straight ahead until you reach the metro station. Take metro number 1 and get down at the station A, exit the metro station through exit 2a. The office is right across the street.
The declarative approach is: The address of the office is 23rd Huston Avenue, Boston, MA.
The key difference here is that with declarative programming the steps are abstracted away from you. In order to reach your destination you must have a GPS or another method that knows how to get you to the address you provide.
To sum up, the difference between the two programming paradigms is that with declarative programming you describe the desired result without explicitly listing all the steps that must be performed while in imperative programming you must describe exactly each step that will be executed.
There are several declarative programming paradigms and libraries you can use but the most used nowadays are react, reactive programming (rxjs) and finite state machines with state-charts (xstate).
React
React is probably the most known declarative library out there. In a React application you are simply stating that the view should look in a certain way given a certain state, there are few things that can go wrong with this approach.
Moreover, React allows us to do declarative programming without the drawbacks that are usually associated with this paradigm: performance. In React, the DOM manipulation is abstracted away from us through the Virtual DOM which handles the performance issue of making changes to the DOM often.
Reactive Programming Using A Library Like RxJs
I am a huge fan of reactive programming which is a declarative style of programming. It makes it very easy to express static or dynamic data streams and moreover it is easy to express an inferred dependency within the associate execution model that facilitates automatic propagation of the changed data.
I first learned about reactive programming and rxjs when I started with the new version of angular a few years ago. I must admit, at the beginning it was a little bit difficult to understand how everything is tight together (because I was used programming in an imperative way) but as time passed by and I got more experience and knowledgeable I started to really see the benefits.
I mean, how awesome it is to create streams of data from different sources like search input, server data, routing data, etc and then combine these streams in other streams that you finally use to create the state of the UI at any given moment? I tell you, it's amazing!!! And makes your code really, really simple to reason about.
Finite State Machines Using A Library Like XState
State machines are one of the most effective methods for developing robust UI. Our apps have lots of states that they can be in. We usually handle states like, loading, success and maybe failure. What about other states our app can be in?
Imagine the number of combinations you can have if you have five booleans in your state, around 120. Crazy, right? There is more to this, our app should not even be in many of those 120 possible states.
All these state problems can be tackled by using a state machine. A state machine encourages you to plan your states, to declare all your possible states and all possible transitions removing the impossible states and reducing the number of total states.
Declarative programming makes it more easy to reason about a piece of code because of the following:
Previously published at https://codemachine.dev/how-you-can-simplify-your-ui-code-through-declarative-programming