Callbacks
Description
ActiveRecord callbacks like before_destroy
or after_create
can be used to add behavior that automatically runs during the lifecycle of a model instance. Callbacks are somewhat controversial in the Rails community. Depending on the nature of the behavior, they can make code harder to understand and surprising, especially when used excessively.
Technology Status at Wunder
Assess. Callbacks are often the simplest solution, but overuse can be problematic.
Notes/Resources
Callbacks are intended for database activity. E.g., the code in after_create
will run inside the transaction, but before it’s committed to the database. If some other piece of data needs to be updated simultaneously, this can be the best time to do it.
- Operations should be limited to database operations.
- Avoid using callbacks for business logic that isn’t database related, e.g. sending an email, or hitting an external API.
- Callbacks should be used when the behavior should always be run.
- If the logic is instead placed in an actor, it can be difficult to ensure that every code path always uses the actor. If not using the actor would result in an invalid database state, using a callback can be more foolproof.
- Note that most
*_all
methods do not run callbacks.
- Avoid actors that only replicate ActiveRecord functionality.
- E.g.,
Destroy
actors that replicate thedependent: :destroy
association behavior. Doing this ejects the model from the standard ActiveRecord lifecycle, making it only safe to destroy the model with that actor. That means that any other models can’t usedependent: :destroy
on that association, and so on up the chain. It also creates two ways to destroy the model, and if only one of them is valid, it can lead to inconsistencies in the database. - Actors are for business logic, not to duplicate ActiveRecord functionality.
- E.g.,
- To avoid cross-segment callbacks, one option is to have the segment register its own callbacks via a concern. That can solve the encapsulation problem, where the other segment is responsible for its own behavior, and the model including the concern is unaware of it.