Over the past month I've been asked 3 times whether Node.js or Ruby is a better fit for new web apps. Time for an article.
Since both Express and Ruby on Rails are web application frameworks, they make for a moderately fair comparison. Opinions and programmer preference can play a large role in selecting the right language, but I generally prefer that the tool matches the job first and foremost.
Development time and cost
Time to develop
Rails is known as the go-to for getting a web app to MVP in the shortest amount of time. Having worked with quite a few other systems, nothing quite compares to the speed of creating migrations and scaffolding with Rails.
Express does come with a similar scaffolding (generators) that cut down on time to generate views and models, but there isn't official migration support at the time of writing this article. There are a couple of packages that attempt to solve this issue, but don't do so very well. What does this mean? More complex databases and relationships are going to be more difficult to setup and maintain in Express.
Rails is an opinionated web framework and expects you to do things a certain way. This can easily be a disadvantage if your web app breaks the usual CRUD architecture (though certainly not a deal-breaker).
With that said, I find myself leaning heavily towards opinionated frameworks when they're appropriate. While unopinionated frameworks provide more control, there's no set of standards or rules for developers to code by. This is fine for one individual, but should you ever work in a team or need to add/switch developers, you're in for a rude awakening.
In a nutshell, an unopinionated framework is more fun to start with, but to pick up on and maintain can be a mess, whereas with Rails a new programmer can almost instantaneously pick up where the old left off.
Languages and frameworks are better than each other at different tasks. While I agree consolidation into a single language is great, this point sounds a little lazy to me.
Rails has gotten a lot of flack for being a slow web framework, but most of these arguments are completely unfounded. This misconception may stem from older versions of Ruby, poor understanding in how to scale Rails, or skewed benchmarks. We've seen apps in plain PHP that when ported to Rails actually had better performance. The truth is that the framework itself probably had little to no influence over these times and there are other bottlenecks in the code.
In a head-to-head battle, Node.js+Express is going to beat Rails in performance almost every time, but there are a few caveats.
Easily the best advantage of using Node.js is the asynchronous data flow and performance. What this does is allow the browser and client to persistently send data back and forth without any of these calls waiting on the other to finish. The connection also continues to remain open after the page has loaded so you don't need to refresh or click any buttons to receive new data.
This makes apps with single page applications or real time interfacing ideal candidates for Express and Node.js. If you had to refresh your browser every time you wanted to check if your chat messenger had a new message or if someone else in your task manager added a new task, your site would be a pain to use.
Ruby on Rails 5 does have ActionCable as a realistic alternative (and easier to implement than the Socket.io solutions) to adding real time capabilities in your app. But if the main focus of your app requires this, Node.js still might be the better option.
CPU intensive tasks
Unfortunately, anything that requires extensive use of your server's CPU, will cause Node.js to choke. Examples of such tasks are dealing with large data sets, searching and sorting through data, or other algorithms. Running these types of tasks in Node.js will clog up your whole web app and bring things to a screeching halt.
Since Node.js uses a single thread, any of these processes that take a long time can prevent the rest of the app from running. The easiest solution here is to create a child process to handle these events, leaving Node's thread free to continue running your app. This does increase the complexity of the app, however.
One of my favorite solutions to this kind of issue is a background job system like Node Resque. While this is a great improvement to the community, it's worth noting that this was ported from the Ruby library, Resque, and that if you're looking at this type of solution it may be worth double checking to make sure Node.js is still the right solution for your web application.
With either of these solutions, it's still far more likely in the earlier stages of development that you're bottleneck is going to be your server, database queries, or front-end framework and assets.
That's not to say you shouldn't anticipate future needs, but is 10 development hours better put towards I/O optimization or towards image reduction? All the asynchronous calls in the world won't make up for a 20MB background image hiding on your page.
Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.Yet we should not pass up our opportunities in that critical 3%.
– Donald Knuth
Stability & future proofing
Stability – the most boring yet oh so important word to any web app. Every web app has tons of moving parts and getting these to harmonize with other parts as well as your own code makes your app run. But as is the life cycle of any package, change happens. And change happens a lot for newer packages.
Packages and gems
Node.js was only created in 2009 and Express in 2010. Ruby on Rails was created in 2004. If that doesn't quite hit home, Ruby on Rails is about twice the age of Express (which translates to roughly a bajillion years in internet-land).
When there is a need for a specific package type, there are usually surges of new packages available. For example, the admin panel packages for Laravel for practically nonexistent and suddenly there were 30 of them. After a while it dwindled down to the top 5-10, followed by the top 2. This is the natural process of selection that needs to happen for a community to start backing certain packages or gems. With open source contributors spread across 30 gems, resources are spread to thin to make great progress on a project.
And if we compare this to the Google trends search, we realize that a packages to gems comparison is even more arbitrary. It tells us that there is a rise in popularity, but not how many active developers are part of that community. To point out the absurdity of this argument, if we look at the Ruby gems available (8,575 at the time of writing) and the total number of WordPress plugins available (50,535), does that mean that WordPress is a more viable candidate for a web application? Absolutely not.
Which should you use?
Gun to my head right now, which do I choose? For our own projects and areas I can afford to be adventurous, probably Node.js with Express. For clients, most likely Rails. I know that if a package (or entire framework) goes belly-up in 6 months, I can put it in the overtime to shift our app into something else.
We do our best to avoid putting our clients in this position. Part of a good programmer's job is risk analysis. Of course you can't be held liable for a third-party framework losing momentum, but it's the last conversation you want to have with a client.
Hey you know that thing we just built for you last year? Ya… it needs to be rebuilt from scratch. Don't worry, about 15-20% of its code base is reusable.
If your application has items that absolutely needs Node.js, then this is an easy decision and you should use Node.js + Express. If it has needs that Ruby on Rails is more geared towards or all else is equal, favor the stable choice – Ruby on Rails. To put this more logically in a way that's more to the point:
express_advantages = [ 'Single Page Application', 'Realtime Applications', 'Frequent Database Interaction', ] rails_advantages = [ 'Coding Conventions', 'Development Speed', 'Data Sorting and Algorithms' ] if project.requires_any(express_advantages) return express elsif project.requires_any(rails_advantages) return ruby_on_rails else # Favor stability if all else equal return ruby_on_rails end
The truth is that with either of these frameworks, there's potential for things to go wrong. Do not pick a framework based on hype and because all the cool kids are doing it. Determine what the application should do and then pick the framework that does that best in a reasonable amount of time.