Best Practices for Migrating Large-Scale Applications to React JS

Saptarshi Chakraborty

Saptarshi Chakraborty

Sr. Technical Lead

Published Apr 29 2020

Migrating a large, high-traffic application to a new technology stack can be a daunting task. UI frameworks and tools have exploded in numbers giving us both the freedom and paralysis of too many choices. If you are reading this blog, you are probably leaning towards React JS.

React is a robust framework with a diverse, helpful community of developers who are always available to resolve your queries. In this blog, you will read about some of the best practices that our teams have gained from migration projects while working for some of the biggest retailers in the United States. While the focus would largely be on jQuery to React JS migration, most points will serve you even if you are coming from another framework, such as Backbone, Knockout, or Ember JS.

Divide and Conquer

In a large application, it is prudent to follow a component-based architecture, i.e., split an app into smaller apps that can be deployed independently. For a retail website, for example, instead of creating one React app that spans the entire website, you can break it into separate smaller apps, such as search and browse, product, bag, checkout, order history, returns, profile, and more. These can be further divided into reusable functional components (e.g. header, footer, recommendations, product carousel, etc.)

Breaking a large application into smaller components

This migration approach is useful even if you have the budget and resources to migrate the entire application in one go. The advantages are listed below:

  • Staggered Release: Migrating a large application can take months. After splitting the app into smaller apps, you can either migrate one section at a time or even ramp up the traffic gradually. Network rules can divert traffic to the new app for only certain URLs, while your existing app can handle traffic for everything else, just as it is doing now. If you notice something wrong with your new app, you can immediately route traffic to the old app while your team debugs and fixes the problem.
  • Parallel Development: Since the development of different sections can progress in parallel, it is possible to have separate teams to take ownership of migrating different parts of the user journey. The process allows the teams to communicate with each other, identify dependencies and gaps in the design sooner, and fix the issues early on in the development phase.
  • Staggered Development: If the developers on the team are new to React, you can stagger the development to identify non-critical sections of the website to begin the development, and use the learnings to develop more business-critical sections.
  • Stable Codebase: Continuous development and deployment become simple. Instead of regression testing the entire application every time something changes, you can only test the section that was modified. This approach makes your application more stable while reducing the testing effort during the entire code lifetime.
  • Hassle-free Deployment: Deployment is more straightforward because you need to deploy only the section that was changed. The release requires only the development team that has worked on the change, freeing up the other teams to carry on their regular work.
  • Independent Scaling: Not all sections of the website receive the same surges in traffic. For example, on a retail website, traffic to product and checkout pages during holiday seasons can see a sharp spike. However, profile and other static sections may not experience any major changes.

With smaller applications, each of them can be scaled independently that can also save infrastructure costs, which is otherwise wasted in scaling the whole application. It also insulates different parts of the website so that if one section goes down, the others continue to serve requests.

Set Standards with Proof of Concept and Documentation

Appropriate tool helps in structuring and building the app. Create-react-app (React Command Line Interface (CLI) for creating application) does a pretty good job of covering everything from scaffolding the app and code transpilation for ES6 and JSX, to configuring webpack for builds and module loading. Customizations should be identified early in every implementation.

Before the CLI is handed over to the development teams, the UI architects must create a complete 'Proof of Concept' application with unit tests, monitor the performance, deploy to a server, and load the page in a browser. Later, they must inspect the files that are being loaded to see if they need to be split up or combined, caching rules that need to be set, etc.

It is essential to have well-documented coding standards and conduct initial hands-on coding sessions with the teams to set expectations before they start with the development process. Assign points of contact for each team to resolve queries. Start with a small group of code reviewers who work with architects before opening up code reviews to individual teams.

Let Go of the Old Code

It may be tempting to try and reuse most of your existing code to save the resources, but understanding what to let go can help you accurately estimate efforts required to avoid disappointments in the long run. If you are coming from jQuery, you must discard code related to the Document Object Model (DOM).

React uses an entirely new way of declarative data binding that requires the writing of code from scratch. Here are the areas that you can look to salvage your old codebase:

  • Utilities: Simple JavaScript utility code—such as those for making API calls, parsing and formatting responses, validating logic, etc.—may be reused with some modification.
  • Markup: If you have clean semantic HTML markup, it is trivial to split and convert them into React components, like JSX. But, if your HTML has gathered some moss over the years, you should start fresh and do things the right way.
  • Styles: If you have existing SCSS or CSS libraries with site-wide styles, it may be possible to re-use them. This is a probable approach if you are reusing your HTML too.

Be Prepared to Change the APIs

A change from jQuery to React JS isn't just about swapping one UI library with another—it is a paradigm shift. React-like frameworks mark a change in how we structure large applications for faster performance and easier maintainability of code.

Around the year 2005, when JavaScript libraries were becoming popular and the newer web browsers were becoming more efficient at running JavaScript, developers started moving a lot of logic that used to be traditionally handled by the server-side code to the client, i.e., the end user's browsers.

With the advent of single-page apps around 2010, routing, maintaining state, fetching, and manipulating data were all added to the laundry list of things that JavaScript would execute—all on the browser at the expense of the user's computing resources. This led to bulky applications that took a long time to load, stuttered during use, and drained the user's batteries.

Today, we know that a page that takes even a few seconds more to load costs a company money in lost conversions. Discoverability is dependent on the speed — Google ranks faster sites higher on its search results. Hence, the current push is to create UIs that load quickly and remain responsive to user inputs. To achieve this, an API must provide the UI with data in a render-ready format.

Here are a few examples:

  • The dollar amount sent by the API should be $12,349.99 instead of just 12349.99. Insertion of million separator commas, currency symbols should not be performed in UI.
  • Dates should be sent in the format you want them to be displayed, i.e., 3rd May 2020 instead of 05/03/2020.
  • A prevalent requirement is where UI needs to display a particular section only when certain conditions are met. Instead of making UI derive this condition by looking at multiple fields in API response, the API should perform this check at the server and then send UI a single true or false flag, like showHolidayMessaging: true.
  • Remove all fields that are not used on the current page from API response. This reduces the payload and memory required to store and work with this response in the browser.

Breaking down a large application into smaller components

The side effect of tailoring API response to a page's requirement is that the same API can't be reused on multiple pages. There can be various solutions to this problem. A simple one would be to pass a page name to the API, which can be used to customize the response. Another solution is to create a middle API layer (sometimes called the API Experience or xAPI) between your existing API layer and UI layer. UI will call the xAPI and not API layer. The xAPI will call your existing APIs to fetch the data, format the data as per the requirement of a particular page, and send it to UI that is ready to be displayed.

When you migrate to React, keep the API layer changes in mind while estimating the work involved.

Other Technical Aspects

Here is a list of few technical aspects that we have found helpful in our experience. While all may not apply to your use case, you can use the ones that hold relevance for you.

  • Don't touch the DOM: One of the significant reasons jQuery-based websites perform poorly is that they deal with the real DOM directly. React, on the other hand, uses a virtual DOM with diffing algorithms internally to operate on the real DOM. When you build a component that reaches out and does its own DOM manipulation, you should ask yourself if you could build the same feature more idiomatically with React's virtual DOM features.
  • Use React hooks: While there may be debates on the merit of switching to React hooks for existing React code, it makes sense to embrace React hooks if you are writing code from scratch in 2020. You lose nothing by using hooks and it dramatically simplifies the code for maintaining component state, et al.
  • React Hooks vs. Redux: We have seen people compare React Hooks and Redux, but the truth is that there is only minor overlap in their purpose. Whether your application needs to use Redux depends on various factors that are beyond the scope of this blog, but if you decide to use Redux to maintain the state of an application, you can (and should) still use React hooks for the component state.
  • UI Component Libraries: Consider UI component libraries before you decide to write everything from scratch. React component libraries, such as React Material UI and many others, offers commonly used UI elements, styled and ready to be used in your React code.
  • Accessibility: If you are building a website in 2020, make it accessible. Make sure all team members, including developers, testers, and project managers, attend workshops and knowledge-sharing sessions to set the expectations. Accessibility standards are notoriously difficult to implement after developing a UI. You must ensure that every UI story has specified its accessibility requirement. The stories should be signed off only when they meet both functional and accessibility requirements.
  • Set-up Private Artifactory: When you split your codebase into multiple smaller apps and components, consider setting up a private Artifactory. There are various solutions to explore here, but we like the Node Package Manager (NPM) since the developers are already familiar with installing and managing dependencies using NPM. You can set up a private registry that is only accessible within your organization with a custom namespace.

Each component will have its own repository with its dependencies declared in the package.json manifest file. When a developer wants to include a component in another component, he can install and use it in his code, just like the use of third-party NPM modules.

Often when components reside in the same codebase, they start as clean and modular, but over time, as more and more developers work on them, developers start depending on the internal logic of those components. This is bad because when the time comes to change some internal logic of a component, it may break other components that depend on its implementation logic. Setting up separate repositories for your components will force you to keep different components independent.

In our experience, migrating to React from other JavaScript libraries is more about changing the way we think about the front-end rather than learning a new framework. Yes, it can take developers some time to learn about the tools that React provide, but thankfully, it is just plain old JavaScript done right.