🚨 N+1 Queries in Rails: What, Why, and How to Fix Them

Austin PremodAustin Premod
2 min read

If you’ve worked on a Rails app that queries the database, chances are you’ve encountered the infamous N+1 query problem—possibly without even realizing it. It can silently degrade performance, especially when dealing with large datasets.

In this post, we’ll cover:

  • What is an N+1 query?

  • Why is it a problem?

  • How to identify it?

  • How to fix it?

🤔 What is an N+1 Query?

Let's explain it with an example where a user has many posts

User.each do |user|
‎‎‎ㅤuser.posts # separate query for each user
end

This example might look normal but there is a huge problem as we will fetch posts from each user creating an N+1 query where 1 is the parent query and N is all the calls the associated model will call.

🐢 Why Is It a Problem?

It might not be a problem in development where you have 5 users and a few posts but in production where there can be thousands of users and each user has hundreds of posts will result in a huge performance and scalability issue as more queries = more stress on your DB

🔍 How to Identify N+1 Queries

We can identify N+1 queries with the help of the Bullet Gem if you're using ActiveRecord with relational databases. However, if you're using a NoSQL database like MongoDB, you can rely on Logs to detect such issues. Logs can be used in both cases.

✅ How to Fix N+1 Queries

We can solve it by just using 1 word “includes”.

User.includes(:posts).each do |user|
user.posts # we eager loaded the association
end

If you are using NoSQL example Mongoid we can “embed” documents also

class User
include Mongoid::Document
embeds_many :posts
end

User.each do |user|
user.posts # no DB call here — already part of the user document
end

When to embed vs reference?

EmbedReference
One-to-few (like 5–20 items)One-to-many (like 1000s of posts)
Data is always accessed by the parentData needs to be queried independently
Example: Address in UserExample: Posts by User

🗣️ Final Thoughts

The N+1 query issue is one of those classic Rails pitfalls that’s easy to fall into but just as easy to fix—once you spot it. So the next time you're looping over associations, check your logs, preload your data, and keep your app speedy and scalable. 🚀

0
Subscribe to my newsletter

Read articles from Austin Premod directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Austin Premod
Austin Premod