Mateo Silva
November 2025
35 minute read

Every experienced Ruby on Rails developer eventually encounters the 'fat model' problem. As your application scales, your Active Record models swell with scopes, validations, callbacks, and methods that are often shared across multiple models, leading to violation of the DRY (Don't Repeat Yourself) principle. This proliferation of code makes maintenance a nightmare and severely degrades code readability.
Enter Rails Concerns. Introduced with Rails 3.1, the ActiveSupport::Concern module provides a structured, elegant way to extract shared behavior into reusable modules, effectively allowing you to DRY Up Models Rails and controllers. Itβs an essential technique for Rails Best Practices and maintaining a manageable codebase.
This comprehensive guide will demystify Rails Concerns, show you exactly how to implement them correctly, and provide expert best practices to ensure your Rails application remains clean, modular, and easy to scale. We will focus on improving code organization and adhering to the core tenets of the Ruby and Rails philosophy.
At its heart, a Rails Concern is simply a Ruby module that includes the special module ActiveSupport::Concern. While you could use a plain Ruby module with an include statement, ActiveSupport::Concern simplifies the process significantly by handling two common mixin scenarios:
Dependencies: It ensures that when one module requires another, the dependencies are loaded correctly.
`ClassMethods`: It automatically creates a special block called class_methods to hold methods that should be added as class methods (like scopes) to the including class, making the syntax much cleaner.
The standard convention is to store concerns in the app/models/concerns or app/controllers/concerns directories, which Rails automatically loads for you.
A well-structured Rails Concern for an Active Record model generally uses the following template:
Let's explore common scenarios where Rails Concerns are the ideal solution for model refactoring.
Many applications require 'soft deletion' instead of permanent DESTROY. This is a perfect candidate for a Rails Concern as the logic (deleted_at column, default scope, destroy override) is identical across many models (e.g., Post, User, Comment).
To use it, simply include SoftDeletable in any model, and you've instantly added complex, reusable logic.
If your Article and Product models both use a tagging system (perhaps via a has_many :tags association and related methods), the common logic can be extracted.
While Rails Concerns are most often associated with models, they are equally effective in controllers, especially for handling recurring logic such as authentication, authorization, or common response formats.
If several controllers require a user to be logged in and an administrator, this logic should be abstracted. This concern defines `before_action` filters that will be automatically applied to the including controller.
A controller can then include it and use the methods, achieving DRYness across the control layer:
While Rails Concerns are powerful, they can quickly lead to 'mixin hell' if used incorrectly. Adhering to these Rails Best Practices is vital for long-term code health.
A concern should encapsulate a single, cohesive domain of responsibility. If your Publishable concern also includes logic for Commenting, it's doing too much. Concerns should be small, focused, and easily understandable. Think of them as traits or roles.
Do not rely on the including class to have specific instance variables (e.g., @user). This creates implicit coupling and makes the concern harder to reuse and test. Instead, rely on methods or parameters.
The clear separation provided by included do... end for boilerplate calls (like has_many, validates, before_action) and module ClassMethods for scopes and class-level helpers is the core benefit of ActiveSupport::Concern. Use it consistently to improve clarity.
Rails Concerns are not a silver bullet for refactoring Rails models. They are best for reusable, cross-cutting behavior that needs to interact directly with Active Record methods (scopes, associations, callbacks).
For complex business logic that involves multiple models or external services, use Service Objects. For logic related to formatting or presentation, use Decorators (like the Draper gem).
While Rails Concerns help DRY up Models, sometimes the shared logic is actually an indication that a different structural pattern is needed. Over-reliance on concerns can lead to 'module soup' where itβs impossible to track where a method originates.
If you find that most of your models are including the same three or four concerns, and they share the same table, consider using Single Table Inheritance (STI). With STI, you define a base model and use subclasses (e.g., Account, with subclasses CustomerAccount and AdminAccount) to organize related, but distinct, logic.
Instead of creating controller concerns for authorization (which mixes business logic with controller structure), use Policy Objects (like the Pundit gem). Policies clearly separate authorization rules from the model and controller, making them easier to test and maintain.
Mastering Rails Concerns is a crucial step in maturing as a Ruby on Rails developer. By correctly leveraging ActiveSupport::Concern, you can effectively tackle the 'fat model' problem, extract shared functionality, and adhere strictly to the DRY principle. This not only improves code readability and testability but also speeds up onboarding for new team members.
Use concerns for logical, vertically sliced behavior that directly interacts with Active Record. Combine them thoughtfully with other Rails Best Practices like Service Objects and Policy Objects to build scalable, high-quality applications that stand the test of time.
You should use a Rails Concern (by including ActiveSupport::Concern) whenever the module needs to define both instance methods and class methods (like scopes) on the including class, or when the concern itself needs to include other modules. The included and module ClassMethods blocks provided by ActiveSupport::Concern simplify this complex mixin behavior.
Yes, you can use Rails Concerns in View Helpers for organizing shared helper methods. The structure remains the same, but instead of defining has_many or scopes, you would define methods that manipulate data for the view, such as formatting or presentation logic.
The primary risk is creating implicit coupling and a phenomenon known as 'module soup' or 'mixin hell.' This happens when a model includes many concerns, making it difficult to trace where an individual method or callback is actually defined. This violates transparency and can lead to unexpected side effects when refactoring.
A Rails Concern is a Ruby mixin (a module that is included into a class), but with extra features provided by the Rails framework. The ActiveSupport::Concern module adds the necessary boilerplate to handle things like adding class methods via the module ClassMethods block, making it the preferred, standardized way to create mixins in Rails.