Object References and Copies
Objects on the canvas are passed by reference. Updating one can change what another block already read. This page shows when that happens and how to take a copy.
An object pin does not carry a snapshot of the object. It carries the object itself. Two blocks connected to the same object out-pin are looking at the same thing in memory, so a change made through one of them is visible to the other, including to blocks that already ran.
That is what makes flows cheap: a document of a few hundred fields is passed around without being copied on every hop. It is also the source of a specific class of bug, where a value changes underneath a block that looked at it earlier.
Which blocks change the original
| Block | What it does with the input |
|---|---|
object_update_fields |
Applies the mutations and field pins to the object you connected, then outputs that same object. The original changes. |
object_new_fields |
Builds a fresh object from its in-pins. Nothing you connected is modified. |
object_shallow_copy |
Builds a new object with the same top-level keys. Nested objects and arrays are still shared with the original. |
object_deep_copy |
Builds a new object with no shared references at any depth. The safest, and the most expensive. |
Shallow copy versus deep copy
A shallow copy protects the top level only. Take a customer object with a nested address: after a shallow copy, setting status on the copy leaves the original alone, but setting address.city changes the original as well, because both objects point at the same nested address.
A deep copy rebuilds every level, so nothing is shared. It costs time and memory proportional to the size of the object, which is why the block's own description warns against widespread use. Reach for it when you genuinely need an independent snapshot, for example when you keep a "before" version of a document to compare against or to log.
original = {
"name": "joe doe",
"address": { "city": "Amsterdam" }
}
copy = shallow_copy(original)
copy.name = "sami b" // original.name is still "joe doe"
copy.address.city = "Utrecht" // original.address.city is now "Utrecht" as well
copy = deep_copy(original)
copy.address.city = "Utrecht" // original.address.city stays "Amsterdam"
Where this bites in practice
- Keeping a before and after. Reading a document, updating it in place, and then trying to log "what it was" gives you the new values twice. Deep-copy the document before the update if you want the old state.
- Iterations. Updating the object of the current item inside a loop mutates the entry in the array you are iterating over. That is fine when it is what you meant, and confusing when it is not.
- Reusing a template object. Building one base object and updating it per iteration leaves every reference pointing at the final state. Build a new object per iteration, or copy the template inside the loop.
- Passing to a function. A custom function that updates the object it received changes the caller's object too. Copy at the boundary if the function is meant to be a pure transformation.
Storage documents follow the same rule
A document read from storage is an ordinary object on the canvas, so everything above applies to it. Writing to it does not write to storage: persisting still goes through mutations and function_update_document, described in Storages. What in-place updates do change is what the rest of your flow sees.
Next steps
Locking and Concurrency The other half of the story: what happens when two flows touch the same data at the same time. Block Execution The order blocks run in, and when a value is resolved. Common pitfals The other mistakes that cost an afternoon.Frequently asked
Does updating an object in RUAL change the original?
It depends on the block. object_update_fields applies its changes to the object you connected and outputs that same object, so the original changes. object_new_fields builds a new object and leaves the input untouched.
What is the difference between a shallow copy and a deep copy in RUAL?
A shallow copy creates a new object with the same top-level keys, but nested objects and arrays are still shared with the original. A deep copy rebuilds every level, so nothing is shared. Deep copies cost time and memory proportional to the object size.
How do I keep the old version of a document before updating it in RUAL?
Take a deep copy before you apply the update. Updating in place changes the object every other block is holding, so without a copy the before and after values are the same.
