Yes, of course you can do horrific things with it. But the ruby community has largely been absolutely stellar about not doing that - most keep insanities tightly bounded, where they can be used to make pleasant and error-resistant APIs. And even when they're not (which is usually self-inflicted by internal code), the language is flexible enough to let you unfuck just about any fuckery that you run across, if needed.
But beyond that though, the vast majority of issues I've seen with monkey patching has been stuff that was created in the project/company that's experiencing the problems. Libraries are generally very good about how they monkeypatch (because doing it wrong with hundreds of using-projects VERY quickly runs into problems), but those ad-hoc internal monkeypatches are routinely done by people who don't fully understand what they're doing, or take shortcuts. Those can have latent bugs linger for a very long time, and yeah - they can be nasty to unravel.
Even this is becoming less and less necessary as more of the commonly-used syntactic sugar gets pulled into Ruby core.
I don't understand how monkey patching is beneficial beyond personal preference.
The way Ruby does metaprogramming makes debugging extremely frustrating because your stacktraces will contain references to methods that exist nowhere. Good luck then trying to find where they're defined so you can understand how they work.
I have a very strong opinion that generating methods at runtime is a terrible feature that should be used only when nothing else can be done.
At the top of any file:
def Weeks(arg) end
static TimeSpan Weeks(this System.Int32 n)
{
return new TimeSpan(n*7, 0, 0, 0);
}
static DateTime FromNow(this TimeSpan dt)
{
return DateTime.Now.Add(dt);
}
static void Main(string[] args)
{
Console.WriteLine(2.Weeks().FromNow());
}That's negligible.
The one I consider the most annoying: it substantially increases load time. ActiveSupport is huge and just requiring it increases the load time by 1.x seconds. Given what it provides, that's substantial.
The inability to extract a library (or a service) without depending on ActiveSupport is a cost that doesn't exist with the other approaches.
Not to underestimate, your _number_ now responds to `weeks`, and duck typing is the only way you have "interfaces" in Ruby. I can easily picture having a "weeks" or days attribute on some entity and a service object expecting that, now there are subtle bugs when passing accidentally the entity's _value_ instead of the entity itself.
Yeah, they are "minor" things, but the point is dying of thousands cuts.
The last damage is more subtle: devs, even less experienced devs, will feel authorized to monkey patch. In the private codebases I saw (multiple) this is common. It's not a last resort anymore and it ends up becoming a hard problem to debug and resolve.
The ability to monkeypatche pushes devs to NOT design their objects in an extensible fashion. This effectively is the highest damaging consequence, but it's incredibly hard to assess.
I'm not sure what you mean; Rails depends on ActiveSupport so if you're a Rails codebase you don't really care, you already depend on it. If you're a gem author it's your own decision if you want to depend on ActiveSupport or not, I'm sure many gems don't depend on it, and others do.
> Not to underestimate, your _number_ now responds to `weeks`, and duck typing is the only way you have "interfaces" in Ruby. I can easily picture having a "weeks" or days attribute on some entity and a service object expecting that, now there are subtle bugs when passing accidentally the entity's _value_ instead of the entity itself.
Never seen this. Not saying it can't happen (can u give an example though? not 100% sure what you mean) but I've never seen it.
> Yeah, they are "minor" things, but the point is dying of thousands cuts.
This is a bit of a hyperbole statement wouldn't you agree?
> The last damage is more subtle: devs, even less experienced devs, will feel authorized to monkey patch.
I think it happens way less than you think; usually in teams of inexperienced people who discover Ruby and metaprogramming for the first time. But the worst monkeypatching bug I've ever seen was actually done in javascript; a guy monkeypatched some jquery method and broke 20000 websites at once (we were a widget startup). Monkeypatching isn't a Ruby thing it's a dynamic languages thing. I don't see Ruby/Rails culture just encouraging people to monkey patch String or Object. The fact dhh and a very experienced team of devs did it, and tested it and perfected it for years, doesn't mean you should do it. This is pretty common knowledge now and I think 99% of Rubyists would agree.
> This is a bit of a hyperbole statement wouldn't you agree?
I'm not sure, I see many small problems that slowly leads to what the Ruby community is now. That being said, if you look at how many things ActiveSupport monkeypatches, "thousands cuts" it's not an understatement: https://github.com/rails/rails/tree/main/activesupport/lib/a...
That's where the monkey patching comes in, usually.
But to be clear, I'm stating the facts that many software developer see in the Ruby world when you join an organization that's growing tremendously. As usual, if you fit perfectly in the Rails target and the Devise target, you won't encounter problems.
The load time of a single test is a recurring problem in the Rails world though, so ActiveSupport adds up.
Monekypatching doesn't lead to crash (it can, but that's not the point), it leads to: - Less extendable code - Less learning - Less maintainable code
Even if the damage is small, given how competitive is the world, why taking the chance for no benefit?
Well that's the crux of it here, you see it as damage/no benefit while others in the Rails world disagree. It's a difference of opinions that won't be resolved tonight by arguing on Hacker News. I did like your comment about naming consistency in Ruby though, learned something new.
Why is this bad? Makes total sense to me. Now both forms work and hopefully it eventually makes it into the language like often happens. But until it does, why not monkey patch obvious mistakes?
Array has `include?`, Regexp has `match?` and String has `match?` and `start_with?`.
This is consistent: there is no S.
Now when I'm in a codebase, depeneding on ActiveSupport and which parts of it are included, the consistency is now missing. Is it with S or without S?
And what methods DO have the S version and which one do not?
Actually this is kind of the whole point of #present? - it's not just a truthiness check.
The only falsey values in Ruby are `false` and `nil`, but in the context of a Rails app you might want to treat things like "" and [] as "not present" (e.g., a user leaves a text field blank).