When trying to make a Rails application faster, I find there are often many approaches I could take. When I first started trying to make my applications faster, this was terribly daunting. Lacking knowledge of where I should focus – much less how to tackle slowness – I sought advice. I applied much of what I learned first in Hostedwiki, but it has since influenced every Rails application I build.
Determining if something is slow, with real data, is my first step. Using a tool like New Relic, Skylight, AppSignal, or many others, I can easily measure percentile based averages at a useful level: controller actions.
These tools allow me to figure out which controllers actions are having the biggest impact on my application's performance by using useful averages like median request times and 95% percentile request times weighted by the number of times an action is called. For example, if I have a slow 1000ms request that only gets called once a minute, but a 700ms request that gets called 30 times a minute, focusing on the faster request will likely give me the biggest gains.
Focusing on impact means I will make larger improvements early on that quickly reduce the amount of time my users wait for the page to load and reduce the number of servers need to run the application at a given rate of requests.
Now that I have a prioritized list of requests, I need to figure out how to make them faster. In most cases, I simply need to figure out how to reduce the number of database queries to render the page.
I first look for N+1 queries, especially on pages that list multiple rows of a database table. I usually take a quick look at the code to see if there's anything obvious, then reach for bullet. I'm not going to go into how to use bullet, as that has been covered, and they have a good README.
If that still doesn't give me a clear path forward, it usually means there is just a lot of complex data being loaded and parsed. I find this on pages that render full taxonomies, have complex relationships between data in many tables, or just display a ton of unrelated information.
In these cases, I usually cannot find a straightforward way to optimize database access – I just need to grind it out often dropping down to Arel or more direct SQL access. At this point, it's super helpful to have a solid grasp of SQL and what special features my database (Postgres) has that I might be able to leverage.
I reach for caching last. If I can get a controller action to be fast enough without caching, I am always happier. It means there are less forks in the logic that need to be considered and I never need to worry about displaying stale data to the user – intended or not.
Understanding how Rails handles caching in the view layer, and some of the basic principles behind why it works that way will help you avoid problems and know what and when to cache. DHH's explanation from a few years ago covers the basics extremely well and is still relevant. The basic idea is make sure your using all the
touch features of Rails appropriately and everything will Just Work.
Go forth and make your Rails applications faster!