Let Rails Die Already

Two months ago was a great time in my life. It was nearing the beginning of Fall, the Summer air was starting to cool, and more importantly, before I had my first experience with Rails. Yes, that’s right, I said Rails. How could something like a framework leave such an impression that I’ll never think of Ruby in the same way again? Let me explain.

Rails is a…

web-application framework that includes everything needed to create database-backed web applications according to the Model-View-Controller (MVC) pattern.

Sounds pretty harmless right? You would think so.

Before this encounter my experience with backend development was primarily with PHP (when I was young) and eventually evolved into Go and heavily into Node. I dabbled with a small amount of Python, mainly Django and some Flask.

Ultimately though, I felt as if I found my niche with Node. For the past three years, I would feel confident saying, 3/4 of my development time outside of my day job has been working with Node.js. I’m certainly no stranger to the backend of a web site / application.

Back to Rails.

Immediately after learning the API server I would be working with was written in Rails, I installed the gem, and had at it.

Getting started with Rails is pretty straightforward.

`rails appname`

The friendly command line interface asks a few questions and **BAM** you have a fully functioning website. Awesome, let’s check out the API routes.

`Rails.application.routes.draw
do root to: ‘visitors#index’
devise_for :users resources :users
end`

The above code allows users to sign up, login, and various other standard REST API endpoints. Simple might seem better to some, but in this case it’s not. Every bit of your backend is now under the magical control of the Rails.

If it works, who cares right?

Wrong. So wrong. The importance of understanding where your data is coming and going from, cannot be stressed enough. Allowing a framework to control every detail of your application, like Rails does, makes customization, and tailoring to your specific needs one hell of a pain.

Let’s look at a snippet from a Node API I wrote.

export default (app) => { app.use(‘/api/uploads’, require(‘./api/upload’)); app.use(‘/api/articles’, require(‘./api/article’)); app.use(‘/api/users’, require(‘./api/user’)); app.use(‘/api/roles’, require(‘./api/roles’)); app.use(‘/auth’, require(‘./auth’));
};

The above code is from the routes.js file, which essentially is the same thing as the rails code. The first glaring difference is even someone unfamiliar with Node can look at the code and have a slight idea as to what it does.

From the code above, its broken down even further when you get into the specific portions of the API. What I mean by that is this:

// api/user/index.js const router = express.Router();
router.get(‘/’, auth.hasPermission(‘manageUsers’), controller.index);
router.delete(‘/:id’, auth.hasRole(‘admin’), controller.destroy);
router.get(‘/me’, auth.isAuthenticated(), controller.me);
router.put(‘/:id/password’, auth.isAuthenticated(), controller.changePassword);
router.get(‘/:id’, auth.isAuthenticated(), controller.show);
router.post(‘/’, controller.create);
router.put(‘/’, auth.hasPermission(‘manageUsers’), controller.update);
export default router;

There’s no surprises there and in my opinion it is how an API should be. The routes are written out plain as day, the applicable functions within each controller are defined, and you find yourself knowing where everything happens. Code doesn’t magically appear out of thin air like the 4 line miracle rails app that potentially even cooks your dinner while it registers your users.

When code functions like magic, it comes from somewhere, and there’s certainly overhead. Each additional gem added to the application increases memory consumption by a large number. Gems, are quick to be abandoned and for the most part seem to struggle with backwards compatibility.

As things become more intertwined and further in the past, sometimes what seemed like a brilliant piece of magic to create super-readable code — now buried under lots more code — looks less brilliant.

The problem with Rails is it’s too fucking opinionated for its own good.

Rails and the Ruby language are Dying

From Google Trends

It’s no secret, and I’m definitely not surprised. Checkout the trends from Github below:

From GitHut.info

Ruby is Grandma slow and adding Rails makes for laughable results.

Of course the top 3 languages for speed are compiled languages, but JavaScript isn’t too much slower than Go, or even Java. Then there’s PHP running on HHVM (HipHop Virtual Machine from Facebook), and a few days later we see Ruby.

The below figure is a rough estimate on how you figure out the throughput for your rails application.

The quick rule of thumb is about 1 app instance per CPU thread. Unless you’re spending a large amount of money to host your Rails application, expect to handle a small amount of requests per minute.

Large Rails Sites = Larger Budget

I’m sure someone will bring up the argument of larger sites using Rails as their backend. It’s true. Sites such as Shopify.com and Envato.com use Rails to power their websites. What people leave out is the amount of servers behind the scenes used to power these websites. If your pockets are deep enough, it’s possible to throw money at the problem (efficiency) until your desired throughput is reached.

Originally published on my blog at https://strues.io/let-rails-die-already/