Note: This is one part of my journey to tame a spaghetti model or god object. Start with Post 1: Unraveling a Spaghetti Model to see how I chose to tackle this problem.

Your spaghetti model has been a dumping ground of methods and associations for years. In our case, it’s the company model. We know we need to add functionality to the system, so we place the new capability on the company because it’s convenient. We may rationalize it with “Besides, the company should know whether it has active medical benefits.”


To deal with models with way too many methods, Rails 4 introduced ActiveSupport Concerns as a way of partitioning and reusing model code. Methods and associations that worked together could be extracted into a Concern.

While this made the code more manageable in the short term, each spaghetti model still had the same number of methods. Further, these concerns may or may not be good abstractions. Instead, we’d like to move domain logic into domain APIs.

An Example

Here is an example ActiveSupport::Concern, the company model now has the method company.origination_bank , the association company.payer_origination_banks, and a lifecycle callback setup_banking! that is called each time the company is saved.


  1. List all of the active support concerns and identify which contain domain logic that should be moved to other packs.
Concern Decision # Models
Sluggable Stay 1
Bank Accountable Move to domains 1
Analytics:Identifyable Unused so deleted 2
PaymentsAndFilingsReschedulable Move to payments 15
RiskAssessable Move to risk 1
BankAccountable Move to payments 2
AttrSquishable Stay 14
CompanyFormsObserver Move to forms 1
CompanyOnboardingFunnelUpdateable Move to onboarding 5
ZensaObserver Stay 36
AttrBoolean Stay 27
DirtyNestedAttributes Stay 14
AttrMemoized Stay 49
ExternalIdentities::IdentityDeletable Stay 4

2. Start with active support concerns that are used by 1 model.


Our goal is to move or remove all the methods, associations, and lifecycle callbacks in the ActiveSupport::Concern.


  1. For each method in the ActiveSupport::Concern, identify where it should live and then follow the techniques in

2. Move active model lifecycle methods to the model. You can remove the concern now and deal with lifecycle methods later.

3. When all that is left is an empty concern, let’s celebrate! 🎉

It’s time to say goodbye. 👋


Tackling an ActiveSupport::Concern is the same as handling methods, associations, and lifecycle callbacks.

Originally published at on September 27, 2023.