- Home
- Blog
- Ruby & Rails Core
- ActiveRecord where vs find_by — Which to Use When
ActiveRecord where vs find_by — Which to Use When
where returns a relation, find_by returns one record or nil. Here's when each is correct, what breaks if you pick wrong, and Rails 7/8 production patterns.
Most Rails developers use where and find_by interchangeably early on. That works until it doesn’t.
The Core Difference
find_by executes immediately and returns one record (or nil):
User.find_by(email: "raza@example.com")
# SELECT * FROM users WHERE email = 'raza@example.com' LIMIT 1
# => #<User ...> or nil
where returns an ActiveRecord::Relation — it doesn’t hit the database until you call something that forces execution:
User.where(email: "raza@example.com")
# => ActiveRecord::Relation (not executed yet)
User.where(email: "raza@example.com").first
# SELECT * FROM users WHERE email = 'raza@example.com' LIMIT 1
When find_by Is the Right Call
Use find_by when:
- You expect zero or one record
- You want nil back if nothing matches (not an exception)
- You are done chaining after this call
account = Account.find_by(subdomain: params[:subdomain])
return render :not_found unless account
find_by(id: x) is safer than find(x) — returns nil instead of raising ActiveRecord::RecordNotFound. Use find only when absence is a bug.
When where Is the Right Call
Use where when:
- You expect multiple records
- You need to chain more conditions, scopes, or ordering
- You are building a query progressively
User.where(role: "admin").where("created_at > ?", 1.week.ago).order(:name)
where composes cleanly with scopes:
User.active.where(plan: "pro").limit(20)
find_by cannot chain. The moment you need another condition or ordering, use where.
The Trap: find_by with Multiple Conditions
User.find_by(role: "admin", active: true)
# Returns ONE admin user — whichever the DB returns first
If you meant all admin users, this silently gives you one. Use where.
What About find?
find raises ActiveRecord::RecordNotFound if the record is missing:
User.find(params[:id])
# Raises if not found — Rails handles the 404 for you
Use find in controller show actions. Use find_by when you will handle nil yourself.
Performance
No difference in the query itself. Both generate the same SQL for single-record lookups. find_by adds LIMIT 1 automatically.
Quick Reference
| Method | Returns | Raises if missing | Chainable |
|---|---|---|---|
| find | single record | yes | no |
| find_by | single record or nil | no | no |
| where | Relation | no | yes |
The Rule
- One record, done: find_by
- One record, absence is a bug: find
- Multiple records or chaining: where
Check every find_by in your codebase where you immediately call .first or .last on the result. That is where(…).first and should be written explicitly. Learn more about rails in our guide: Does ActiveRecord find_by Return nil? (Guide 2026).
Was this article helpful?
Your feedback helps us improve our content
How We Verify Conversions
Every conversion shown on this site follows a strict verification process to ensure correctness:
- Compare results on same dataset — We run both SQL and ActiveRecord against identical test data and verify results match
-
Check generated SQL with
to_sql— We inspect the actual SQL Rails generates to catch semantic differences (INNER vs LEFT JOIN, WHERE vs ON, etc.) - Add regression tests for tricky cases — Edge cases like NOT EXISTS, anti-joins, and predicate placement are tested with multiple scenarios
- Tested on Rails 8.1.1 — All conversions verified on current Rails version to ensure compatibility
Last updated: October 03, 2026
Try These Queries in Our Converter
See the SQL examples from this article converted to ActiveRecord—and compare the SQL Rails actually generates.
Deep Dive into ActiveRecord
Raza Hussain
Full-stack developer specializing in Ruby on Rails, React, and modern JavaScript. 15+ years upgrading and maintaining production Rails apps. Led Rails 4/5 → 7 upgrades with 40% performance gains, migrated apps from Heroku to Render cutting costs by 35%, and built systems for StatusGator, CryptoZombies, and others. Available for Rails upgrades, performance work, and cloud migrations.
More on Joins & Associations
Rails Counter Cache—When Posts.count Brought Down Production
posts.count hit the database on every request and brought down production. Here's how counter cache fixes it, what to watch for, and how to backfill safely.
SQL JOIN Made Sense. ActiveRecord includes() Confused Me for Weeks. Finally Clicked.
SQL JOINs made sense. ActiveRecord's includes() confused me for weeks. Here's the mental model that finally made it click and when to use each.
ActiveRecord Ran 47 Identical Queries—Bullet Gem Found the Pattern
ActiveRecord was running 47 identical queries per request. Bullet found the pattern. Here's what caused it, how to read Bullet's output, and the fix.
Leave a Response
Responses (0)
No responses yet
Be the first to share your thoughts