Rails Avoid enums

Avoid using enums in ActiveRecord. While they seem like a great idea at first, they can cause problems down the road.

class Document < ActiveRecord::Base
  # ✗ Avoid this!
  enum status: [:active, :archived]
end

Leaky abstraction

Enums are a way to use numbers instead of strings (eg, 1 instead of 'active'), but this abstraction can get leaky. In the example above, you can easily use :active and :archived as you would use any other value. Rails abstracts away the conversion of them into integers.

Document.where(status: :archived)
# This actually does `Document.where(status: 1)`

However, there are cases where this doesn't apply. For instance:

# ✗ Throws an error
Document.where("status = ?", "archived")

# ✗ Throws an error
Comment.joins(:document).where({ document: { status: :archived }})

# It should instead be written like this:
archived = Document.statuses[:archived]
Comment.joins(:document).where({ document: { status: archived }})

Insertion can get tricky

If you must use Enums, always assign explicit values to them. Consider the example above: if we were to add another status, we may be tempted to do it like so:

 class Document < ActiveRecord::Base
-  enum status: [:active, :archived]
+  enum status: [:pending, :active, :archived]
 end

This, however, will break all existing Document records. The new :pending status will be assigned a value of 0, which was the previous value of :active. With this, all previously active documents will now be :pending, and all previously archived documents will be :active.

Resources

Here are some more arguments against Enums:

Other resources: