Migrating from Backbone.js to React.js without losing it

Oleksandr Heiets
Preply Engineering Blog
7 min readSep 19, 2018

In this article I will share our experience as a team of migrating from old code and libraries to something new and more effective.

Enough said.

My name is Oleksandr Heiets and I’m a FE developer at Preply.com.

Contents:

  • Our story ❤
  • Developing a business mindset. Literally.
  • Migrating to React.js
  • How does it technically work
  • Life’s a b**** (Problems)
  • Key takeaways

Our story ❤

Back in the early 2013, when we’ve started a project that is now known as Preply.com, Backbone.js was one of the most popular frameworks.

Low entrance barrier and simplicity were the key factors for initial decision to go with Backbone.js. The main benefit it’s brought to Preply, however, was the MVC pattern structure as well as the possibility to avoid the so-called “spaghetti code” often produced by teams that only work with vanilla JS and JQuery.

As you can see from Google Trends analysis below, Backbone.js was even more popular then Angular and React (clearly, ’cause they weren’t yet invented in 2013).

But, as time went by, less and less websites were supporting it and obviously the job market for Backbone developers has completely shrunk. 😐

There’s been only one engineer working on our initial Backbone.js code that has quickly turned into legacy.

When we started analyzing whether to stick with Backbone or choose a different framework, we have found a few issues we were not happy about from the technical point of view:

  1. DOM updates are hard: A common pattern in Backbone is to listen to a variety of DOM events (click, change, etc.) and when fired, manually update the DOM using jQuery to hide and show different elements.
  2. Views aren’t reusable: compared to React.js component, Backbone Views are much harder to reuse. They sometimes become “zombies” and often rely on rendering engines (e.g. Underscore or Mustache) which may constraint the functionality.
  3. Performance considerations: in complex views Backbone.js wasn’t able to update parts of the DOM compared to “shadow DOM” concept implemented in React. React, on the other hand, could provide great performance as well as a fully manage rendering lifecycle for the components;

Suddenly, React.js became the new rising star ⭐️ :

Developing a business mindset. Literally.

One of the reasons for choosing a different framework was the fact that Backbone.js code wasn’t reusable, unlike the React.js components. With React.js we could create one universal components with approved design and use it in many places on our website. This would drastically decrease the developing time for our future business tasks.

Backbone.js is really hard to scale and maintain, with no possibility of test coverage that was making the business suffer from continuous code mistakes.

From the hiring side we’ve also noticed that in the dynamic FE world it is rather hard to find good Backbone developers who want to grow as Backbone developers in the age of React/Vue hype. As we planned to grow our technical team, recruiting was very important part of decision making process.

Another factor was a strong ecosystem that includes integration with GraphQL via Apollo, Storybook, ability to experiment with cross-platform using React Native and other awesome things.

New technologies certainly attract talent to join the company since they allow to get creative with new approaches and use the latest programming tools.

Truth be told, our aim is to stop using Backbone.js and jQuery but the question is: how to do it in the smoothest way?

Migrating to React.js

Rewriting all the web pages from the scratch is an extremely time-consuming decision that would negatively affect the entire business since less time would be spend on developing new features and discovering profit frontiers.

With this in mind, we decided to write all the new features on React.js and try to import them into our Backbone code. Sometimes there’s time to rewrite the existing components to React.js but it, frankly speaking, it doesn’t happen very often ‘cause we do focus on business in the first place.

Imported React components into Backbone environment

As soon as we took the decision to begin the process of migrating from Backbone.js, we started improving lots of other things along our way.

Style-wise we have transitioned from less to styled components. We indeed did have some problems with Less code: our Less files contained thousands lines of code, therefore they were really hard to read. Another problem was that our Less files had a global CSS rules scope and sometimes we had to place !important in order to override single CSS rule. Styled components allowed to create global ones that are simple and easy to use and work well with Preply design system that comes with strict rules regarding colors, spaces, fonts etc.

We’ve also switched from Javascript to Typescript because it brings so many new useful features, prevents bugs and makes the code simple to read, understand and develop according to strict type rules.

In addition to the above, we have started using GraphQL in all of our features because apollo client made the communication between front-end and back-end so much easier. We used Redux for storing our server data but now it’s only used for UI global state.

Sounds pretty optimistic, doesn’t it?

How does it technically work

Currently, we’ve got 2 separate folders for javascript code in our codebase, js and js-webpack:

List of folders

“js” folder includes old javascript (ES5) built by gulp, while “js-webpack” folder is built by webpack. At the time of writing this article, we have 73% of javascript code in old js folder and 27% in new js folder.

There is a list of our front-end libraries which include Backbone.js, jQuery, Moment.js, Underscore and, of course, React.

Our libraries and frameworks according to wappalyzer

As you can see, we have a clear seperation of the new and old code by folders. It seem like there is no option to use React.js in the old code folder. But not everything is what it seems though: a React component is actually just a function that can be set as a first argument in ReactDOM.render with dom element as second argument.

This is how it actually works: we export new components as functions to create React components, import them in some specific Backbone code and use ReactDOM.render to render it!

Lets consider “view course button” component. We use this component in each single lead (bunch tutor/student). Components code is separated from a single bundle. Have a look:

Here’s a component code:

Here we call ReactDOM.render with React.createElement (view course component, props) and DOM element as second argument:

We use Backbone model and import specific fields into React environment as properties and get our React components in Backbone code.

Life is a b**** (Problems)

It sounds pretty simple but actually there are so many subtleties here. The first problem can be a memory leak: we use ReactDOM.render very often and if we forget to clean DOM and memory, the user might feel it stronger than us.

Here’s the rule of thumb:

keep your DOM tree tidy and use ReactDOM.unmountComponentAtNode every time before rendering components to avoid memory leak and some unexpected situations

We had a case when React component was imported to the wrong place in Backbone.js and did server request to fetch flags every time the user message is rendered. If the user with huge amount of messages enters the messaging page, there were just too many requests.

Check this out:

‘/chat/flags/’ called every time before react element was imported into backbone code to render

Scary, right? No worries, we’ve fixed this immediately. Lesson learned:

keep your code clean and consider all the possible scenarios.

Keep your codebase tidy even if it is legacy

One more interesting thing is that if you use new technology for your feature, you still need to invent some other ways to get some information from your new BE inside your old code. Here is an example of GraphQl request in ES5. As you can see, it is just a post request with some specific body so you can use GraphQl in ES5 even without React and Apollo client.

Some good voodoo magic

Key takeaways

We are still in the process of migrating, of course, and the finish line is pretty far away, though the best thing about this is the amount of things we have already learned and continue learning day by day.

Here are our key takeaways:

  • You can start writing 0 Backbone.js and 100% React code for new features
  • Pay attention to place, where you render React components, unmount it when you have to and try not to overcomplicate the process
  • Double check everything, write tests for each component to prevent possible issues
  • Process aligned with the business attracts new awesome talent

more to come… :) Stay tuned!

--

--