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.
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.
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.
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:
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.
Stop using JS libraries wrapped in always out-of-date gems: is wrong.
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.
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.
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:
- 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.
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.
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.
Take Home Message