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


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?
Embed | Reference |
One-to-few (like 5–20 items) | One-to-many (like 1000s of posts) |
Data is always accessed by the parent | Data needs to be queried independently |
Example: Address in User | Example: 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. 🚀
Subscribe to my newsletter
Read articles from Austin Premod directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
