June 19, 2025
Yesterday morning, I had a deep and enjoyable technical interview covering a wide range of topics. But there was one moment I can’t forget: I completely fumbled a question about avoiding the N+1 problem in Rails.
Maybe it was the lack of coffee — or just nerves — but my brain didn’t cooperate, and ironically, the example I gave was the N+1 problem itself.
So I did what any good developer should: I spent the afternoon (and part of this morning) revisiting my trusted Rails books and docs to refresh my knowledge. Here’s a breakdown of the problem — and the right ways to handle it.
The N+1 Anti-pattern
User.all.each do |user|
user.articles.each do |article|
puts article.title
end
end
What happens here?
- 1 query to get all users.
- 1 additional query per user to get their articles.
That’s N+1 queries , and it kills performance as data grows.
The Right Solutions
1. includes — Eager Loading
User.includes(:articles).each do |user|
user.articles.each do |article|
puts article.title
end
end
Rails runs 2 queries only : One for users, and one for all articles using WHERE user_id IN (…).
Use it when you’re displaying associated data without filtering or sorting it.
2. preload — Always Separate Queries
User.preload(:articles).each do |user|
user.articles.each do |article|
puts article.title
end
end
Forces Rails to run separate queries for each association (like includes, but never uses JOINs ). Good when you’re not using conditions or order clauses on the associated table.
3. eager_load — SQL JOIN
User.eager_load(:articles).each do |user|
user.articles.each do |article|
puts article.title
end
end
Uses a LEFT OUTER JOIN to load everything in one query. Best when you need to filter or sort based on attributes in the joined table (e.g., articles.published_at DESC).
4. Optimizing with pluck or select
If you only need a few fields:
User.includes(:articles).each do |user|
user.articles.pluck(:id, :title)
end
Or more advanced optimization with joins and select:
User.joins(:articles).select('users.*, articles.title AS article_title').each do |user|
puts user.article_title
end
Bonus: Detect N+1 Issues Automatically
Use the bullet gem in development:
# Gemfile
gem 'bullet'
# config/environments/development.rb
config.after_initialize do
Bullet.enable = true
Bullet.alert = true
Bullet.console = true
end
It will warn you in real-time when you’re making N+1 mistakes.
Final Thoughts
We all have off days in interviews — but what really matters is how we respond. For me, this was a chance to sharpen my Rails skills and make sure I never miss this question again.
If you’re working with Active Record and associations, knowing how to avoid N+1 is essential. Bookmark this for the next time you’re optimizing your queries — or prepping for your own technical interview!
Top comments (0)