Why callbacks are bad in Rails models

ActiveRecord callbacks are a common feature in Ruby on Rails for triggering actions before or after changes are made to database records. However, callbacks can be problematic for several reasons:

Complexity

Callbacks often result in tightly coupled code, making it difficult to maintain and understand the flow of an application.

Performance

Callbacks are often executed in the same transaction as database operations, which can slow down database performance.

Debugging

Debugging issues related to callbacks can be complex because it can be challenging to trace the flow of execution and understand the interactions between different parts of the application.
So how to tackle that? Let's see:

class User < ApplicationRecord
  before_save :capitalize_letter, on: :create
  after_save :send_welcome_email, on: :create

  def capitalize_name
    name.capitalize!
  end

  def send_welcome_email
    Users::Commands::SendEmail.call(self.id)
  end
end
Typical fat models example

We have some sort of operation in before saving to convert the name to an uppercase string. Then, when we have kept the data to the database, we want to send a welcome email. So how can we automate this and make it much cleaner? - We could make it a process!

module Users
  module Processes
    class RegisterNewUser
      def self.call(**attrs)
        user = Commands::Create.call(**attrs) # do all transitions here
        ::Users::Commands::SendEmail.call(user.id)
        user
      end
    end
  end
end
Step by step process of doing complicated logic

In the process example, we see line-by-line actions, which is more readable than catching callbacks in the code. It's also easier to debug and call the class elsewhere - because it's a standard Ruby class.

Summary

In conclusion, while ActiveRecord callbacks may seem convenient, but they can result in complex, slow, and difficult to debug code. Developers should consider alternative approaches such as service objects, processes, commands, or other plain old Ruby objects to manage their complex operations. Use callbacks only when it really makes your job easier overall - not because it's faster to implement.

Happy coding!