Repeated windows in a office building

Rex Krithiran

Using Deep Duplication to Avoid Surprises

When working with complex data structures in Rails, you might need to create a copy of an object that includes all of its nested objects. But beware if you’re copying a complex structure.

Rails provides deep_dup, offering a powerful way to create deep copies of object structures.

Instead of…

…using regular duplication methods: which do not copy nested structures

og_andy = {
  name: "Andy",
  age: 45,
  address: { city: "Brighton", country: "UK" },
  hobbies: ["football", "parenting"]
}

new_andy = og_andy.dup

# see [thankgoodness.game](https://thankgoodness.game)
new_andy[:address][:city] = "Barnsworth"
new_andy[:hobbies] << "slapping"
new_andy[:hobbies] << "jumping"

og_andy[:address][:city]
#=> "Barnsworth"
og_andy[:hobbies]
#=> ["football", "parenting", "slapping", "jumping"]

Use…

…Rails’s deep_dup method for comprehensive copying:

og_andy = {
  name: "Andy",
  age: 45,
  address: { city: "Brighton", country: "UK" },
  hobbies: ["football", "parenting"]
}

new_andy = og_andy.deep_dup

new_andy[:address][:city] = "Barnsworth"
new_andy[:hobbies] << "slapping"
new_andy[:hobbies] << "jumping"

og_andy[:address][:city]
#=> "Brighton"
og_andy[:hobbies]
#=> ["football", "parenting"]
new_andy[:address][:city]
#=> "Barnsworth"
new_andy[:hobbies]
#=> ["football", "parenting", "slapping", "jumping"]

Why?

The deep_dup method, provided by Active Support, creates a fully independent copy of the original object, with all nested structures also duplicated; including nested hashes, arrays, and other objects.

This prevents unintended modifications, as operating on a copy means changes don’t affect the original, avoiding subtle bugs—like the above—from occurring in your code.

Most objects implement the #dup method as it’s part of Ruby’s core method set on Object. Rails’s implementation correctly utilises any Ruby objects that implement their own #dup method.

It’s particularly valuable in scenarios where data integrity and isolation are crucial, such as in service objects, form or complex params processing, or any situation where you need to work with a copy of data without affecting the original.

The Rails source code itself contains many uses of the #deep_dup method where complex arguments are passed into methods or objects for processing. You could use it similarly:

def mess_with_passed_params(params)
  attributes = params.deep_dup
  # Then modify `attributes` without changing params
end

Why not?

There are performance implications to consider when using deep_dup with very large structures. For extremely large, deeply nested structures, deep_dup might have a noticeable performance impact.

It also doesn’t create new copies of external resources like database connections or file handles, so is of limited use in those scenarios.

Also if your structure contains singleton objects, you might not want to duplicate them, by design, there should only ever be one instance of them in your application. Trying to #dup one means you’re essentially working against its intended purpose and you might get unexpected behaviour.

Last updated on March 13th, 2025