Table of Contents
Over the course of these articles, I am reflecting on why UI code is much more prone to become legacy as opposed to other types of software. As I identify the reasons behind this phenomenon, I propose an approach — not the most obvious one, but, as I will do my best to justify, is key to opening the door to making UIs legacy-proof and scalable as well as offering extra perks.
- Part 1 — Why does code become legacy?: By using a simple metaphor, I try to illustrate the problem and create a simple reference for the entire series
- Part 2 — Why is UI development legacy-prone?: Another illustration that builds on top of the previous one, further clarifying the problem
- Part 3 — How to make UIs testable and easy to change?: A concise survey of known approaches to UI in the context of the problem outlined in the previous articles and where they fall short as well as proposing a new solution
- Part 4 — React as a stateless view in Storybook: Example of what it means to have a completely stateless view
- Part 5 — MVU architecture in a React application: Example of how to make a completely stateless view interactive
- Part 6 — From Figma to React, Vue, Svelte, Preact, and Beyond: Exploring the legacy-proof (adaptability) benefits of having a completely separate view that matches designs closely
- Part 7 — Decoupler and the future for legacy-proof UI: Further exploration of perks that this approach allows that go even beyond legacy-proof code considerations
Part 1 — Why does code become legacy?
Why does code become legacy?
Writing code is like connecting two points.
Naturally, you would like to take the most straightforward path — a straight line, from A to B. This works for a lot of simple cases, but for the majority of real-world scenarios, you just can’t, because aside from connecting the points, you will have to move around the obstacles.
But, aside from the need to maneuver a little bit, there is no problem.
Let’s increase the number of obstacles in the path by an order of magnitude. The line just keeps on becoming more convoluted.
Now let’s make these obstacles move (which, I, unfortunately, have no animation for), albeit pretty slowly, but enough to cause us trouble of needing to reconnect the points. This isn’t the nice straight-line scenario at all anymore. This is getting serious!
Well, as if that wasn’t enough, how about we now make the points move around as well? And not only that, but will make sure that these points aren’t glued to the lines, and you have to follow them to make sure they stay connected. Well, that’s even more annoying!
But this isn’t everything! What if we only knew the approximate location of the points, and the only way we could tell is by being able to request their location every 5 minutes? How crazy would that be?
And yet this is pretty much what real-world development is often like.
In the context of this metaphor, the points are the functional requirements of what the software should do. It’s great when we know exactly whether our code connects to them properly. However, in reality, these requirements change (hence moving points), and we have to update our code accordingly. Yet, it’s not always easy to tell whether we connected the points correctly, and only through rigorous testing could that be determined.
The obstacles could be limitations in technology, performance, programming paradigms, various process, and everything else that could stand in our way — and there is always something — to which we, as problem solvers, are quite accustomed.
Naturally, given this dynamic setup, the code that started out with the best intents and practices possible turns into an ugly squiggle that bears testament to the tortures a developer had to go through to make it work.
This type of code is known as legacy — code that is very hard to change without everything going down in flames and that is as costly to refactor as rewrite it from scratch.
Like jumbled yarn, legacy code usually exhibits high coupling between pieces of logic, that similar to yarn you wouldn’t be able to disentangle easily.
This begs the question of whether there is something that we could do to avoid our code becoming legacy.
How to solve this problem?
Well, this problem is solved partially by the use of so-called design patterns — bite-sized solutions that we could assemble together like Lego blocks to make the process faster and more controlled.
But design patterns are not enough. It’s much more important to know that we’re going in the correct direction as soon as possible than going in the wrong direction “correctly”. And yes, there is a bunch of legacy code that follows great design patterns. In fact, the overuse of design patterns beyond necessary is called “over-engineering.”
We should know where the points are located as soon as possible and to do that, we need some automated, instantaneous way of getting this feedback. That’s what testing your code accomplishes. Not every test would do, but black box tests specifically check the points and not some parts along the path.
So, in order to solve the problem of constantly changing position of the points and the obstacles we need to meet two conditions:
- To know that we are going in the right direction ASAP (correctness feedback in the form of black box tests)
- Use some preconceived solutions to build the path faster and in a more controlled manner (design patterns)
As simple as these two objectives sound, they are pretty difficult to achieve within the context of UI development, and yet this is what we will try to solve over the course of these articles.
However, in these articles, most focus will be placed on the first aspect (the immediate feedback) requirement as this aspect proved to be the most painful, whereas the design patterns are relatively well studied and known, thus would only be mentioned as a brief survey.
Link to Part 2 — Why is UI development legacy-prone? Another illustration that builds on top of this one, further clarifies the problem
- Black Box Testing: An In-Depth Tutorial — This tutorial on Guru99 provides a comprehensive guide on black-box testing. It covers its techniques, types, methods, advantages, and disadvantages.
- Design patterns — A comprehensive guide to software design patterns. Each pattern is explained in detail with examples to help understand when and where to use them.
- SOLID Principles: Explanation and examples — This FreeCodeCamp post breaks down the SOLID principles in an easy-to-understand way with lots of examples.