Ruby on Rails optimization techniques

There’s a reputation that Ruby on Rails apps are slow, however, because of its simplicity, readability and many good techniques and tools available out of the box, it’s easy to make ROR apps behave faster than other language frameworks.

So unless your app’s business model heavily depends on how much each transaction cost (WhatsApp, Twitter, and other social networks) ROR can be the right tool for you.

Let’s begin!

No Optimization

No optimization article would be complete without mentioning premature optimization. So the first rule of optimization is that you shouldn’t optimize unless you have a problem, and unless you know for sure there’s going to be a bottleneck, you should focus on readability and business value instead of solving a problem that doesn’t exist.


The second rule is measure! How do you know you are making improvements unless you have numbers to back you up?

There are different tools available based on what you want to measure.

For optimizing specific methods you can use the inbuilt Benchmark module

require 'benchmark'
puts Benchmark.measure { 10_000.times { your_code } }
#        user     system      total        real
# => 1.500000   0.000000   1.500000 (  1.500000)

For measuring web requests you could use a handy gem called rack-mini-profiler:

Or you could use a very primitive combination of time and curl:

time curl  > /dev/null 2>&1

There are important details related to measuring execution time.

After you change your code make sure you don’t measure the first request to your server because the code is going to need a reload. The same goes for micro-optimizations, your processor caches need to be warmed first.

Once you have measured the execution time of your code you can begin thinking of different techniques to speed up your app.

Database Optimizations

We are going to start with database optimizations: the more data you have the easier it is to shoot yourself in the foot by not using optimal solutions. Thankfully by following a few simple techniques, we can reach 80% of the way to make our DB access fast.

We are going to talk about:

  • Getting rid of N+1 requests
  • Adding Indexes
  • Rewriting ORM queries in plain SQL
  • Denormalizing database
  • Doing inserts in transaction

Getting rid of N+1 requests

Usually, the first thing I look at when trying to optimize a web request is to try to get rid of N+1 SQL queries.

The easiest way to spot them is to open your logs, make a new HTTP request and see if there are many identical SQL queries being logged.

You can also use a handy bullet gem:

An example of N+1 is an application where you have many articles, each article has an author and many comments.

Say you want to display all articles, and each article shows its author and comments.

It takes 1 SQL request to load the article, however when your code iterates over them and tries to access its author or comments, it needs to make an SQL request for each one.

articles = Article.all
articles.each do |a|

Given you have 100 articles, this code is going to make 201 SQL requests.

You could turn this into 1 SQL request by adding just a bit of code.

articles  = Article.eager_load(author: {}, comments: {})

However this makes database perform a huge join, a better alternative is to use "includes":

articles  = Article.includes(author: {}, comments: {})

Includes is going to make 3 SQL queries.

SELECT * FROM articles
SELECT * FROM authors WHERE authors.article_id IN (...)
SELECT * FROM comments WHERE comments.article_id IN (...)

In practice, this is faster than a huge join.

When serializing complex nested data structures you could use something like gem.


If your table has more than 1000 records, it is vital to consider if it needs indexes.

An index can be created for a column to make SELECT queries that filter by this column faster.

Imagine a phone book, if you are searching for your friend’s number by a name, and names are not sorted, it is ok to list through maybe 50 contacts, but if you have more it becomes a nightmare, it is much easier to find your friend if contacts are sorted by names.

This is very close to what database does when you create an index, it creates a data structure where references to records are sorted by the specified column.

You need to analyze what columns are included in your SELECTs and create indexes for them.

It is better to have too many indexes than too few.

Downsides of indexes are extra storage usage and a bit slower UPDATE and INSERT queries on that table since indexes need to be rebuilt each time values in the indexed column change.

Rewriting ORM queries in plain SQL

ORMs are a controversial topic, some love them, some hate. Personally, I like them for the productivity boost, but it comes at a cost, especially in ROR, where every model is very smart and complex.

When you need to load many records at once, it might be a better idea to get the data you need via plain SQL. Since instantiating ActiveRecord objects is costly, it can speed up your code several times.

For most of us writing business logic is more natural and convenient in our programming language, than in SQL, however, when you are loading a lot of data and then crunching it, if it’s slow, rewriting it in SQL can make your code significantly faster.

Denormalizing database

