Rails offers several ways to remove records. They differ in whether they instantiate records, fire callbacks (including dependent: associations) and how they manage relation state afterward.
destroy_all
This is the definition of destroy_all:
# ActiveRecord::Relation
def destroy_all
records.each(&:destroy).tap { reset }
end
-
recordsevaluates SQL, caches result -
.each(&:destroy)iterates the cached Array, returns it -
.tap { reset }callsRelation#reset, clearsrecordsandloaded, returns the Array unchanged
each(&:destroy) vs destroy_all
Both methods snapshot upfront, but the other does not reset the cache.
relation = Post.where(archived: true)
relation.destroy_all
relation.loaded? # => false (reset clears cache)
relation.count # forces a new query
relation.each(&:destroy)
relation.loaded? # => true (stale destroyed objects remain cached)
relation.count # does not query again
Irrelevant when the scope is called fresh each time (cleanup jobs), matters if reusing the relation variable.
destroy vs delete
dependent: options are registered as before_destroy callbacks. They only fire through destroy as it actually instantiates the records, never through delete as it's a pure SQL deletion.
delete_all deletes the records in bulk and also does not instantiate records nor does it run callbacks.
Example:
class Post < ApplicationRecord
has_many :comments, dependent: :destroy # before_destroy callback
end
post.destroy # deletes post AND all comments (callback fires)
post.delete # comments orphaned
Post.delete_all # bulk SQL deletion, all children of posts orphaned
Posted by Felix Eschey to makandra dev (2026-04-09 07:37)