A Modern Web Application With Rails

It is time to wrap up some learnings from the last years and give suggestions for the choices to take tomorrow.

Alessandro Rodi
Ruby Inside
10 min readJan 16, 2019

--

Things changed a lot since Basecamp introduced Rails in 2005, but without even going that far away, things changed a lot even in the last five years.

In this article, I will not tell you how great is Ruby On Rails, but I’ll give you suggestions on the decisions to take when you will realise your next web application. The things I propose derive from six years of experience developing RoR applications for Renuo. As a Web Agency, we have the advantage to be always on the edge of technologies. Every new project we start (and we have many every year), we can learn from the previous ones and get better, introduce new technologies and try new ways of doing things. This is the reason why the following suggestions are well founded.

Five years ago we were happy Rails developers. We were ruling the world, and we were teaching everyone how easy and fast was to develop a Web Application.

It was possible to bring an idea from concept to production in days, instead of months. Our estimates went down from days to hours, and our clients could finally afford to implement their ideas without spending a hundred thousand dollars.

Then, about five years ago, another revolution started, and having a standard client-server application seemed not to be enough anymore: if you were not joining the SPA (single page application) train, embrace whatever Javascript framework, and split your application in microservices, you were going to be off the market soon.

Very few voices were speaking out of the choir, and (almost) everyone started this adventure in the world of multi-tier applications.
Rails, in the meantime, was not putting a lot of effort trying to keep up with the hype of these technologies. Some concrete things have been implemented during this period, in particular, Turbolinks and API Only mode.

Turbolinks was aiming to make our “old-fashioned” client-server application as fast as a Javascript Frontend Application, while Rails API was telling everyone “Hey! We are here, and we can perfectly serve data to your frontend applications if you like.”.

If you didn’t see them, the following are some excellent articles of out-of-the-choir voice:

What that period left us

Very few applications were meant to be developed as a SPA. In the apex of this craziness, I saw very simple CRUDs being developed as multi-tier applications.

Requirement: “I need two static pages and a page with a list of artists. As an admin, I want to manage these artists by creating, editing and deleting them. They have a name, description and a photo”.

I saw the following:

rails new my_new_important_client
rails generate scaffold artists name:string description:text
git push heroku master

turned into months of work! No kidding…the screenshot is real:

Yes, Redmine is simple and efficient.

Of course, five years later the project does not even build and we lost a lot of money (poor choices should never be on the customer…).

A new era: I am done with this

I decided to not struggle anymore with multi-tiers, impossible Docker configurations to run system tests, redefine APIs every time I want to change a comma, move all my components logic to the browser, waiting minutes for my project to compile, define my routes twice and, in the end, hear complaints about SEO performance, wrong redirects and conflicting versions between frontend and backend.

Starting from tomorrow, in most of the cases, start your project with a pure, latest version, Rails application. Throw Sprockets away and configure Webpacker. That’s it. Rails -sprockets +webpacker.

  • Don’t plan a multi-tier application.
  • Don’t plan APIs from the beginning unless you need them for an external app.
  • Most important: don’t split your application in separate apps.

Keep everything in a Monolith and embrace it.

  • Keep your application clean by making it modular
  • Use service objects to wrap your logic into simple Ruby objects
  • Choose the best frontend framework for your needs.

In the next sections, I will cover some of the decisions you have to take when starting the development of a new application and tell you what I would choose.

Shall I use a frontend framework?

It doesn’t matter. Wrong question. Setup Webpacker.
There is absolutely nothing that Sprockets does, that Webpacker cannot do, but there are tons of features that Webpacker has and Sprockets is missing. And let me immediately answer your question: “I just need some lines of Javascript, why should I setup Webpacker?”. Because is the right tool for the right job. It does not add complexity. It does not add any overhead. It will be basically invisible for you.

I wrote already some articles on how to migrate to Webpacker from Sprockets, or how to use some of the cool features it offers, and more will come.

Stop using JS libraries wrapped in always out-of-date gems: is wrong.

Webpacker allows you to go with plain and simple Javascript (default) or configure the framework that best fits your needs (React, Vue or Angular). Zero configuration. It just works.

When you will go live, as it was for Sprockets, everything will be compiled and packed properly and will be deployed in your public folder.

And now, you have access to the full library of NPM packages.

We don’t want two applications

Let’s state this clearly: having two applications costs more. And is not double: is much more. Please look at this very sophisticated graph where I put in relation the number of apps you have to maintain with the costs to develop a new feature.

These are the numbers I usually keep in mind: in a monolith, to realise a feature, you spend 1'000$. With two layers, you will need 4'000$. If you have a Backend, a Vue frontend and an Android app, you will need 9'000$.

To realise a CRUD with Rails will take you about one hour. If you have a Rails Backend and a separate Angular app, once you define and implement the APIs, document and test them, implement the pages, routes and logics in Angular and deploy everything, you spent already more than four hours. It is a fact and there is nothing wrong with it. You are simply using the wrong tool for the job.

Even if your client is a millionaire, this does not allow you to charge him more for something extremely simple and that can be obtained in much less time.

By not splitting your application, you will be able to code everything in one place and make changes to both Backend and Frontend in one place, open a single Pull Request, and keep your code consistent at any time.

Frontend development

