Replacing a live front-end legacy code – The bottom to top migration strategy

This article is written about work done in 2016 – and the technoloigies mentioned match the time:

Here’s the deal – A company has an existing product with outdated technology, an old legacy code which is unmaintainable and unextendable, essentially, written with old technology. The company in question understands that it’s time to move forward, catching up with the present day and time.

Unlike the usual method of rewriting an entire product from scratch (top to bottom), there is a better way. This includes building a new front-end feature and immediately using it within the old legacy product. This methodology works even when the technologies are light years away from each other.

I used to work for a company like that. Before I started working there, their initial starting point included an old product which was an auto-generated HTML code out of a java backend, also known as “Automagically generated JS, CSS, and HTML”. This happens to be a front-end developer’s true horror.

This company decided to rewrite its product from scratch, with all new technology (Javascript and a Python API). They recruited developers, product specialists, used the best resources, and meeting hours. This was the first time that this company had a big front-end project ahead of them.

During the following year, they had an old legacy product that was live. It was making money but had minimum resources for maintenance, and no new features whatsoever.

On the other side of the development department, a new pile of fresh code and architecture was just beginning to be written. Unfortunately, it piled up and laid there without anyone knowing about the product or its users. The feature set wasn’t that big, but because this product was supposed to replace an already live product, no one could cut the scope of its features.

After more than a year, the freshly written code and architecture failed to reach its deadline, and there was no end in the horizon.

The project was canceled.

So there we were, with a year’s worth of development time and fresh code, but none that was usable. We had a product that was not maintainable and a business that needed to keep working and growing.

We all wondered, how could we all solve this horrible problem? What would have been the best way to make up for this lost project before us? How could we revive our motivation and bring back trust to the front-end department in the company?

I was called on to find a solution – and thankfully, did. The solution focused on the business. For a developer that always wanted the cutting-edge technology to be written from scratch – it was a difficult solution to understand, at the beginning.

The idea  was to reach goals in a better way:

The solution included giving the company the possibility to maintain and add features to its old applications. And, in such a way that also took the company forward in the direction of having a full new front-end application as needed. The solution enabled us to transparently transition from an old product to a new and cutting-edge front-end product.

Unlike the original method that was used which included rewriting the whole product from scratch (i.e. from top to bottom), the new method  was from bottom to top. I determined that we could build a new front-end feature and immediately use it, and we could make a profit and learn from it.

This also enabled us to stop at any point, and to shift resources at any time, without any loss. This was because every new feature was already in production, and embedded in the old application, but separated and detached from it.

This solution undertook important calculations and measurements:  

  1. Time / Resources
  2. Risks
  3. Scope
  4. Technical limitations

Technical Problem:

In all actuality, the legacy product was very limited from a UI perspective. We were bounded only by the UI elements and functionality that it provides. And, it is not a front-end friendly framework at all – it is a closed product.

Even worst –  it was mixed in with badly implemented JS code, on top of old product, which “helped” overcome the above problem in the past, this layer can be called: the hacks layer.

It was unscalable for code/feature extension, and since each update is global, it can easily break style and functionality.

We must keep developing the money-maker products, and we cannot merely rewrite it.

When addressing a migration from an old technology to another, we developers always think about a total new re-write. But if you are able, technically, to isolate your new code, components, and UI elements – you can do the re-write in a much more efficient way.

Infrastructure preparation:

On top of all feature requests, we must implement a few front-end infrastructure features:

The idea of this preparation job is to offer a set of front-end tools for easier maintenance, deployment, and development:

  • E2E tests (wooha, Selenium)
  • SASS compiling
  • Minifier / Concatenator
  • Angular
  • Bower

Now to the real work:

JS

We chose Angular.

Most importantly, the new code must be encapsulated, and most importantly, we must not rely on the DOM.

We found Angular the most encapsulated framework, allowing us to separate our JS code and HTML from the old container.

Each component must contain an angular view and have the ng-application root. We don’t want Angular to waste resources on listening to the whole DOM (and conflicts with the legacy lib). We encapsulated the page too small Angular apps.

CSS

Style separation: The Style lib

The style we came up with was the most complicated part.

CSS is inherited. There is no easy way to encapsulate an element from a CSS rule that applies to this selector (There is one way, but it is not supported by any IE, and we could use other tricks, that looked hacky).

So, we have an old framework, with a big pile of CSS, and with various rules that select tags, classes, states, and most importantly – deep rules, such as:

body form label input [type-text]{}

There is no way to write generic rules that can predict and override them.

Also – When we write a new feature, we cannot determine what overrides what. There is an option to check in the development tools, but this is a very long process, especially for each rule.

The solution was a separate UI lib. We generated two CSS files: The old Lib.css and the newLib.css.*
On our portal, we included both files.
On a separate static HTML page, we included only the newLib.css file.

When a new feature is being built, or an existing feature is being rebuilt, we must first build it inside the static HTML. Thus, creating it’s CSS rules in a clean, isolated environment.

At this stage, we know that the independent style we included on the page will work when we remove the oldLib.css file.

As for the rules from oldLib.css that are overriding the newly created rules (some !important or deep nesting rules from the legacy code that we cannot remove), they were minimal in numbers and got their special overridden section at the end of newLib.css

Once the oldLib.css file is removed, we can then remove the sections. We will then have an immediate working product, with minimum need to adjust and fix.

Results:

As for the time spent writing these lines, many of its features were overridden, and many new features were built, all with a new front-end code. They are all live, which immediately allows the company to earn money from the invested time they spent.

In this case, the old legacy product is maintained again, it is also extendable, and one day, it will get rid of the old code easily because of this separation.

What we created allows us to change resources without any problem whatsoever. No waiting on code that will never see the light of day or make any money.

And, on the developer side, we are able to do cutting-edge technology, using the tools that we would have used in a complete rewrite project. But, now we have an even better, harder, and more complicated task. This challenges us more and helps us to find solutions that other developers who use frameworks out of the box don’t need to deal with. It forces us to need a better developer, better learning processes, and a great backup and belief from the company management that this is achievable.

The above solution gives us the possibility to maintain and add features in such a way that will also take us forward in the direction of having a full new front-end product, in its old automagically generated front-end. It also enables us to have a transparent transition of the old product, to a new cutting-edge front-end product, which is miraculous.

* not the real names of the files

Leave a Reply

Your email address will not be published. Required fields are marked *