Is "Monkey Patching" Really That Bad

Is monkey patching really that bad?

Wikipedia has a short summary of the pitfalls of monkey-patching:

http://en.wikipedia.org/wiki/Monkey_patch#Pitfalls

There's a time and place for everything, also for monkey-patching. Experienced developers have many techniques up their sleeves and learn when to use them. It's seldom a technique per se that's "evil", just inconsiderate use of it.

What is monkey patching?

No, it's not like any of those things. It's simply the dynamic replacement of attributes at runtime.

For instance, consider a class that has a method get_data. This method does an external lookup (on a database or web API, for example), and various other methods in the class call it. However, in a unit test, you don't want to depend on the external data source - so you dynamically replace the get_data method with a stub that returns some fixed data.

Because Python classes are mutable, and methods are just attributes of the class, you can do this as much as you like - and, in fact, you can even replace classes and functions in a module in exactly the same way.

But, as a commenter pointed out, use caution when monkeypatching:

  1. If anything else besides your test logic calls get_data as well, it will also call your monkey-patched replacement rather than the original -- which can be good or bad. Just beware.

  2. If some variable or attribute exists that also points to the get_data function by the time you replace it, this alias will not change its meaning and will continue to point to the original get_data. (Why? Python just rebinds the name get_data in your class to some other function object; other name bindings are not impacted at all.)

Is a bad practice to monkey patch a base ruby class?

Monkey patching isn't considered to be a bad practice unless you are writing odd methods that do not have PatchedClass-related behavior (for example, String.monkeyPatchForMakingJpegFromString is rather bad, but Jpeg.fromString is good enough.)

But if your project is rather large, the libraries that you use in it may happen to have colliding patches, so you may have one more problem with all these patching stuffs. In Ruby 2.0, refinements come to an aid. They work as follows: you define a module, refine your (even core) class in it, and then use that module where it's necessary. So, in your code it works as:

YourClass.new.refinedMethodFromCoreClass #=> some result

But

CoreClass.refinedMethodFromCoreClass

produces undefined method exception.

That's all monkey patching stuff: monkey patching is useful and convenient, but refinements add some features, that make your code more secure, maintainable and neat.

Monkey-patching Vs. S.O.L.I.D. principles?

There's a difference between monkey-patching (overwriting or modifying pre-existing methods) and simple addition of new methods. I think the latter is perfectly fine, and the former should be looked at suspiciously, but I'm still in favour of keeping it.

I've encountered quite a few those problems where a third party extension monkeypatches the core libraries and breaks things, and they really do suck. Unfortunately, they all invariably seem stem from the the third party extension developers taking the path of least resistance, rather than thinking about how to actually build their solutions properly.

This sucks, but it's no more the fault of monkey patching than it's the fault of knife makers that people sometimes cut themselves.

The only times I've ever seen legitimate need for monkey patching is to work around bugs in third party or core libraries. For this alone, it's priceless, and I really would be disappointed if they removed the ability to do it.

Timeline of a bug in a C# program we had:

  1. Read strange bug reports and trace problem to a minor bug in a CLR library.
  2. Invest days coming up with a workaround involving catching exceptions in strange places and lots of hacks which compromises the code a lot
  3. Spend days extricating hacky workaround when Microsoft release a service pack

Timeline of a bug in a rails program we had:

  1. Read strange bug reports and trace problem to a minor bug in a ruby standard library
  2. Spend 15 minutes performing minor monkey-patch to remove bug from ruby library, and place guards around it to trip if it's run on the wrong version of ruby.
  3. Carry on with normal coding.
  4. Simply delete monkeypatch later when next version of ruby is released.

The bugfixing process looks similar, except with monkeypatching, it's a 15 minute solution, and a 5-second 'extraction' whereas without it, pain and suffering ensues.

PS: The following example is "technically" monkeypatching, but is it "morally" monkeypatching? I'm not changing any behaviour - this is more or less just doing AOP in ruby...

class SomeClass
alias original_dostuff dostuff
def dostuff
# extra stuff, eg logging, opening a transaction, etc
original_dostuff
end
end

To monkey-patch or not to?

Don't!

Especially with free software, you have all the possibilities out there to get your changes into the main distribution. But if you have a weakly documented hack in your local copy you'll never be able to ship the product and upgrading to the next version of curses (security updates anyone) will be very high cost.

See this answer for a glimpse into what is possible on foreign code bases. The linked screencast is really worth a watch. Suddenly a dirty hack turns into a valuable contribution.

If you really cannot get the patch upstream for whatever reason, at least create a local (git) repo to track upstream and have your changes in a separate branch.

Recently I've come across a point where I have to accept monkey-patching as last resort: Puppet is a "run-everywhere" piece of ruby code. Since the agent has to run on - potentially certified - systems, it cannot require a specific ruby version. Some of those have bugs that can be worked around by monkey-patching select methods in the runtime. These patches are version-specific, contained, and the target is frozen. I see no other alternative there.

Is it a bad idea to monkey patch Object to have a more natural way for testing if an element is in an array?

Ignoring the general arguments for/against monkey patching in Ruby, the code you describe is an example of a convenience method to help with your readability. Arguments against its inclusion also apply to the generic case as well, so there doesn't appear to be a specific drawback. Technically, this method is already included in the Ruby on Rails framework, so the authors shared your view in supporting it as a natural expression.

What does 'Monkey Patching' exactly Mean in Ruby?

