I’ve written a few front-ends user interfaces during my professional career. Each of those front-ends either went through, or were going through large rewrites. In some cases, people considered the front-end rewrites as an inevitability - a natural progression which was required to get to the end goal. Some of those rewrites were the cause of a failure, and others were even caused by success. In this blog post, I’ll only be considering rewrites due to engineering decisions rather than rewrites for UX.
Partly, the reason user interfaces are so commonly rewritten is because the cost to rewriting is low. If you consider the entire system, nothing is dependent on the actual user interface. In contrast, changing some downstream backend service will need to handle versioning and communicate changes in expectations of its interface.
Another reason is that the problem that they are trying to solve changes - companies grow and add more features/products to their portfolio. Mergers with other companies require integration of all new user interfaces, which need to be transparently integrated into their existing user experience. And most commonly; the initial problem of getting the user interface out to empower customers has expired - and now the next problem is changing it so that it’s easily extensible by a quickly growing engineering force.
If you did things right, you shouldn’t be doing rewrites due to UX design changes. The components themselves should be dumb & be easily switchable with differently designed components.
There are a few interesting & intuitive patterns that have taken ahold of web UI development. With the advent of some front-end frameworks such as Angular & React, developers have been empowered to implement various clean architectures. With the introduction of components, state can be passed down the component tree. By abstracting away the necessity of DOM manipulation, and providing a method of passing state across reusable components, user interface development became easier and cleaner. There is also the concept of “dumb” components, which have no state transitions & essentially just take some inputs that somehow change how they look. This can take the burden of styling off of the developer.
Both Angular & React provide component lifecycle hooks, which allows you to do things at different parts of the component life-cycle. This allows you to have hooks at which you do some asynchronous work to obtain the data that you need to display your user interface.
Angular & React are extensible, which allows you to use a state-management tool alongside them such as Redux. When your component tree gets too deep, redux can simplify state changes and logic necessary to process those state changes in a predictable manner, without having to pass it through multiple layers of components. That way, the intermediary components can be simplified without having to burden them with knowledge of some data that they are not responsible for.
I’ve also seen success in implementing an explicit model/view/controller pattern with React. My observations of this is that it makes it difficult to make a “deep” interface with this pattern, deep here referring to how many layers of “smart” components are housed within eachother. I beleive that this is an advantage in disguise, as too many layers of smart components can make following the transformation of your data as it goes down the tree confusing.
At the end of the day, these frameworks allow you to control the data flow through the components and transform them in ways such that they can easily be displayed. What you choose shouldn’t matter, and should really reflect the skills of your own engineering force and your hiring pool. Popular is not better, but in the current climate of engineer shortages, choosing the frameworks which have the highest usage seems like the best idea. However, I’ve seen rewrites occur because of the framework in use becoming antiquated.
You can be clever and design your user-interface so that the UIs for each feature are completely isolated from one another. This reduces the damage of a rewrite, as you can upgrade a micro UI one at a time. Take AWS for example - each team manages their own user interface. The EC2 user interface & the parameter store user interfaces are likely housed in seperate repositories. While they look similar due to reuse of internal web components, they can have drastically different architectural designs. Some user interfaces may need to be more interactive and have large amounts of state changes, so those teams can decide to use redux. Another user interface may simply display some data, so it can get away with a much simpler design. This, in contrast to a monolithic approach, allows for easier growth of the product and even 1:1 mapping to teams that build these UIs (see Conway’s Law).
In this design, each user interface lives under a “masthead” with a static API that the “micro UIs” can utilize to do necessary work (such as obtaining an access token to make backend API calls).
This replicates similar developer productivity advantages gained by the microservice architecture. While the main selling point of microservices was the ability to scale services asymmetrically, along the way people discovered the developer productivity advantage as well. Having a walled garden around your code can enhance motivation by giving a feeling of ownership, and microUIs replicate this. This ownership can empower people to become “benevolent dictators” and keep things like test-coverage & code quality high. It can also prevent code rot from a particular service spreading across the system.
At the end of the day, the greatest computing constraint we have is the people actually building the software. Engineering designs should take developer productivity into consideration, especially user interfaces as they follow an on/off development life cycle. While transfers across different microUIs may not feel seamless, the trade off of having more productive developer teams can be worth it.
It’s difficult to look into the future and understand what problems you will need to solve, so sometimes redesigning your system is inevitable. However, think carefully about the costs incurred & the benefits acheived. Don’t do it because you simply fell for the “ooh new shiny framework” syndrome that is prevalent throughout web development. Instead aim for benefits that pay off dividends over the long term. Empower your team to deliver.
Note: the upload date is not the same as the written date. I uploaded this far after I wrote it as I was too lazy to fix my blog scripts. Well, its not like anyone reads this blog anyways.