The go-to resource for upgrading Ruby, Rails, and your dependencies.

Rails 4 Transition: From *_filter to *_action Callbacks


The Semantic Clarity of Action Callbacks

When software frameworks evolve, they often reflect a growing understanding of how developers actually use them. Consider the evolution of medical terminology: what was once broadly called “consumption” eventually became the more precise “tuberculosis,” which better described the specific bacterial infection and enabled more targeted treatment. The terminology change wasn’t just cosmetic — it reflected a deeper understanding of the underlying condition.

Rails 4.0 introduced a similar refinement when it renamed controller callbacks from *_filter to *_action. The old names — before_filter, after_filter, and around_filter — suggested something applied to the entire request cycle. The new names — before_action, after_action, and around_action — more accurately describe their actual scope: they run in the context of controller actions, not across arbitrary request processing.

This change didn’t alter behavior, but it did clarify intent. For teams upgrading legacy Rails applications to modern versions, understanding this transition is essential for maintaining clean, maintainable controller code.

What Changed in Rails 4.0

Rails 4.0 introduced new method names for controller callbacks while maintaining backward compatibility with the old names. The mapping is straightforward:

  • before_filter became before_action
  • after_filter became after_action
  • around_filter became around_action
  • skip_before_filter became skip_before_action
  • skip_after_filter became skip_after_action
  • skip_around_filter became skip_around_action
  • prepend_before_filter became prepend_before_action
  • prepend_after_filter became prepend_after_action
  • prepend_around_filter became prepend_around_action
  • append_before_filter became append_before_action
  • append_after_filter became append_after_action
  • append_around_filter became append_around_action

The old *_filter methods were immediately aliased to the new *_action methods, meaning existing code continued to work without modification. However, the old names were deprecated and eventually removed in Rails 5.0.

Why the Rename Happened

The original *_filter naming came from Rails’ early days when the framework drew inspiration from servlet filters in Java web frameworks. However, this terminology was misleading in the Rails context.

In Rails, these callbacks don’t filter requests in the traditional sense of accepting or rejecting them based on criteria. Instead, they execute code before, after, or around controller actions. They’re tightly coupled to the action lifecycle within a specific controller, not general-purpose request filters that might apply across the entire application stack.

The rename to *_action better reflects this reality. It makes the code more self-documenting and aligns with Rails’ philosophy of convention over configuration by using terminology that matches the framework’s actual behavior.

The Upgrade Path

For applications running Rails 3.2 or earlier, upgrading to Rails 4.0 and beyond requires updating these callback names. While the old names continued to work in Rails 4.x, they triggered deprecation warnings and were removed entirely in Rails 5.0.

Finding All Filter Usage

The first step is identifying all uses of the old filter syntax. Since these are method calls, a simple grep can surface most instances:

grep -r "before_filter\|after_filter\|around_filter" app/controllers/

For a more comprehensive search that includes skips and prepends:

grep -rE "(before|after|around)_filter|skip_(before|after|around)_filter|prepend_(before|after|around)_filter|append_(before|after|around)_filter" app/controllers/

Automated Conversion

For large codebases with hundreds of controllers, manual replacement is error-prone and time-consuming. A scripted approach is more reliable.

Using sed on Unix-based systems:

find app/controllers -name "*.rb" -type f -exec sed -i '' \
  -e 's/before_filter/before_action/g' \
  -e 's/after_filter/after_action/g' \
  -e 's/around_filter/around_action/g' \
  -e 's/skip_before_filter/skip_before_action/g' \
  -e 's/skip_after_filter/skip_after_action/g' \
  -e 's/skip_around_filter/skip_around_action/g' \
  -e 's/prepend_before_filter/prepend_before_action/g' \
  -e 's/prepend_after_filter/prepend_after_action/g' \
  -e 's/prepend_around_filter/prepend_around_action/g' \
  -e 's/append_before_filter/append_before_action/g' \
  -e 's/append_after_filter/append_after_action/g' \
  -e 's/append_around_filter/append_around_action/g' \
  {} \;

On Linux systems without BSD sed, omit the empty string after -i:

find app/controllers -name "*.rb" -type f -exec sed -i \
  -e 's/before_filter/before_action/g' \
  -e 's/after_filter/after_action/g' \
  -e 's/around_filter/around_action/g' \
  -e 's/skip_before_filter/skip_before_action/g' \
  -e 's/skip_after_filter/skip_after_action/g' \
  -e 's/skip_around_filter/skip_around_action/g' \
  -e 's/prepend_before_filter/prepend_before_action/g' \
  -e 's/prepend_after_filter/prepend_after_action/g' \
  -e 's/prepend_around_filter/prepend_around_action/g' \
  -e 's/append_before_filter/append_before_action/g' \
  -e 's/append_after_filter/append_after_action/g' \
  -e 's/append_around_filter/append_around_action/g' \
  {} \;

Using RuboCop for Detection and Correction

RuboCop, the Ruby static code analyzer, includes a cop specifically for this deprecation: Rails/ActionFilter. This cop detects the old filter syntax and can automatically correct it.

First, ensure RuboCop is configured with the Rails extension in your Gemfile:

group :development do
  gem 'rubocop'
  gem 'rubocop-rails'
end

Run RuboCop to detect violations:

bundle exec rubocop --only Rails/ActionFilter

Auto-correct the violations:

bundle exec rubocop --only Rails/ActionFilter --autocorrect-all

This approach is generally safer than manual sed commands because RuboCop understands Ruby syntax and won’t accidentally replace comments or strings that happen to contain these words.

Testing the Migration

After renaming filters to actions, run your full test suite to verify behavior hasn’t changed:

bundle exec rspec
# or
bundle exec rails test

Since this is purely a naming change with identical semantics, tests should pass without modification. Any failures likely indicate pre-existing issues that weren’t related to the rename.

Edge Cases and Gotchas

Metaprogramming and Dynamic Calls

If your application uses metaprogramming to dynamically define or call filters, you’ll need to update those as well:

# Old
[:authenticate, :authorize].each do |filter_name|
  before_filter filter_name
end

# New
[:authenticate, :authorize].each do |action_name|
  before_action action_name
end

Search for dynamic calls using send:

grep -r "send.*_filter" app/

Gems and Engines

Third-party gems and Rails engines may still use the old *_filter syntax internally. If you’re using Rails 4.x, this will continue to work due to aliasing. However, if you’re upgrading directly to Rails 5.0 or later, you may need to update gem versions or fork and patch gems that haven’t been maintained.

Check your Gemfile.lock for gems that might define controllers:

grep -l "before_filter\|after_filter\|around_filter" $(bundle show --paths)

Documentation and Comments

Don’t forget to update inline comments and documentation that reference the old terminology:

# Old comment style
# We use a before_filter to ensure authentication

# Updated
# We use a before_action to ensure authentication

Long-Term Maintainability

Adopting *_action terminology aligns your codebase with modern Rails conventions. This reduces cognitive overhead for developers familiar with current Rails patterns and makes the codebase easier to onboard new team members into.

More importantly, it removes technical debt that would have eventually required remediation anyway. Rails 5.0 removed support for *_filter entirely, so any application planning to stay current with Rails releases must make this transition.

By addressing this change proactively during a Rails 4 upgrade, you avoid emergency fixes later when you need to upgrade to Rails 5 or beyond for security patches or new features.

Sponsored by Durable Programming

Need help maintaining or upgrading your Ruby on Rails application? Durable Programming specializes in keeping Rails apps secure, performant, and up-to-date.

Hire Durable Programming