Often necessity to use deep copy (in any language) signals bad design: when you don’t know for sure that some code to which you pass that object will not mutate it and its deep sub-parts. That’s overly defensive programming. I rarely encounter even shallow copy in practice (almost always it’s right before mutating such objects) and almost never encounter need for deep copy.
I’m not an FP zealot, but I work with Elixir these days, and “deep copy” is an alien concept there. If you add an item to a list, you get a new copy of the list with the new item added. It’s impossible to affect any other bit of code that had the old list.
It’s nice not to have to think about that.
I agree it’s bad practice. My moment of enlightenment was realizing the problem is isomorphic to serializing/deserializing an object graph. Example if you have machinery to completely serialize the state of an object (or an object graph), just use that for a “deep copy” ie deserialize multiple times.
I don’t agree that is bad design. Let’s say that you have a method that updates an Entity and at the end, you emit an event with the new Entity and the old one. One solution is to fetch the entity from database clone it to a $previousEntity variable and use the $entity to perform the replaces you need and then emit the event. However does not mean you should have mutable VOs but from my experience at every place I have been there are always exceptions for certain reason and it can be very easy to start having bugs and very difficult to understand where they are coming from ;)