How to Speed Up Your Ruby on Rails Software Product

We’ve already discussed why choosing the Ruby on Rails framework for your software solution is a good idea. Ruby on Rails (or RoR) is extremely popular, as it speeds up development. This framework provides developers with a great collection of libraries.

But just choosing Ruby on Rails doesn’t mean your project will succeed. To meet users’ expectations, your Ruby on Rails application should be fast as internet users expect a website to load within a few seconds. 

Speed really matters. The performance of a web application is crucial, as it plays an important role in its success. So what optimization techniques will help you improve the performance of your app?

Tips to optimize the performance of a Ruby on Rails app

Let’s see what you can do to increase the performance of a Ruby on Rails app.

Choose your hosting provider wisely

Web hosts offer important services like server management and backups. Choosing the right hosting provider is important for any app – not only one built with Ruby on Rails – as it influences your app’s speed. Different hosting packages provide different features.

Consider either cloud hosting, a virtual private server (VPS), or dedicated hosting. Cloud hosting platforms can be flexible, scalable, reliable, and efficient. With cloud hosting, your hardware is virtual. This is cost-efficient, as you don’t need to pay for a real server.

Dedicated hosting offers a dedicated physical server. This means there are no other users’ accounts on your server and you get full control over it. This solution best fits enterprise-grade projects and high-load web applications where speed is crucial.

A virtual private server, or VPS, offers you a share of a physical server. With a VPS, you pay for the hardware resources you actually use. This type of hosting is more affordable and is a good choice for websites with average traffic.

comparison of cloud, vps, and dedicated hosting

Now let’s check what hosting providers are on the market. If you need thorough control over your Ruby on Rails deployment, consider choosing Amazon Web Services or the Google Cloud Platform. Both are reliable and scalable solutions. But note that when using these hosting providers, you’ll have to hire administrators to configure your network and storage as well as a DevOps specialist to manage the environment and deployment.

On the other hand, solutions like Heroku allow you to launch your Ruby on Rails app faster than two previous platforms. Heroku provides you with a ready-made environment helping you deploy your code quickly whereas the AWS deployment process is more difficult. Application Performance Monitoring (APM) tools offered by Heroku provide details about app performance, allowing you to identify parts of the app that might be causing the slowness. With Heroku, it’s easy to launch applications by downloading them from your Git repository. This option provides less control over your servers than the next one, though.

Digital Ocean is a good choice for those who need a high level of control over their servers. It solves the problem of managing different cloud services by offering bundles called Droplets. Droplets are scalable computing platforms with add-on storage, security, and monitoring capabilities that allow you to easily run production applications.

Read also: How to Choose the Proper Web Hosting Service for Your Website: Types and Tips

Eliminate the N+1 query problem

One of the most frequent causes of slow app performance is the N+1 query problem faced by most Ruby on Rails applications. This refers to instances in which one line of code results in many more queries than expected. As a project grows, this problem only compounds. You may find that your project is running lots of queries to achieve one result rather than running one query with many results. Let’s find out how to fix slow code in Ruby.

To ensure the performance optimization of a Ruby on Rails application, you need to cut the number of independent database queries. Now you’re probably wondering how to do that. The answer is to eager-load associated relations. That means you should collect related data with one query. It’s important that developers understand this performance issue and are aware of newly introduced queries that can be optimized.

Reducing N+1 queries is significant. But it may be difficult to identify N+1 problems when your project scales. You may only detect these database performance issues when moving to a production-sized database. And even when you pay close attention, errors can still sneak into production code. That’s why we advise using the Bullet gem, which identifies and reports N+1 queries. Sometimes it can fail to identify N+1 problems or report queries that don’t have problems, however.

Example:

Let’s say we have a category with many articles and an article class that belongs to this category. We’ve set @articles = Article.all in our controller action. In our view for our index page, if we call all our articles and display the categories they belong to as presented below, we’ll face the N+1 query problem.

<% @articles.each do |article|%>
 <%= article.category%>
<% end %>

