Rails to React Migration: Backwards Compatibility. Part 1

Ethan Ozelius
The Startup
Published in
6 min readAug 13, 2020
View from a recent hike up Mt. Ida in Rocky mt. national Park.

Paying customers, backwards compatibility and refactoring

Recently, I agreed to do some work for a fast growing startup. This company and their web application has been active over 2 years, boasting a handful of developers and a sizable amount of early round funding.

We’re not talking enterprise level but the codebase is well beyond a hobbyist project. Zipped the codebase is over 50MB, unzipped over 300MB of tightly wound business logic, duplication and tech debt. But let’s not overlook the fact that this application is in production, and big name clients rely on and (most importantly) pay for its features.

Paying customers don’t care about an application’s tech debt, testing coverage or using the latest frameworks. They want good software today, not perfect software tomorrow.

Objective: Ripping out the Frontend! I mean, “migrating” the Frontend.

It was immediately clear to me, and other engineers on the team that the greatest need was to separate the Front and Back end codebases. So that’s what I set out to accomplish, with a couple goals to keep in mind.

  1. Backwards compatibility: The existing website had to continue serving customer’s needs. No closing up shop for 3 months to rebuild everything.
  2. Piece by Piece: A refactor of this magnitude could only be accomplished little by little.
  3. Use existing Backend: create new JSON API endpoints to run alongside traditional controllers.
  4. Abstract Components: Refactor Frontend HTML/ERB templates into React components.
  5. End goal: full transition to distinct React Frontend. Move templates and features slowly into a new React app that will eventually entirely replace the previous MVC Frontend.
  6. Have fun.

In my 9 – 5 grind, I’m a Frontend developer, but I have had a few years of experience working with Ruby on Rails backends. I was not prepared for the black hole of code this startup had amassed.

I won’t even mention the backend language or framework, because frankly to this day I still don’t understand how it worked. And you know what? I’m at peace with that. The principles I learned while taking this project on could be applied to any MVC web application migrating to an independent Frontend. For the sake of this article and my sanity, I will code the Backend examples in Ruby, and Frontend examples in React.

Side note: There are dozens of ways to refactor, your situation and code will differ from mine. This series is not a step by step guide on how to migrate any Rails MVC to a React Frontend. React on Rails and React Rails are fantastic gems available to Rails projects to accomplish what this article describes.

Let’s meet our fancy (clunky) MVC app

Code for this project is available on my github.

Our example app for this article is: Car Finder!! It’s not cutting edge. And it sure isn’t powered by AI, or Blockchain. But for our purposes, this should work just fine.

The main thing going on in this app is a some sweet 90’s style CSS, hacked together by someone who got fired long ago. First we see a Car Hero, welcoming us to the MVC Car Finder. On the left side is our Featured Cars List. Finally we have our All Cars List list of all the cars. At the moment this app is completely MVC. All of this has been generated with ERB/HTML templates, no JS.

So let’s dive into the code.

Backend (M): Ruby Models

Our car.rb model has several database backed attributes, name, make, style, year, price_min, price_max and color. We included a helper method: featured_cars() to return a list of featured cars.

version 1 MVC model: car.rb

Backend (C): Ruby controllers

Our cars_controller.rb contains an index action, where an instances variables are set that will later be used in the template to render our Featured Cars List and All Cars List.

version 1 MVC controller: cars_controller.rb

Frontend (V): ERB/HTML Templates

Our Frontend is generated by Rails ERB/HTML templates. Notice the Rails partial CarsHero being used in an attempt to stay dry and organize our app into components. Then our code iterates over the @all_cars and @featured_cars lists to render blocks of HTML. And people say that HTML isn’t real programming!

Injecting React in MVC, as painful as real injection

Before we continue, it’s necessary to go on a small tangent to explain why integrating React into an existing MVC app is such a pain. In a perfect world we’d spark up a new React app with a nifty new framework like Gatsby.js, or Next.js, and our MVC app could just import a couple JS files from localhost and Bam! Everything works. Reality is much different. Building a React bundle requires a lot of work adding dependencies, transpiling JS for browsers, often even compiling images and stylesheets into JS.

This process of selecting a React framework and reverse engineering its build files to be used in your MVC app will require a lot of up front work. But it will be worth in the end to have a stand alone React app.

For this tutorial, we’re going with a more homemade, less scalable solution. We will be using the built in Rails webpacker gem. This solution involves very obvious tradeoff: we can get started right away creating components, but our one-off components can’t benefit from a full-feature React framework like Gatsby or Next.

Rails 6 and Modern JS

Rails has made significant improvements integrating with modern Frontend frameworks, out of the box, rails 6 includes the webpacker gem to easily incorporate modern js. As previously mentioned, react_on_rails and react-rails are great options that I would infinitely recommend over the approach covered in this article. But remember, in real life I wasn’t working with Rails.

With Rails, getting up and running with React is amazing easy, leveraging the built-in webpacker gem.

$ bundle exec rails webpacker:install:react

This command will install React into our MVC, configure webpack to compile assets for React, and create a hello_world.jsx React Component. Let’s keep our new React components separate from any existing JS code in our MVC app, by creating new React components directory.

Our First Component: CarHero.js

With React installed in our MVC app, we’re ready to create our React components and inject them into the existing HTML/ERB templates. Our CarHero Rails partial is a perfect candidate to be refactored into a new React Component.

Create the CarHero.js React component, we can even create SASS styles and it will be compiled flawlessly by Rails. I ❤️ u rails.

Injected CarHero into our Template

With our CarHero.js component built, we need to prepare our HTML/ERB template to receive the React component. Let’s create a container inside of the HTML/ERB template where we want our component to be injected. We’ll add 2 important properties

  1. class="react-component" so that we can search for it later with document.querySelectorAll('.react-component')
  2. data-component="CarHero" so that React knows the name of the component to render.

Last step before that beautiful demo time, we have to write a simple JS script render the React components. Create a react_components_manifest.js pack, although you can name the script whatever you’d like.

$ touch app/javascript/packs/react_components_manifest.js

This script imports our dependencies and components, then adds a document.ready event to find all HTML elements with the react-component class. Then iterates on each DOM element, extracting the React Component that will be rendered from the data-component attribute. Finally we render the component into the DOM element with ReactDOM.render(React.createElement(<Component>, <domNode>))

Our First Component in Action

Conclusion

And with that, we’ve enabled our MVC app to use injected React components. We’ve maintained backwards compatibility for our customers, and trail-blazed a path forward for our developers to build a service-based architecture. We will continue our migration by extracting the AllCars and FeaturedCars components from MVC into React, and even and an impromptu new search feature. Because no refactor would be complete without last minute product requirements.

Thanks for reading, I hope you’ve learned something or at least laughed at my misfortunes.

Any feedback would be greatly appreciated, or passive aggressively ignored.

ethanoz.com

--

--

Ethan Ozelius
The Startup

Just a drifting computer hacker trying to get some sunshine.