Migrating a Frontend In-place

Using an nginx reverse proxy for pagewise migration

AJ Ribeiro
Bigeye
3 min readOct 12, 2020

--

Just like this, but with more Javascript

When I joined Toro Data Labs, we had an existing legacy UI which was built in the earliest days of the company to deliver an MVP. As the company and product evolved, we gained a better understanding of our customers’ needs and the technical capabilities we needed to build to serve them.

With this information, we decided to migrate to a new framework which would best suit our use-case and unlock the sorts of features we want to deliver to our customers. The framework we chose was Next.js running Typescript under the hood.

With an rapidly developing product and a fast-paced, competitive environment, we didn’t have the luxury of having our existing UI sitting stable while the rewrite was going on — but we couldn’t easily develop the types of product features that we wanted on our existing frontend either.

Plain old rewrites

Our team has seen a number of frontend migration efforts at previous companies, and they are often attempted as a complete rewrite, i.e. create interface B to replace interface A, and do it as a whole.

In practice, A does not sit and wait patiently for the migration to finish. Rather, development inevitably continues on A and those developing B find themselves constantly trying to hit a moving target.

This approach often results in either of:

  1. B ships but it is far behind schedule
  2. B never even ships because during the course of its development B itself becomes obsolete

The solution in our case was to perform a rewrite, but to do it incrementally and in-place. We would write new UI features and pages in Next.js, maintain/support existing features in our legacy UI, and lazily migrate pages as the business allowed. The fact that a migration was going on would be undetectable to our customers.

Our in-place migration

In order to achieve the migration we wanted, we leveraged nginx and its reverse-proxy capabilities. The solution we came to was simple:

  1. continue to run our legacy UI server
  2. in parallel, run our new Next.js UI server
  3. use nginx to route the incoming traffic appropriately.

Consider this simplified example:

Let’s say we have a legacy UI server with pages /a, /b, and /c running on port 5000. We then spin up our new UI server on another port, let’s say 6000. As our first step in migration, we move page /a to our new UI and delete it from our legacy UI. We can then run nginx with an override for /a to go to our new UI server, and everything else to go to our legacy server.

An example nginx configuration for this scenario is as follows:

A block diagram view of this architecture looks like this:

On the fly, we are migrating pages to a new framework and using a whitelist to direct web traffic to the appropriate server. As we migrate more pages, we can update the whitelisted pages in the nginx configuration. The end result is that we can:

  1. Develop new, exciting products in our new UI framework
  2. Continue the development and maintenance of the existing UI functionality
  3. Lazily migrate/rewrite existing pages as bandwidth permits

During the migration, we are of course at risk of breaking functionality for our customers. We avoid this risk by having comprehensive integration tests, which will be the subject of a future post.

Conclusion

As we learned more about our customers and what they wanted, we made a decision to migrate to a new UI framework. And we already knew a few mistakes that might lead to outcomes we want to avoid.

Specifically, we have seen teams underestimate the cost of a complete frontend migration, and we have also seen the deprioritization of a rewrite in favor of mission-critical features.

This solution didn’t take long to implement, and has allowed us to balance our needs by allowing us to ship new features customer-facing features in Next.JS, maintain our existing functionality in our legacy UI, and migrate existing pages lazily as bandwidth allows. And so far, it’s been serving us well.

--

--