Rails 8 adds comparable option to serialized attributes

Serialized attributes in Rails have always had a subtle problem: changes in how data is serialized can trigger database updates even when the actual data hasn’t changed. Rails 8 introduces the comparable option to solve this issue.

The Problem

When using serialized attributes, the same data can have different string representations:

1
2
3
4
5
6
7
8
9
10
11
12
class User < ApplicationRecord
  serialize :preferences, coder: JSON
end

user = User.create!(preferences: { theme: "dark", notifications: true })

# Later, the preferences hash might be reordered
user.preferences # => {"notifications" => true, "theme" => "dark"}

# Even though the data is the same, Rails thinks it changed
user.changed? # => true
user.save! # Unnecessary database write!

This problem becomes worse when:

  • JSON libraries change their serialization behavior
  • Hash keys get reordered (Ruby doesn’t guarantee hash ordering across versions)
  • Float precision changes slightly
  • Whitespace or formatting differs

Rails 8 Solution

Rails 8 adds a comparable option that compares the deserialized objects instead of their serialized strings:

1
2
3
4
5
6
7
8
9
10
11
12
class User < ApplicationRecord
  serialize :preferences, coder: JSON, comparable: true
end

user = User.create!(preferences: { theme: "dark", notifications: true })

# Even if the serialized form changes
user.preferences = { "notifications" => true, "theme" => "dark" }

# Rails now knows the data is the same
user.changed? # => false
user.save! # No database write!

Custom Serializers

The comparable option works with custom serializers too:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class CompressedJSON
  def self.dump(obj)
    Zlib::Deflate.deflate(obj.to_json)
  end
  
  def self.load(data)
    return {} if data.nil?
    JSON.parse(Zlib::Inflate.inflate(data))
  end
end

class Archive < ApplicationRecord
  serialize :data, coder: CompressedJSON, comparable: true
end

When to Use comparable: true

Use it when:

  • Data comes from external APIs that might reorder fields
  • Using JSON/YAML serialization where formatting can vary
  • Storing configuration or settings that rarely change
  • Working with legacy data that might have inconsistent serialization

Don’t use it when:

  • The serialized data includes timestamps or unique identifiers that should trigger updates
  • You need to track any change in serialization format
  • Using custom serializers with complex comparison logic

Conclusion

The comparable option is a small but impactful addition that prevents phantom updates in Rails applications. It’s especially valuable for applications that sync data with external sources or deal with serialized configurations.

References

Prateek Choudhary
Prateek Choudhary
Technology Leader