When it comes to speeding up Rails apps, sometimes people get too fancy and forget the most important fundamentals.
Like most things in life, the Pareto Principle (80/20 rule) applies to Rails performance too:
80% of your app's slowness is caused by 20% of slow code or infrastructure.
Now, don't get hung up on the exact numbers - the 80:20 ratio is a ballpark. The real numbers may be 90:10 or 75:25.
The point is: instead of trying to optimise every little detail, first focus on the big issues. The places where you will get the most bang for your buck.
And for most web apps, that's the DATABASE.
Before you do anything complicated like memory profiling or complex caching, optimise your database queries, which means two things:
1. Add indexes (or indices, if you please) to important columns - any columns you use for lookups (using the where clause) or for sorting records.
2. Avoid N+1 queries by eager loading associated records.
For example, on Learnetto when I load courses, I also load associated teacher records with includes.
@courses = Course.includes(:teacher)
You can easily detect N+1 queries by looking at your Rails log and looking for multiple similar SQL queries.
Or you can use something like the bullet gem which automatically detects N+1 queries.
(A word of warning: Once I worked on a large client project and adding this gem started causing mysterious test failures, which had us tearing our hair out. After many hours of poring over stack traces, we realised the gem was the culprit, removed it and the problem went away.)
But here too, you must apply the 80/20 rule.
Not all N+1 queries are worth eliminating!
If you have a horrible N+1 query on a page of your website that no one ever visits, then it's not worth optimising (yet).
So, focus on the most popular pages and controller actions which your customers are actually touching.
For example, find the top 10 most visited pages on your site from your website analytics and optimise queries for those pages.
Or if you use an App Performance Monitoring (APM) system like New Relic or Scout, you can get better data.
In the New Relic dashboard, under the Transactions tab, you can see a list of transactions ranked by where users spend the most time (or by the slowest response times).
Below is an example from the courses#show transaction for Learnetto:
Like most things in life, the Pareto Principle (80/20 rule) applies to Rails performance too:
80% of your app's slowness is caused by 20% of slow code or infrastructure.
Now, don't get hung up on the exact numbers - the 80:20 ratio is a ballpark. The real numbers may be 90:10 or 75:25.
The point is: instead of trying to optimise every little detail, first focus on the big issues. The places where you will get the most bang for your buck.
And for most web apps, that's the DATABASE.
Before you do anything complicated like memory profiling or complex caching, optimise your database queries, which means two things:
1. Add indexes (or indices, if you please) to important columns - any columns you use for lookups (using the where clause) or for sorting records.
2. Avoid N+1 queries by eager loading associated records.
For example, on Learnetto when I load courses, I also load associated teacher records with includes.
@courses = Course.includes(:teacher)
You can easily detect N+1 queries by looking at your Rails log and looking for multiple similar SQL queries.
Or you can use something like the bullet gem which automatically detects N+1 queries.
(A word of warning: Once I worked on a large client project and adding this gem started causing mysterious test failures, which had us tearing our hair out. After many hours of poring over stack traces, we realised the gem was the culprit, removed it and the problem went away.)
But here too, you must apply the 80/20 rule.
Not all N+1 queries are worth eliminating!
If you have a horrible N+1 query on a page of your website that no one ever visits, then it's not worth optimising (yet).
So, focus on the most popular pages and controller actions which your customers are actually touching.
For example, find the top 10 most visited pages on your site from your website analytics and optimise queries for those pages.
Or if you use an App Performance Monitoring (APM) system like New Relic or Scout, you can get better data.
In the New Relic dashboard, under the Transactions tab, you can see a list of transactions ranked by where users spend the most time (or by the slowest response times).
Below is an example from the courses#show transaction for Learnetto:
Another source of performance issue I've noticed is unnecessary gem dependencies.
Including too many gems or heavy gems that are not really needed can bloat your application and slow down load times.
A very common example of an unnecessary gem is a simple API wrapper for a third party service. I've found that 99% of the times, using a generic library for making HTTP calls is more than enough (Net::HTTP library or httparty gem).
The most recent example is when I wanted to use the OpenAI API in a fun project for Instagram profile analysis, I first reached for the ruby-openai gem. When I ran into some configuration issue before I could make my first API call, I decided to switch to httparty. It turned out to be perfectly adequate for my use case. And I was already using it in the project.
The most recent example is when I wanted to use the OpenAI API in a fun project for Instagram profile analysis, I first reached for the ruby-openai gem. When I ran into some configuration issue before I could make my first API call, I decided to switch to httparty. It turned out to be perfectly adequate for my use case. And I was already using it in the project.
So regularly audit your Gemfile, remove unused gems, and consider lighter alternatives or custom solutions for heavy gems that are only partially used.
What other low hanging fruit do you first check for when you want to speed up your Rails app?
What other low hanging fruit do you first check for when you want to speed up your Rails app?