If you've ever investigated a slow Rails endpoint, chances are you've encountered one of the most common performance problems in the Rails ecosystem:
N+1 Queries.
The scary part?
Your application can appear perfectly healthy during development, pass all tests, and work flawlessly in production for months.
Then one day your dataset grows, traffic increases, and suddenly an endpoint that used to respond in 100ms now takes several seconds.
No infrastructure changes.
No database outages.
No major code deployments.
Just a silent performance killer hiding inside your ActiveRecord relationships.
In this article we'll explore:
- What N+1 Queries are
- Why they happen so frequently in Rails applications
- How they impact performance
- Techniques to prevent them
- Tools that can help detect them before they reach production
What Is an N+1 Query?
An N+1 Query occurs when your application executes:
- One query to fetch a collection of records
- One additional query for each record in that collection
Let's imagine a simple blog application.
Each Post belongs to an Author.
class Post < ApplicationRecord
belongs_to :author
end
Now imagine the following code:
posts = Post.limit(100)
posts.each do |post|
puts post.author.name
end
At first glance this looks innocent.
However, Rails may execute:
SELECT * FROM posts LIMIT 100;
Then:
SELECT * FROM authors WHERE id = 1;
SELECT * FROM authors WHERE id = 2;
SELECT * FROM authors WHERE id = 3;
...
Instead of executing two queries, the application executes:
1 + 100 = 101 queries
This pattern is known as an N+1 Query.
Why Does This Happen?
The answer lies in one of Rails' most powerful features:
Lazy Loading.
ActiveRecord delays loading associated records until they are actually needed.
For example:
post.author
does not load the author when the post is fetched.
It loads the author only when that line is executed.
This behavior is incredibly convenient because it reduces unnecessary database access.
However, when used inside loops, it can accidentally create hundreds or even thousands of additional queries.
The framework is doing exactly what we asked it to do.
The problem is that we didn't realize how many times we were asking.
Why N+1 Queries Are Dangerous
Many engineers focus on query execution time.
The real problem is query volume.
Imagine:
- 10 users → 11 queries
- 100 users → 101 queries
- 1,000 users → 1,001 queries
- 10,000 users → 10,001 queries
As your data grows, the number of database round trips grows with it.
This creates:
- Increased database load
- Higher application CPU usage
- More network overhead
- Longer request times
- Reduced scalability
What worked perfectly in staging may become a production nightmare months later.
How to Avoid N+1 Queries
The most common solution is eager loading.
Rails allows you to preload associations before they are accessed.
Instead of:
posts = Post.limit(100)
Use:
posts = Post.includes(:author).limit(100)
Rails now loads:
SELECT * FROM posts LIMIT 100;
SELECT * FROM authors WHERE id IN (...);
Only two queries.
No matter how many posts are returned.
This is one of the easiest performance wins available in Rails.
Common Places Where N+1 Queries Hide
Many engineers look only at controllers.
In reality, N+1 issues often appear in:
API Serializers
PostSerializer.new(posts)
Jbuilder Templates
json.author_name post.author.name
GraphQL Resolvers
field :author
View Templates
<%= post.author.name %>
Background Jobs
users.each do |user|
user.account.plan
end
Any place that loops through records and accesses associations can potentially create N+1 Queries.
Helpful Tools for Detecting N+1 Queries
One of the best investments a Rails team can make is detecting N+1 Queries early.
Bullet Gem
The most popular N+1 detection tool in the Rails ecosystem.
It automatically alerts developers when associations should be eager loaded.
Example notification:
USE eager loading detected
Add to your query: .includes(:author)
Rack Mini Profiler
Provides detailed request analysis.
Helps identify:
- Slow queries
- Query counts
- Rendering bottlenecks
- Performance hotspots
Skylight
Excellent for production monitoring.
It helps visualize database activity and identify endpoints suffering from excessive query counts.
New Relic
Provides detailed transaction tracing and database monitoring.
Very useful for identifying performance degradation caused by query-heavy endpoints.
Datadog APM
Widely used in large-scale Rails environments.
Offers visibility into:
- SQL execution
- Request traces
- Database latency
- Application bottlenecks
Final Thoughts
N+1 Queries are dangerous because they don't usually break functionality.
Your tests pass.
Your code looks clean.
Users may not notice the problem immediately.
But as data grows, the hidden cost grows with it.
Understanding N+1 Queries is one of the first steps toward becoming a performance-conscious Rails engineer.
And the good news?
Many N+1 issues can be fixed with a single line of code.
In the next article, we'll dive deeper into advanced eager loading strategies, compare includes, preload, and eager_load, and discuss how to detect N+1 Queries in production environments before your users notice them.
Top comments (0)