Article Load (40.7ms) SELECT "article".* FROM "articles"
Category Load (0.8ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = ? LIMIT 1 [["id", 1]]
Category Load (0.8ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = ? LIMIT 1 [["id", 2]]
Category Load (0.8ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = ? LIMIT 1 [["id", 3]]

To fix this problem, we can use @articles = Articles.all.includes(:category)

With this simple use of includes, we’ve eagerly loaded the associated category records. Now we only have to query the database twice: once for the Article model and once for the Category model. We don’t actually end up loading less data, but we query the database far fewer times.

Read also: Vital Things To Consider When Choosing a Database for Your App

Use pluck

Pluck is a shortcut that helps you choose one or several attributes without needing to load the corresponding records. Let’s compare pluck with map. Map loads objects into memory and gets an attribute. Pluck returns an attribute without loading objects into memory. With pluck, you get better SQL performance and less time and memory use by Ruby on Rails.

Here’s a real example taken from one of our developer’s database: 

StockChange.all.map {|i| [i.accepted_at, i.reverted.at]}
StockChange Load (186.7ms)  SELECT "stock_changes".* FROM "stock_changes"

StockChange.pluck(:accepted_at, :reverted_at)
(69.4ms)  SELECT "stock_changes"."accepted_at", "stock_changes"."reverted_at" FROM "stock_changes"

Pluck returns an Array. However, you can use Extends ActiveRecord by applying the pluck_to_hash method to return an array of hashes instead of an array of arrays. This is helpful when you need to pluck numerous columns for rendering JSON data or to access specific fields in your view page for StockChange.pluck_to_hash(:accepted_at, :reverted_at).

Use size instead of length

Don’t use length to define how many records there are in your database. ActiveRecord doesn’t define a length property. This means that if you call StockChange.all.length, all StockChange records will get loaded and converted into an array. As a result, you’ll end up with the following: 

StockChange.all.length
StockChange Load (169.9ms)  SELECT "inventory_stock_changes".* FROM "stock_changes"

But if you call .size instead of .length, the result will be much better:

StockChange.all.length
(13.9ms) SELECT COUNT(*) FROM "stock_changes" 

Size completes the process 10 times faster and consumes less memory than length

Ruby also provides the count method. This true method determines the number of elements with an SQL COUNT query. If you haven’t loaded any records, you can use count to perform a count query on your database. The size method is adaptive, meaning that if the collection is loaded, it counts its elements with no additional query. If the collection isn’t loaded, it creates an additional query.

Upgrade Ruby 

New Ruby versions tend to bring performance improvements. They also bring specific upgrades and new methods that are often faster for their specific use cases than less specialized methods. 

Caching

This is a big topic, as there are many types of caching that can be applied in different cases. But in general, caching helps you store content generated during the request–response cycle and reuse it when responding to similar requests. Check out a detailed overview of caching with Rails.

Gems and plugins are your friends

How to optimize code of Ruby on Rails apps? A common way to speed up the development of a web application with Ruby on Rails as well as the app itself is using gems and plugins. But gems and plugins can speed up your app only if you don’t use too many of them. We’ve already mentioned that using the Bullet gem solves the N+1 problem that most Ruby on Rails applications face. But there are other useful gems and plugins that can help you optimize RoR code.

rubygems

Some gems and plugins solve common problems in Ruby on Rails applications so developers don’t have to create solutions from scratch. Gems and plugins handle issues connected with APIs, security, Active Record, testing, debugging, deployment, payments, authentication, authorization, and other things.

But don’t forget that lavish use of gems and plugins means there’s more code to process (which takes more time). With too many gems and plugins, Ruby on Rails apps become larger than they have to be, slowing down performance. Check twice to make sure the gems and plugins you’re going to use won’t harm performance.

Use background processing

To boost up the performance of your Ruby on Rails application, it’s important to use background processing. Background processes run periodically and can calculate statistics, process videos, send emails, run reports, and do lots of other things. Optimize your repetitive tasks by handling them automatically. Background processes shouldn’t slow down the application’s response.

There are tools created especially for the Ruby on Rails framework that manage background jobs. We use the Clockwork and Sidekiq gems. Clockwork is a cron replacement. It runs as a lightweight Ruby process that schedules recurring work for a specific time and date. 

Sidekiq is one of the most popular Ruby background job frameworks. It uses Redis as its job management store, leveraging its speed. Sidekiq provides a dashboard that shows all your job queues and their processing states. The Sidekiq framework is open source, but there are also paid versions — Sidekiq Pro and Sidekiq Enterprise — that provide additional features including periodic scheduled jobs and unique jobs.

Use a content delivery network

To speed up applications, one common practice is to use a content delivery network (CDN) no matter which language and framework you use. Ruby on Rails isn’t an exception. CDNs are services that provide edge nodes around the world so that files are hosted closer to a user’s location. With CDNs, website content like JavaScript, CSS, HTML, images, and videos load equally fast in different locations.

how a cdn works

A CDN ensures consistent app performance and makes sure users download data from the geographically closest server. Content delivery networks are paid third-party services. There are expensive CDNs for enterprise-level projects as well as cheaper CDNs for small and medium-sized websites.  

One popular CDN is Amazon CloudFront. It’s built on the global AWS infrastructure that currently includes 54 Availability Zones within 18 geographic regions. CloudFront is a secure CDN that provides network- and application-level protection.

Add indexes to your database

Your app may slow down when it gets more traffic because database tables expand, slowing down lookups. What are our tips to fix this problem? You can add indexes to your database. Actually, a lack of indexes on columns is one reason why some queries take way longer to execute than they should.

When creating a database column, it’s important to take into account if you’ll need to find and retrieve data based on this column. Without indexes, the database engine will check every record in the table until it finds a match. With indexes, the lookup will be a lot faster, as the engine will check only specific records. Before adding an index, it’s important to know whether searches or updates will be performed more often. Indexes speed up searches but slow down updates.  

It’s also important to add indexes to foreign keys, joint tables, and polymorphic relationships for faster lookup. When you need to remove an index and a table at the same time, remember that it’s crucial to remove the index first.

Example:

Let’s say we have a database with 30,000 records. Let’s add an index to first_name in our User model.

First, we have to generate migration:

rails g migration add_index_to_first_name_column_on_user

Then we add the index:

class AddIndexToFirstNameColumnOnUser < ActiveRecord::Migration[6.0]
 def change
   add_index :user, :first_name
 end
end

Before adding the index, we had: 

User.find_by(first_name: "Jon Snow") => 25.3ms

After adding the index, we achieved: 

User.find_by(first_name: "The Hound") => 0.6ms

That’s a very good result.

To build a fast Ruby on Rails application that satisfies users, focus on choosing solutions that fit your project. Select a proper web hosting provider and consider using a CDN to ensure great performance in different geographic regions. Make complex operations asynchronous and move them to background jobs to speed up your app. And don’t forget about useful plugins and gems like Bullet to solve common speed problems. Don’t use too many of them, though. We hope the general practical tips we’ve benchmarked in this post will help you provide Ruby performance tuning.

4.0/ 5.0
Article rating
234
Reviews
Remember those Facebook reactions? Well, we aren't Facebook but we love reactions too. They can give us valuable insights on how to improve what we're doing. Would you tell us how you feel about this article?
Want to create a web application?

We can help

Let’s discuss your concept

We use cookies to personalize our service and to improve your experience on the website and its subdomains. We also use this information for analytics.