Stop Killing Your Database: 5 ActiveRecord Tips for Faster Rails Apps

Published: (February 21, 2026 at 07:19 PM EST)
3 min read
Source: Dev.to

Source: Dev.to

In this article I share my journey with ActiveRecord. When I first started with Rails, I thought ActiveRecord was just “magic” to save data to the database. After working on larger projects I realized there’s a lot more to it. It’s easy to write code that works, but it’s also easy to write code that makes your app slow. Below are five levels of ActiveRecord knowledge, moving from simple queries to more advanced optimizations.

LEVEL 1: The Basics (Chaining)

Most beginners know find and where. The cool thing about ActiveRecord is that you can chain these methods together endlessly until you actually ask for the data.

# Instead of doing this
users = User.where(active: true)
recent_users = users.where('created_at > ?', 1.day.ago)

# You can chain it nicely
User.where(active: true).where('created_at > ?', 1.day.ago)

This generates only one SQL query. Rails waits until the last moment to touch the database.

LEVEL 2: Cleaning up with Scopes

Repeating the same where conditions in multiple controllers is annoying. Define scopes in your model; they act like class methods.

# app/models/post.rb
class Post  { where(published: true, deleted: false) }
  scope :recent, -> { where('created_at > ?', 1.week.ago) }
end

Now the controller code is much cleaner:

# app/controllers/posts_controller.rb
def index
  @posts = Post.live.recent
end

LEVEL 3: Solving the N+1 Problem (Advanced)

The most common mistake is loading a parent record and then looping through its children, causing a separate query for each association.

# controller
@posts = Post.all

# view (ERB)

Rails will run 1 query for the posts and N additional queries for the users (one per post). With 100 posts that’s 101 queries, which kills performance.

STEP 1: Use includes

Tell ActiveRecord to fetch the associated records in a single query.

@posts = Post.includes(:user).all

Rails now runs only 2 queries:

  1. Fetch all posts.
  2. Fetch all users associated with those posts.

LEVEL 4: Pluck vs Map (Optimization)

When you only need a single column, avoid loading full ActiveRecord objects.

# Heavy: loads all User objects into memory
User.all.map(&:email)

Instead, use pluck to retrieve the column directly from the database:

# Fast: fetches only the email column
User.pluck(:email)

pluck returns an array of strings without instantiating model objects, saving memory and time.

LEVEL 5: Use exists? instead of present?

If you only need to know whether a record exists, don’t load the record.

# Bad: loads the post into memory just to check existence
if Post.where(id: params[:id]).present?
  # …
end

# Good: runs a lightweight SQL `SELECT 1` with LIMIT 1
if Post.exists?(params[:id])
  # …
end

exists? returns a boolean and stops querying as soon as it finds a match.


Mastering these few tricks will make your Rails applications faster and your code cleaner.

0 views
Back to Blog

Related posts

Read more »

Stop Using .any? the Wrong Way in Rails

Introduction A single block passed to .any? can silently load thousands of records into memory—no warnings, no errors, just unnecessary objects. Most Rails dev...

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...