Thankfully we now have frozen string literals so that takes some of that class of pain away…
But, yes, I love freeze.
I always put a freeze at the end of every constructor….
Sometimes, later, I remove it, but surprisingly seldom.
It improves the quality of my code and uncovers my stupid early.
You can also freeze constant Strings to minimize GC pressure. I blogged about using a magic comment recently:
Sweet baby Jesus, string literals are mutable? This is why we can’t have nice things.
TIL! Thanks for sharing 👍
Yes, absolutely, freeze everything you can in Ruby. Just like being extremely liberal with final in Java is a good thing.
Freezing shared constants is important, but I would push back against liberally sprinkling lots of Ruby code with freeze. Ruby is idiomatically a language that embraces mutability. It’s fighting an uphill, losing battle and YAGNI.
EDIT: I have come around to this perspective after having been a proponent of final being default behavior in Java. My opinion around this isn’t too strongly held, but more a recognition of the friction of going against the grain with that approach. I do value immutability.
Oh, I have a fairly low bar for removing freeze from my constructors….
….but then I know and understand why and when I’m mutating this object, I’m not guessing.
I doubt accidental mutation of such things is a frequent problem. Yes, concat mentioned in article is misleading method which mutates receiver instead of returning concatenated list like Array#+. But usually it’s quite clear when mutation occurs.
User-land code (in Ruby) is the wild west of mutation. I’d rather not take my chances, especially in very large monolithic code bases.
I especially use freeze when I’m exploring somebody elses code.
ie. The mutation is probably not accidental, but I didn’t know about it, or when it would occur.
Conversely if adding a freeze doesn’t break anything…. then I also have some valuable hard data about the code base.
Of course, these requires a fairly good degree of test coverage.