Krzysztof Witczak

Splitting the Monolith

October 04, 2020

Couple years ago, microservices were not that popular, but people already started to deploy their web apps as separated frontend and backend - basically, having two applications communicating with each other.

With small team of three developers, once it was requested from us to add React to one-year old, existing Rails app, we’ve made decision that splitting our web app would be too costly for us - we had short deadlines and this move would put our project at risk. Instead, we wanted to bring benefits from fast development with Ruby on Rails and high potential of modern UI which React offered. We’ve found React on Rails gem. We’ve been really surpirsed by it! Couple benefits:

  • We’ve just added a gem, followed simple instructions, and in just a couple of minutes React was working on example .erb page we just did. We could mix old Rails pages with new React pages without any additional work.
  • Webpack was integrated out of the box with configuration set for typical Rails app, hot reload and anything we would need. That ment, we could stop using always outdated gem wrappers for frontend libraries.
  • It was possible for us to inject props straight from Rails controller. We could start working on the product right away.
  • All integration tests were still working like a charm.
  • All routing, translations, web sockets - everything was working.

We felt really productive and fast, customer was happy and deadlines were met. The only issue - we knew that one day, when app will gain traction, we may need to make a frontend/backend split, for couple reasons:

  • We wanted to scale backend independently from frontend.
  • We wanted in the next couple years to have more backend API customers (like mobile app).
  • If app would grow enough, we could have dedicated frontend team. It felt unnecessary for them to setup backend locally.

How we prepared for that day

We knew that props injection was a quick way of making everything work, but we started to build more API on backend side and use async calls in every spot - even when user opened the page for the first time.

It sounded a little counter intuitve in RoR developer’s head - I return index.erb page and I can easily and quickly return all entities as I’m serving the index.erb page and inject them right away into the React class instance. Instead, after couple of intense initial phases, we started to pull everything with specialized APIs. Till the day 0 (when we made decision to split) I believe we had around 90% of our backend wrapped in nice API, whle the remaining 10% were just initial .erb pages.

Separation plan

Our tactic was pretty simple:

  • In phase 1, we wanted to move remaining 10% of routes to API and have a single index.erb as our React root. We wanted to move to React Router (it’s a shame we didn’t use it before…) and do everything what we can to make next phase easier, but also make product releasable.
  • In phase 2, we planned to create second docker image for frontend and move all frontend assets to second application. That ment changes in our infrastructure for every environment, new deployment flow, a lot of operations work.
  • In phase 3, our goal was to clean up any weird problems that could happen after phase 1 and 2, optimize frontend code, clean it up.

Separation challenges

Some of the decisions were not easy for us, and we needed to talk them through. Couple of these:

  • Right now translations were shared between backend and frontend (using cool gem called i18n-js). Should we split them now between two services?
  • Rails serves great websockets feature, called ActionCable. Will it work with separated frontend?
  • Right now we’ve been injecting backend routes into frontend using js-routes gem. Do we need to manually do it now for frontend service?
  • Should we store services in single repo, or two repos?
  • Should we change our auth into something else than Rails sessions, like JWT token?

Summary

After all these years I still think we did a good decision by picking React on Rails gem, due to the small team size and short deadlines. I really think it made a difference. It’s a pity we didn’t switch to React Router earlier to benefit more from single page app experience though.