The development of your frontend has never been so easy. Thanks to Hot module reloading, your frontend development is going to be a breath. That’s one of the advantages of using Webpacker I was talking before. Take into consideration, right from the beginning, to realise your application responsive. And not only responsive but with mobile first in mind. Take any CSS framework you prefer and that will help you to quickly build your frontend components. We often use Bootstrap, but when you use Angular or React you may opt for other solutions. Nowadays having an app realised with mobile-first in mind, has never been so important: the reason is that in order to avoid developing a second application for Android and IOS, you should definitely realise a PWA. By doing so, your mobile version of the app will be installable in most devices (without going through the app store!) and will provide a pretty good experience to your users.

If you want to release your application in the Play Store or App Store, you may take a look at Turbolinks adapters (android is deprecated at the time of writing).

None of these solutions will give your app the look and feel of a native application. If your client wants a Native App, none of the ones proposed above is a viable solution. But your client needs also to be aware of the costs of a native application. If he wants both a Web and a Native App, it will cost, and this must be clear from the beginning.

Talking about framework choice, at the moment we tend to use one of the followings, depending on the needs:

  • StimulusJS: perfect for applications with just a little bit of Javascript. No complex behaviours or components needed.
  • ReactJS: when some more complex components are needed, and we want some more fancy user interactions, we use React. React is our choice because we can also share the code, in the future, with a Native App, that we usually code using ReactNative.
  • Angular: this is the framework we adopt when the client needs a SPA.

Native applications

We never use Rails API mode. The reason is simply that at some point (usually very early), the client will need some administrator interface. Even without the API Only mode you can still extend ActionController::API instead of ActionController::Base and you will be able to realise a simple web page to advertise the product online.

For what concerns the APIs we usually proceed directly with JsonAPI Standard. Using a standard will save you an incredible amount of time. APIs are one of those topics where your team will be able to spend hours and hours in infinite discussions on how they should look and behave. Well, good news: someone already discussed that for you. Go with the standard and stick to it. A wonderful gem, developed by Netflix, allows you to quickly build all your endpoints and expose JsonAPI compliant APIs. Another (not so wonderful) gem allows you to read the same format. On the Frontend, you have a vast choice of libraries to work with the same standard.

System Tests

One of the biggest advantages of this approach is the possibility to use Rails built-it System Tests to perform high-level end to end tests on your application. Since you have the two layers packed together, you can use Capybara to navigate your Angular frontend and use Ruby to write your tests, without the need of any other Test Framework. Writing e2e tests for an application composed of a backend and a frontend, communicating through APIs was really frustrating. Our approach was to use orchestrated Docker instances connected to each other, and a third Docker container where the tests were running. This container was taking care of many pain-points: resetting the state between each test, cleaning the database of the Backend application, seeding the database for each test, waiting and synchronising the different components and, finally, running the tests. This solution allowed us to have proper, isolated, e2e tests. Of course, we had to pay a big price in terms of time to realise it and make it running on a CI.

Not anymore. Today I wrap my layers in one, single, monolith and enjoy the simplicity of Rails System tests. They allow me to have isolated, fully independent, tests. Entirely wrapped in a single transaction to speed up the cleaning and seeding process of each test. Capybara offers me a clean syntax to interact with my Frontend and it’s perfectly designed to work also with Frontend frameworks like React or Angular. No need to manually synchronise or wait for components to be rendered in the Browser. Capybara will take care of it for me. Finally, the possibility to connect to different engines gives me full flexibility to test entirely my app on many different scenarios/browsers/ screen resolutions. Parallel tests are also coming to Rails 6, native in the framework. I couldn’t ask for more.

Zero configuration. Nothing to “re-invent”. It just works.

The power of Javascript

I’d like to focus again on an important point: I am not saying “don’t use Javascript frameworks”. What I am saying is to wait until you need one and pack it together with Rails using Webpacker. A JS framework will probably allow you to have a faster user interaction and decrease the loading time for some pages. Rails is known for not being the fastest framework, but pre-optimization is the source of all evil. There are many tools built-in in Rails that you should use to make your life easier, like Turbolinks. Turbolinks comes as an NPM package as well, so you can perfectly use it in combination with the other libraries you will decide to integrate. It will make your navigation between pages faster and will integrate perfectly with React or Stimulus when you will decide to use them. Don’t rush for performance on day zero. Wait until you have a working product and improve it step-by-step. This is also the reason why you should start by using just Javascript and ignore Coffescript, Typescript, Dart, or Flow. You will get there when (and if) you need them. Use ECMAScript6

Take Home Message

There are more tools today than ever to develop a Web Application. And today, more than ever, is easy to get off the rails during the development of a project. As a Web Agency, we need to keep trying new technologies and keep high our level of criticism over them. We use side or smaller projects to try out new stuff, and use well established and stable technologies for our clients. We learn from our mistakes and believe that today, Ruby On Rails is the best way to go for a new Web Applications. It allows our clients to jump straight into the market and test their ideas. It allows us to develop in an Agile way and approach improvements step by step. Webpacker was the missing piece to use all the power of newest Javascript frameworks without the struggle and costs of managing two different applications. You can do the same and convince your clients that less is better, that they do not need to be on the edge of technologies at all costs because they have to focus on selling the product. Once your product works, you will have all the time and money to refine it and improve it further with the certainty of a stable framework behind, that runs applications used by millions of users every day as GitHub or AirBnB.

--

--

Alessandro Rodi
Ruby Inside

Open Source Software Engineer at Renuo AG. Located in Zürich. I do stuff. Sometimes.