“I monkey patch from time to time.”
medium.com
medium.com
Small example: You're using a 3rd party library that happens to include layers of object inheritance. You notice a piece of functionality a couple of layers up that you need access to or actually requires correction, but wasn't exposed in a way that you can fix it. With monkey patching, you can add a couple of lines of code in your code base that corrects the library without any changes elsewhere. Then you add a test to monitor it to see if something ever changes.
Without monkey patching, you have to fork the entire 3rd party library, make the change, use your fork on your project, keep it up to date and hope the library owner eventually accepts your PR. OR use the code as is and rebuild the inheritance tree in your own code base everywhere it's impacted.
It's indispensable when this scenario comes up.
A long time ago, when Django was less mature we had a monkey patch to add a join condition to some of our queries. It obviously broke when we needed to upgrade to Django 2, but it did allow us to eek out a major performance boost without abandoning the ORM quest builder. One of my coworkers practically fell over laughing when he discovered the file.
And because you’re in control and the upstream has no idea you’re doing what you’re doing, breakage will fall on you anyway v
1. Monkey patch it to be compatible
2. Include in the monkey-patch initializer a version check, so the monkey-patch won't outlive the current version of the dependency
3. Submit a pull-request or open an issue upstream to resolve the compatibility issue.
This comes up _constantly_ during ruby version updates and when updating central libraries that many others depend on (like activesupport).
The other alternative is usually to fork the gem yourself and resolve the compatibility issues, and depend on your forked version - then you need to stay on top of updating and building your fork regularly. Multiply that by a dozen or so different cases, and it becomes clear that monkey-patching is in some cases _the right thing to do_, and not a "bad habit" at all.
I understand this was pretty common in the early days of Ruby, around 1.8.7, 1.9.3, 2.0, etc, along with Rails 2.x and earlier. I maintain a number of Rails apps, both at work and personal, and I've never had to do this or heard of anyone else doing it post roughly Rails 4.x and Ruby 2.2+.
In particular, the change during ruby 3.0 to the handling of keyword arguments vs final hash params broke a _lot_ of gems (like memoist and ruby-kafka's datadog integration), that wrap arbitrary methods, because when they wrap keyword-argument methods, they passed the arguments into the wrapped method as hash-args instead.
I expect that the longer you wait after a release to upgrade, the less it's an issue - the ecosystem takes time to catch up. We've been staying fairly on top of the upgrades.
It's worth noting that the reason there aren't a ton of compatibility issues 9 months after those things are released is _because_ of shops that perform the upgrades, find the issues, and report or solve them before then. The later you choose to go in the cycle, the less you'll be impacted, and the less you will contribute.
I gotta say I'm sick of advertisements masquerading as blog posts. It is like tech bro astro-turfing, and frankly insufferable to see the owner of a SaaS/package/product describe their own stuff as "a gem". Once I see past the worm and see the hook, I just stop reading. I wish these blog posts would be more upfront about their bias rather than coming across as a friendly coffee chat that turns out to be a quite biased sales pitch.
Apologies for the rant, and again apologies to the OP for catching a stray bullet here as your product is interesting. I just find this method of advertising distasteful.
It literally is a gem. In Ruby, distributable packages are called gems.
You can see their gem at https://rubygems.org/gems/overrides_tracker
Then friend you are on the wrong website.
LD_PRELOAD=/path/monkey-patch.so your-binary args...
In other words, even C can be like Ruby in this aspect, just more cumbersome. You need to write a C function with the same interface and compile a dynamic library. Doable but tedious.I do that when I want to reverse. I patch-wrap the function with conditional logging and subtle manipulation. It's more flexible than just tracing.
Not so easy to do with compiled and linked software, and a great way to get thrown out of an app store, if you do it for iOS.
I think we don't need this kind of runtime monkey patching as soon as the packaging system is simpler, which allows easier to fork that package.