Denormalizing comes at the cost of maintainability but sometimes is inevitable to reach the necessary performance.

The main idea is that instead of calculating information every time on the fly, doing joins, you can store information in a database column.

E.g. when you want to show user rating of some shop, instead of going and loading all the ratings for it and taking an average on every request, you can calculate that average once a day and store in a column on that shop’s table.

The same can be done with counts, instead of doing queries to calculate how many comments each post in a blog has, you can store it in a column on that post.

Doing inserts in transaction

When you are inserting many records at once there’s a trick to make it faster: put all inserts inside a transaction. Besides helping with consistency, it can make inserts several times faster.


Perhaps the most powerful optimization technique, but also it can be hard to get right, it can cause many bugs (right away and later when you change your code).

Caching views

One of the highest levels of caching (ignoring HTTP) is caching HTML in your views.

You can either rely on rails "magic":

<% @articles.each do |article| %>
  <% cache article do %>
    <your-html />
  <% end %>
<% end %>

Or generate cache keys yourself

<% @articles.each do |article| %>
  <% Rails.cache.fetch(
     "articles/#{}-#{article.updated_at}/details", expires_in: 1.hour) do %>
    <your-html />
  <% end %>
<% end %>

On the one hand, you can avoid a whole lot of queries and HTML generation, on the other, you need to be very careful in what data you include in your HTML and make sure that data in the cache key uniquely identifies the content. E.g. if your HTML contains data specific to a user, you need to include this user’s id in the cache, otherwise, one user is going to see another users content, which is not only confusing but can be a gaping hole in your security.

It’s also important to think if your content always needs to be up to date.

E.g. if a new article was added and you are showing a new article list, how soon does it have to appear.

One way to deal with it is expiring cache every time a new article is created, but it can be very hard to track all the places where the article can be updated.

A common technique to deal with having user-specific content on cached pages is caching the whole fragment without user information, and then loading user info via AJAX upon page load.

Caching DB queries

One level below view caching lies caching DB queries. Often they are slow and could use some speeding up.

Rails.cache.fetch("user_articles_#{}", expires_in: 1.hour) do
      Article.where(owner: current_user)

There’s actually a bug in the code above, it’s not going to be cached, because it only caches the relationship and not the actual records, it can be fixed by adding .load or .to_a which is going to actually load the records.

Rails.cache.fetch("user_articles_#{}", expires_in: 1.hour) do
      Article.where(owner: current_user).load

Caching DB queries has the same pitfalls as caching views. You need to be very careful!

Not caching

That sounds like a weird technique, but having too much caching can, in fact, slow your app down. In some cases a lot. Usually, your cache store (e.g. Redis) lives on a remote server, and making requests to it incurs quite a cost. To write to cache you need to calculate key, serialize content, send that content via a network. To read from the cache you need to calculate key, read content from the network and deserialize it.

You should never cache things like:

<% Rails.cache.fetch("long_piece_of_html") do %>
        This is a title
    <!-- more plain html -->
<% end %>

Unless they have some DB queries inside or a lot of data crunching.

It’s much easier to generate a string locally than to make a network request for it.

HTTP optimizations

You can squeeze out more juice out of your app by correctly configuring Nginx and your assets.

Turning on HTTP 2 is fairly easy and provides a good performance boost for download speed, especially if you have many files living on your server since they can be downloaded in parallel.

It is very important to enable Gzip compression for your files, it can be done from Nginx.

Better yet use Brotli compression. It’s a new compression algorithm developed by Google that was specifically optimized for the web. It can be reduced your asset size by 14-20% when compared to Gzip. Sadly configuring it for Nginx is not very easy, but if you are using Cloudflare, you can enable it by simply toggling a checkbox.

Another optimization that you can include to your pages is adding different hints to the web browser.

There are prefetch, preconnect, preload, prerender.

Can be used like:

<link rel="prefetch" href="//">

This technique requires to carefully think about assets and helps browser load relevant content faster.

Caching assets

By assets I mean images, JS and CSS files.

It’s a huge topic in and of itself, but too important to skip.

Usually during deployment Rails generates files with unique names based on file content.

It runs a hash function over file content and adds it to the file name. So the file can become my_js_2086A9193BE7DD4E916989BFFACDB767.js. This way if file ever changes its name changes as well, so you can safely cache it.

