Why using default_scope is a bad idea

default_scope is a method provided by ActiveRecord, which allows you to set a default scope (as its name implies) for all operations done on a given model. It can be useful for allowing soft-deletion in your models, by having a deleted_on column on your model and setting the default scope to deleted_on: nil

class Animal
  default_scope where(deleted_on: nil)
end

This will hide deleted records and only return non-deleted ones in your queries after setting deleted_on instead of calling destroy.

> Animal.limit(5)
  Animal Load (4.2ms)  SELECT `animals`.* FROM `animals` WHERE `animals`.`deleted_on` IS NULL LIMIT 5

Quite useful! However, default_scope has a dangerous behavior: it affects your model’s initialization.

Say you were using STI on your Animal model, and you had a default_scope for always filtering your model by “Cat”:

class Animal
  default_scope where(type: "Cat")
end

When you initialize a new Animal, it will always use the value defined in your default scope:

> Animal.new
=> #<Animal id: nil, created_at: nil, updated_at: nil, type: "Cat">

This can lead to some headaches when you’re not aware of this side-effect, and depending on the default scoped attribute/value it can cause some pretty bad bugs.

Also, I’d like to mention the fact that it’s very difficult to write queries once you have used default_scope on a model. For example: Animal.where('deleted_on is not null') won’t work, you’d need to use Animal.unscoped, which makes everything awkward. You also need to use unscoped carefully, because it will remove all scopes of the relation, not just the default scope.

I recommend that you always avoid default scope if possible. Prefer explicit scopes instead. If you really need to use it, use it with care.