The short answer is that there is no "exact" meaning, because it's a novel term, and different folks use it differently. That much at least can be discerned from the Wikipedia article. There are some who insist that it only applies to "runtime" code (built-in classes, I suppose) while some would use it to refer to the run-time modification of any class.

Personally, I prefer the more inclusive definition. After all, if we were to use the term for modification of built-in classes only, how would we refer to the run-time modification of all the other classes? The important thing to me is that there's a difference between the source code and the actual running class.

In Ruby, the term monkey patch was
misunderstood to mean any dynamic
modification to a class and is often
used as a synonym for dynamically
modifying any class at runtime.

The above statement asserts that the Ruby usage is incorrect - but terms evolve, and that's not always a bad thing.

Monkey Patching in python: When we need it?

Monkey-patching is a way to make some global under-the-hood change in a way that existing code will continue to run, but with modified behavior.

A really really trivial example of changing the behavior of the builtin str command:

b.py

def foo(msg):
s = str(msg)
print s, type(s)

a.py

import b

b.foo('foo')

# monkey-patch
import __builtin__
__builtin__.str = unicode

b.foo('foo')

# Results:
#foo <type 'str'>
#foo <type 'unicode'>

The a module has modified the behavior of other code using the str command, by patching it to use unicode instead. This would be necessary since we pretend that we have no access to b.py's code. It could have been a huge package that we simply use and can't change. But we can slip in new code to be called that changes the behavior.

A real world example from gevent

>>> import gevent
>>> from gevent import socket
>>> urls = ['www.google.com', 'www.example.com', 'www.python.org']
>>> jobs = [gevent.spawn(socket.gethostbyname, url) for url in urls]
>>> gevent.joinall(jobs, timeout=2)
>>> [job.value for job in jobs]
['74.125.79.106', '208.77.188.166', '82.94.164.162']

The example above used gevent.socket for socket operations. If the
standard socket module was used it would took it 3 times longer to
complete because the DNS requests would be sequential. Using the
standard socket module inside greenlets makes gevent rather pointless,
so what about module and packages that are built on top of socket?

That’s what monkey patching for. The functions in gevent.monkey
carefully replace functions and classes in the standard socket module
with their cooperative counterparts. That way even the modules that
are unaware of gevent can benefit from running in multi-greenlet
environment.

>>> from gevent import monkey; monkey.patch_socket()
>>> import urllib2 # it's usable from multiple greenlets now

Is it possible to do monkey patching in Java, if not is there an alternative?

There are a number of techniques that might be applicable here, but your question is too vague to narrow them down to a single answer.

"Monkey patching" in the literal sense that it is used in Ruby (i.e. "replace methods of a class at runtime", see e.g. [1]) is possible with "Java Agents" and the "retransform" API, but it's much harder than it is in Ruby.

Source code patching

I need it for a java application that I applied my own patch to

If there is an app for which you have the source code, say in git, then you could fork their project, apply your own patch and build a modified version.

I want to be able to keep apply any patch I make, to the patches they make.

If you create your patch on a branch then it will be easy with git to pull in any future changes from the "upstream" project into your branch, and build a new modified version.

Replacing classes at class-load time via classpath precedence

An easy technique that more closely approximates Monkey Patching is to compile a single class from the target app, with modifications, and put it earlier on your classpath than the original JAR. (This is covered in the old Monkey Patching q at this answer: https://stackoverflow.com/a/381240/8261 )

The JVM loads all classes by name, and will use the first classfile it finds on the classpath for any class, so you can replace the classes one-by-one from the project you wish to modify. If you have the source for the target project, then copy that into your app file-by-file and then apply your patches to the java source.

(You will need to manually apply any future upstream changes with this approach.)

Transforming classes at class-load time or "retransforming" method bodies at any time via JVM Agents

The JVM has an API called "Java Agents" which lets you register code to modify classes at the time they are loaded.

There is also a "retransform" API that lets you change the definition of already loaded classes. This is used by JRebel to update code in running applications. It's much more limited that Ruby's monkey patching in that you can't add or remove methods (you can change method bodies).

This mechanism is used by https://github.com/fommil/class-monkey to "monkey patch" a JVM bug, for example.

If monkey patching is permitted in both Ruby and Python, why is it more controversial in Ruby?

As a Python programmer who has had a taste of Ruby (and likes it), I think there is somewhat of an ironic parallel to when Python was beginning to become popular.

C and Java programmers would ‘bash’ Python, stating that it wasn't a real language, and that the dynamic nature of its types would be dangerous, and allow people to create ‘bad’ code. As Python became more popular, and the advantages of its rapid development time became apparent, not to mention the less verbose syntax:

// Java
Person p = new Person();
# Python
p = Person()

we began to see some more dynamic features appear in later versions of Java. Autoboxing and -unboxing make it less troublesome to deal with primitives, and Generics allow us to code once and apply it to many types.

It was with some amusement that I saw one of the key flexible features of Ruby – Monkey Patching, being touted as dangerous by the Python crowd. Having started teaching Ruby to students this year, I think that being able to ‘fix’ the implementation of an existing class, even one that is part of the system, is very powerful.

Sure, you can screw up badly and your program can crash. I can segfault in C pretty easily, too. And Java apps can die flaming death.

The truth is, I see Monkey Patching as the next step in dynamic and meta-programming. Funny, since it has been around since Smalltalk.



Related Topics



Leave a reply



Submit