You should never serve files via your ROR server, it’s just not meant for that.

A much better alternative is placing your ROR server behind a reverse proxy like Nginx, which would read these files from the filesystem and serve them for you. In this case, you need to make sure that Nginx adds correct header when serving these files:

location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header 'Cache-Control' 'public';

A better yet alternative is placing your server behind a proxy like CloudFlare, which would look at response headers and cache your content on their own servers and deliver it to your clients via a global CDN.

Optimize your images

Images are often the heaviest assets on your site. You battle to save 50kb from JS bundle only to add an image that weights 1mb without giving it a second thought.

You should add images in matching resolutions, if your designer gives you an image in 4k resolution, you shouldn’t use it as it.

Better yet, use "srcset" to specify images of different resolutions for different screen sizes.

Also, you need to remember, that 2 images with the same resolution can have a drastically different size based on how they were encoded. Most of the time you can sacrifice a bit of quality to get an image that is 10 times lighter.

Another easy optimization technique is lazy loading images, to only show them when the user scrolls and they appear on the screen.

Also, Chrome team promised lazy loading functionality to be available in Chrome 75.

It's going to be as easy as adding a "loading" attribute to your img tags:

<img src="example.jpg" loading="lazy" />

Split your JavaScript

This one requires a bit of thinking if you don’t have a SPA with chunk splitting, but can drastically reduce your JS bundle size. For example, you could split everything that requires interactivity from your main JS bundle and exclude it from your home page, which often has only marketing details.

Move logic to background workers

Much of the work that happens on web requests to your web application doesn’t have to complete before we show a user some response.

The classic example is emails.

Imagine that a user is buying a product. The code would look something like:

def buy_product 
  charge_user(user, product)
  send_success_email(user, product)
  return user.money_left

Sending the email could be slow, instead of waiting for it and slowing the user’s experience we can move it to a background worker.

There are other benefits to this technique besides speed:

  • Worker code can be re-tried, especially if it’s a transient error
  • Your app is much more resilient to sudden spikes of requests
  • It is easier to separate your app into different services later

DRY your code

DRY - don’t repeat yourself.

By following this core software development practice, not only can you be more productive and reduce bugs related to changing code, but your code can be faster as well.

By making sure your code doesn’t repeat itself, you can spend time optimizing it in one place and anywhere it’s used is going to benefit from it.

The story continues

There are infinite ways your code can be optimized, I only mentioned the ones I deem most impactful for an average Web app.

