Stop Using .any? the Wrong Way in Rails

Published: (February 24, 2026 at 10:30 AM EST)
4 min read
Source: Dev.to

Source: Dev.to

Introduction

A single block passed to .any? can silently load thousands of records into memory—no warnings, no errors, just unnecessary objects. Most Rails developers don’t notice it.

Both .any? and .exists? answer the same simple question: Is there at least one record?
Under the hood, however, they can behave very differently. This article explains what happens when you call each method, when to use which, and how to avoid a common performance trap.

Basic Usage – Checking Existence Only

If you just need to know whether a relation contains any records, both .any? and .exists? generate the same efficient query:

user.posts.any?
#=> SELECT 1 AS one FROM "posts" WHERE "posts"."user_id" = 1 LIMIT 1

user.posts.exists?
#=> SELECT 1 AS one FROM "posts" WHERE "posts"."user_id" = 1 LIMIT 1

No objects are loaded into memory. Both methods ask the database a simple yes/no question and return immediately after finding the first match.

The same applies when you chain .where—as long as you don’t pass a block to .any?:

user.posts.where(published: true).any?
#=> SELECT 1 AS one FROM "posts"
#   WHERE "posts"."user_id" = 1 AND "posts"."published" = true
#   LIMIT 1

user.posts.where(published: true).exists?
#=> SELECT 1 AS one FROM "posts"
#   WHERE "posts"."user_id" = 1 AND "posts"."published" = true
#   LIMIT 1

If this is all you need, pick whichever reads better in your code; there’s no performance difference.

.any? With a Block

The moment you pass a block to .any?, Rails changes its behavior completely. Instead of asking the database, it loads every matching record into memory and filters in Ruby:

user.posts.any? { |post| post.published? }
#=> SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 1

What Happens

  1. Loads all posts from the database.
  2. Instantiates an ActiveRecord object for each row.
  3. Iterates over them in Ruby.
  4. Returns a boolean.

In development this may look harmless, but in production it can be disastrous. If a user has 50,000 posts, you’ve just loaded 50,000 objects into memory just to check whether one of them is published.

Implementation Insight

def any?(*args)
  return false if @none

  return super if args.present? || block_given?
  !empty?
end

When a block is given, Rails delegates to Enumerable#any?, which requires the full collection in memory. You’ve effectively moved the filtering from SQL to Ruby.

Prefer SQL for Filtering

Push the condition into the database instead:

user.posts.exists?(published: true)
#=> SELECT 1 AS one FROM "posts"
#   WHERE "posts"."user_id" = 1 AND "posts"."published" = TRUE
  • One row, one column.
  • Stops at the first match.
  • No object instantiation.

Same result, far lower cost.

When .any? Is Actually the Right Choice

There is an important exception: if the relation is already loaded, .any? will not hit the database again.

users = User.includes(:posts)

users.each do |user|
  user.posts.any? { |post| post.published? }   # no extra query
end

Because posts were preloaded, .any? works entirely in memory. In this scenario:

  • .any? → no additional query.
  • .exists? → forces a new SQL query.

Using .exists? here could introduce unnecessary database calls and even create an N+1 pattern. Rails internally checks whether the relation is loaded; .any? behaves like a normal Ruby collection.

Guideline Summary

Relation StateRecommended Method
Not loadedexists? (or any? without a block)
Already loadedany? (without a block)
Need to evaluate a block on an unloaded relationAvoid .any? with a block; use SQL (exists? with conditions)

Conclusion

Small differences in ActiveRecord APIs can have real production impact. Before calling .any?, ask yourself:

  • Am I merely checking existence? → Use exists? (or any? without a block).
  • Am I about to load an entire collection? → Avoid .any? with a block on unloaded relations.

Understanding these nuances helps you write more efficient Rails code and prevents hidden performance pitfalls.

0 views
Back to Blog

Related posts

Read more »

Simplifying timestamp toggles in Rails

Overview I often use timestamps, like completed_at, as a boolean flag. It offers a bit more meta data than a real boolean, but on the UI you typically want to...