Make sure you follow these practices but make sure you don’t go overboard since the end goal of you as a developer is delivering business value, and not chasing numbers.


  1. Actually your exploratory writing capacities has enlivened me to begin my very own Blog Engine blog now. Extremely the blogging is spreading its wings quickly. Your review is a fine case of it SEO in New Jersey

  2. Due to the above statistics, it therefore becomes essential for any SEO service provider in the current market to operate in a manner that matches his/her setup. blog comment

  3. Thankyou for this blog its really interesting and informative, but there is some errors
    Which need to be recorrect by owner. See i also have some good blogs related to
    Technical services, you can check on my website.

    Avast Login
    bullguard support number

  4. The last perspective you have to consider is the notoriety of the administration you are going to procure. At the point when you go to online journals and various discussions, blog comments service

  5. Ineffective SEO implementation, seo If executed properly, blog commenting can be very beneficial for your brand and business. The value that blog commenting brings in a marketing campaign are:

  6. Search engine optimization will likely be information outreach, from actual internet sites, true customers and authentic information and facts composed for virtually any true certain individual find out here now!see this here It may seem just like a new terminology but it has been about for an extended period no less than the process however it is completely one of the most preferred link building techniques

  7. Nice post! This is a very nice blog that I will definitively come back to more times this year! Thanks for informative post. Image editing services

  8. Numerous site streamlining agents seem to be authentic pros and can pitch an appealing system, yet the truth is there's various guilty parties who neglect to convey the prescribed procedures of SEO and site improvement.
    blog comments service in 1$

  9. On-page component extractor is the specialized procedure of removing information from web by utilizing programming program from demonstrated site as it were. SEO PACKAGE

  10. Httpmarketing is a Full-service web agency for entrepreneurs and SME and SMB companies in the Netherlands. We make mobile apps, build professional websites with, of course, responsive web design. We also provide customized solutions for companies that want to be highly findable on Google! Make your site findable on Google!

  11. This comment has been removed by the author.

  12. Get the instant solution for all technical issues which face by you Yeah! Because Now Geek Squad Appointment 24x7 customers care always available for you. Only Dial Geek Squad Service Number (+1) 855-554-9777. And Our Geek Squad Team helps you As soon as possible.

  13. I think this is an informative post and it is very useful and knowledgeable. therefore, I would like to thank you for the efforts you have made in writing this article. gems and jewels

  14. I haven’t any word to appreciate this post.....Really i am impressed from this post....the person who create this post it was a great human..thanks for shared this with us. garnet engagement ring

  15. If you have SEO on your mind and are looking for an SEO professional then it is important to ensure that you get the correct solution, the first time. Like any service enquiry, you should a) communicate clearly what you need and b) ensure that you understand what services are being offered, and what the expected results are going to be for the price. In short you should make sure that both parties have the same expectations.

    מסה מדיה קידום אתרים

  16. I was getting bore since morning but as soon as I got this link & reached at this blog, I turned into fresh and also joyful too.
    UI design company

  17. This was a really great contest and hopefully I can attend the next one. It was alot of fun and I really enjoyed myself.. instagram likes buy uk


  18. Mediator Łódź
    Adwokat Łódź

    Lutownica transformatorowa
    Taxi Zgierz
    <a href=">Mechanik Łódź </a>

  19. Waooow!!! Magnificent blogs, this is what I wanted to search. Thanks buddy
    check out this site

  20. Your blog is extremely brilliant especially the quality content is really appreciable.
    web design company

  21. The Python code runs more than fast enough for most applications. It is used in a wide variety of application domains. Python is an excellent language for learning object orientation. unindent does not match any outer indentation level python

  22. The team provided original ideas and executed an organized product development website design agencies firm collaboration to minimize oversight.

  23. McAfee Activate 25 digit code – Enter your McAfee activation code to access, download, install, activate, renew and transfer your McAfee Subscription and stay protected from viruses and malware. | Download, Install & Activate McAfee McAfee Activate – Since the world is growing every day with new digital technologies, cyber threats, malware, data, and damaging infections have also become more and more advanced with each day. These cyber infections damage a device or files in various ways. They will enter into a device in multiple ways, while a user is trying to install a file or opens a malicious link while browsing. Once it affects a device, the device will get slow, applications & programs fail to run properly, valuable and precious files will vanish or get corrupt, and more importantly, there will also be the risk of data being stolen. The best solution to prevent your device and data from cyber threats, malware, viruses, and other harmful attacks is to get McAfee at

  24. Website design enhancement comprises of two unique things - on-page and off-page optimization. SEO no cure no pay

  25. There are numerous sites giving appropriate data about SEO and internet showcasing, and you can gain from them. seo birmingham uk

  26. Very useful info. Hope to see more posts soon!. безплатни

  27. Try not to transform SEO into your essential movement. Recollect that your essential objective is to zero in on what you specialize in your business. increase website traffic

  28. Superior post, keep up with this exceptional work. It's nice to know that this topic is being also covered on this web site so cheers for taking the time to discuss this! Thanks again and again! 200 dofollow blog comments

  29. Fabulous post, you have denoted out some fantastic points, I likewise think this s a very wonderful website. I will visit again for more quality contents and also, recommend this site to all. Thanks. I will 80 high quality niche relevant blog comments backlink

  30. Your content is nothing short of bright in many forms. I think this is friendly and eye-opening material. I have gotten so many ideas from your blog. Thank you so much. facebook Page ibmseo

  31. Don’t know the difference between your beam type and pre-set torques? Read this blog to find out some handy tips for repairing your bike and save some money! preschool silver spring md

  32. amazing article, it turned into exceptionally obliging! I understandably began in this and i am becoming greater acquainted thinking about it bigger web design company in washington dc

  33. Nice to be visiting your blog again, it has been months for me. Well this article that i've been waited for so long. I need this article to complete my assignment in the college, and it has same topic with your article. Thanks, great share. 먹튀검증


Post a Comment

Popular posts from this blog

Faster Google Maps load times

Next.js: restrict pages to authenticated users