Monkeypatching is Destroying Ruby

A

Avdi Grimm

Hi folks,

I wrote a blog post with the intentionally provocative title above,
which can be found here:

http://avdi.org/devblog/?p=18

While the title is a bit of deliberate hyperbole, I am genuinely
troubled about the popularization of monkey patching in the Ruby
community. I explain my reasons more thoroughly in the post, but
here's a synopsis:

Monkeypatching has become the hip thing to do in the
Ruby and (especially?) Rails communities, and it has
reached the point where experienced programmers are
turning to it as the tool of first resort *even* when there
is a simpler, more traditional solution available. I
suggest that it's time for Ruby hackers to start setting a
better example.

My hope with this post is to start a conversation about fostering
robust software extension mechanisms in the Ruby ecosystem. I welcome
any comments, both here and on the blog.

--
Avdi

P.S. Before anyone accuses me of it, yes, this is a kind of
self-promotion. But I really do want to start a conversation about
this, and no one reads my blog.
 
G

Gregory Seidman

Hi folks,

I wrote a blog post with the intentionally provocative title above,
which can be found here:

http://avdi.org/devblog/?p=18 [...]
My hope with this post is to start a conversation about fostering
robust software extension mechanisms in the Ruby ecosystem. I welcome
any comments, both here and on the blog.

I read the post. I think it is both futile and wrongheaded. Futile, because
I don't think it will change anyone's opinion. Wrongheaded, because I think
it's a filtering problem rather than an approach problem.

For any community of open source software, there will be lots of code
released. A lot of it will be crap. Consider CPAN; it's full of
implementations and reimplementations of the same APIs, functionality, and
bindings to C libraries. Some of the packages are good, but a lot of them
are crap. How do you tell the crap from the good? That's a filtering
problem. Trying to get people to only release good code is utterly futile.

So you had a problem with UnitRecord. Its code smelled bad to you. You
reimplemented the functionality in NullDB in a way that doesn't smell as
bad. Assuming it works equally well (I haven't had occasion to use either),
there are now two implementations of the same functionality, one of which
is, for some metric, better than the other. Should the worse one never have
been written/released? What a silly thought. Even if it shouldn't have
been, there's no means to control it (nor should there be, in my opinion).
Furthermore, would you have thought to write your "better" implementation
if the "worse" implementation hadn't been there as an example? Maybe, maybe
not.

There's an argument, usually in the context of test-driven development, for
doing the simplest thing that works. Well, you say, it doesn't work. No, it
*no longer* works for your purposes. That's when you need to revisit it. If
a piece of code implementing functionality you need is causing problems,
you need to either fix it or find/build a new implementation of that
functionality.

Monkey patching can cause problems, but it makes it easy to get things
working quickly. There's a lot of value in that. Until it breaks, it's a
win. When and if it breaks, it's time to fix or replace it. This is true of
any number of other techniques (e.g. relying on Rails-generated SQL until
there are performance problems).

So don't lament that people are using this tool to increase their
productivity, and that it sometimes gets in the way of your productivity
when you reuse their code. Rejoice that someone thought of useful
functionality and implemented it for you, and fix/replace it if it doesn't
suit your needs. If you are dedicated to never releasing code that includes
monkey patches, more power to you. Don't expect anyone else to follow that
lead.

As a side note, I had a knee-jerk reaction to ignore the entire post when
you brought up dependency injection and robustness. It shows a very
enterprisey (in the worse possible way) perspective. You and your team were
capable of figuring out and fixing your problems. Your reaction to a hard
problem should not be to promote "practices" to make it easier for less
capable programmers; it should be (as it was with NullDB) to solve the
problem and release your solution for everyone's benefit.
--Greg
 
A

Avdi Grimm

I read the post. I think it is both futile and wrongheaded. Futile, because
I don't think it will change anyone's opinion. Wrongheaded, because I think
it's a filtering problem rather than an approach problem.

Thanks for reading it! I appreciate your feedback.
are crap. How do you tell the crap from the good? That's a filtering
problem. Trying to get people to only release good code is utterly futile.

I want to make it clear that I never called for only releasing good
code, or for some kind of control over what kind of code is released.
80% of everything will always be crap; but there's still value in
setting a good example.
There's an argument, usually in the context of test-driven development, for
doing the simplest thing that works. Well, you say, it doesn't work. No, it
*no longer* works for your purposes. That's when you need to revisit it. If
a piece of code implementing functionality you need is causing problems,
you need to either fix it or find/build a new implementation of that
functionality.

I think there's a misunderstanding here - I used the example I did
because it was a case where the initial solution was *not* the
simplest thing that could possibly work. I obviously cannot speak for
Dan's thought process, but to me it appeared an example of having
gotten into the habit of always reaching for the monkey wrench (if
you'll excuse the pun) first, even when it's not the most pragmatic
tool. Whether or not this was the case with UnitRecord, it is
definitely an attitude I've seen more and more lately.

It's this habit that I'm trying to combat, because I think a lot of
times monkey patching not only hurts productivity in the long term, it
hurts it in the short term too. Monkey patching is *not* always the
shortest distance between two points. It's tricky and prone to subtle
bugs. I do not agree that monkey patching is usually the quick &
dirty solution - often it's the slow & dirty solution.

Unfortunately, along with the assumption that monkey patching is the
easiest way to get things done comes the assumption that if anyone
wants to extend your code they can always monkey patch, so there's no
point providing extension points.

Again, thanks for the alternate point of view.
 
E

Eric Mahurin

[Note: parts of this message were removed to make it a legal post.]

Hi folks,

I wrote a blog post with the intentionally provocative title above,
which can be found here:

http://avdi.org/devblog/?p=18

I don't agree with the title, but I agree almost everything you said. When
I first starting using ruby a few years ago, I thought the idea "monkey
patching" was a very powerful and useful technique. Later, I came to the
same conclusions as you. I know we are in the minority. I believe that
"monkey patching" in packages can easily kill interoperability with other
packages.

I think this comment of yours hits the nail on the head:

"And this is really the point. Monkey patching is the new black. it's what
all the hip kids are doing. To the point that smart, experienced hackers
reach for a monkey patch as their tool of first resort, *even when a
simpler, more traditional solution is possible*."

So true. Here are a couple cases I can think of people doing "monkey
patching" and the alternative traditional solution:

* adding new (or perceived missing) functionality (methods) to a class. The
traditional solution would be to just inherit from the class and use the
derived class instead where you want this new functionality. Since you
can't inherit from some classes (i.e. immediates, a problem with the
language IMHO), you can instead use something like Forwardable instead.

* adding #to_* methods to String. A single letter is quite common for the *
which can make a collision all the more likely. The more encapsulated
solution would be to class method for creating one of these objects from a
String (i.e. klass#from_s(s)).

Every case I thought that I needed "monkey patching", I've found a
relatively simple solution. I usually only do monkey patching in the
following cases which have no reuse: top-level script, testing, and quick
hacking.

Eric
 
A

Avdi Grimm

I don't agree with the title

Like I said at the beginning of the article, neither do I ;-)
* adding new (or perceived missing) functionality (methods) to a class. The
traditional solution would be to just inherit from the class and use the
derived class instead where you want this new functionality. Since you
can't inherit from some classes (i.e. immediates, a problem with the
language IMHO), you can instead use something like Forwardable instead.

Indeed. The irony here is that Ruby is perhaps the easiest language
in the world to implement delegation in. I'm planning on writing a
series of posts on alternatives to monkey patching, and delegation is
going to be one of the first techniques I talk about.
Every case I thought that I needed "monkey patching", I've found a
relatively simple solution. I usually only do monkey patching in the
following cases which have no reuse: top-level script, testing, and quick
hacking.

Quite. I have no objection to monkey patching in those contexts,
assuming that it really IS the easiest solution.

Thanks for the reply!
 
E

Eric Mahurin

[Note: parts of this message were removed to make it a legal post.]

Indeed. The irony here is that Ruby is perhaps the easiest language
in the world to implement delegation in. I'm planning on writing a
series of posts on alternatives to monkey patching, and delegation is
going to be one of the first techniques I talk about.

Here are the main alternatives I use (in order):

* put the functionality elsewhere. The simplest example is instead of
monkey-patching String#to_xyz, make XYZ::from_s. When your new class wants
some interaction with a built-in class, put the functionality in the new
class instead of monkey-patching it into built-in class. The monkey
patching approach usually just saves you a few characters when using the new
functionality. I do this 95% of the time where others might monkey-patch.
* inherit the class where you want additional functionality and use the
derived class instead.
* use delegation if inheritance doesn't work (immediates). Delegation is
basically just a "has-a" implementation of "is-a" (inheritance), but it
gives a little more flexibility. I've never really had to use this when I
was tempted to monkey-patch.

It would be an interesting exercise to go through some of the ruby stdlib
and show alternatives to monkey-patching.

Eric
 
T

Thomas Hurst

* Eric Mahurin ([email protected]) said:
I believe that "monkey patching" in packages can easily kill
interoperability with other packages.

Anyone remember the Chainsaw Infanticide Logger Manuever? ActiveSupport
hacked into logger.rb to remove formatting, which was really nice when
you wanted to use ActiveRecord in something outside Rails.

And let's not mention the scary hacking of require. Let's just say.. I
don't use Active* in any of my !Rails projects any more.
 
M

M. Edward (Ed) Borasky

Avdi said:
Hi folks,

I wrote a blog post with the intentionally provocative title above,
which can be found here:

http://avdi.org/devblog/?p=18

While the title is a bit of deliberate hyperbole, I am genuinely
troubled about the popularization of monkey patching in the Ruby
community. I explain my reasons more thoroughly in the post, but
here's a synopsis:

Monkeypatching has become the hip thing to do in the
Ruby and (especially?) Rails communities, and it has
reached the point where experienced programmers are
turning to it as the tool of first resort *even* when there
is a simpler, more traditional solution available. I
suggest that it's time for Ruby hackers to start setting a
better example.

My hope with this post is to start a conversation about fostering
robust software extension mechanisms in the Ruby ecosystem. I welcome
any comments, both here and on the blog.

One of my friends once passed this little quotation on to me:

"Always code as if the person who will maintain your code is a violent
psychopath who knows where you live."
 
M

Marc Heiler

Rails != Ruby

The same is valid for the communities. The communities only partially
overlap.
I know many that do not use rails.
I know many that do use rails.

It is frustrating to try to read a blog that claims monkey patching is
destroying ruby when in actuality the author meant Rails. And I do not
care about rails either way (positive or negative) so it hardly
interests me ...
 
A

Avdi Grimm

It is frustrating to try to read a blog that claims monkey patching is
destroying ruby when in actuality the author meant Rails.

No, I meant ruby. I have been coding in Ruby for ~7 years, since long
before Rails appeared. At work I'm the first to correct people when
they make a comment about "Ruby" when they really are talking about a
Rails-specific feature.

Yes, I used a Rails example. And I suspect that it's true that the
phenomenon is more prevalent in in the Rails community than in the
wider Ruby community. But for better or worse the majority of Ruby
code being written today is being written for Rails, and the Ruby
coders of tomorrow are cutting their teeth in the Rails community.
Rails cultural problems will, increasingly, be Ruby cultural
problems.
 
R

Rados³aw Bu³at

SSBjYW4gc2F5IHRoYXQgSSBhZ3JlZSB3aXRoIGFsbCBwaWVjZXMgb2YgeW91ciBhcnRpY2xlLiBU
aGlzIGlzc3VlIGlzCm5vdCByZWxhdGVkIHRvIHJhaWxzIGNvbW11bml0eSwgaXQncyBydWJ5IHBy
b2dyYW1tZXJzIGlzc3VlIChidXQgb2YKY291cnNlIHRoZXJlIGFyZSBwcm9ncmFtbWVycyB0aGF0
IG5vdCBtYWtlIHN1Y2ggYSBtaXN0YWtlcykuIFRoZSBzYW1lCmdvZXMgd2l0aCB1c2luZyBtb2R1
bGVzIGFzIG1peGlucyB3aGVuIHRoZXJlIHNob3VsZCBiZSB1c2VkIGRlbGVnYXRpb24Kb3IgaW5o
ZXJpdGFuY2UgYW5kIG1hbnkgb3RoZXIgdGhpbmdzLiBJIHNheSB0aGF0IFJ1YnkgaXMgdmVyeSBz
aGFycAprbmlmZS4gSXQncyB2ZXJ5IHBvd2VyZnVsIGJ1dCB5b3UgbXVzdCBrbm93IGhvdyB0byBv
cGVyYXRlIHdpdGggaXQgdG8Kbm90IGh1cnQgeW91cnNlbGYuCgoKLS0gClJhZG9zs2F3IEJ1s2F0
CgpodHRwOi8vcmFkYXJlay5qb2dnZXIucGwgLSBt82ogYmxvZwo=
 
M

Morton Goldberg

One of my friends once passed this little quotation on to me:

"Always code as if the person who will maintain your code is a
violent psychopath who knows where you live."


Truly wonderful. If there were a poster of that available, I'd buy it
and hang it where I could see it when I look up from my screen.

You didn't make an attribution, so I presume you don't know who
originated it. Too bad.

Regards, Morton
 
T

Trans

Oh bother. If it ain't the Duck, it's the Monkey.

No. It's not the Monkey, or the Duck that's killing Ruby... It's the
Peoples.

T.
 
T

Todd Benson

Oh bother. If it ain't the Duck, it's the Monkey.

No. It's not the Monkey, or the Duck that's killing Ruby... It's the
Peoples.

In other words, it's the hairless monkeys (I'm one of them :).

Todd
 
M

Morton Goldberg

In other words, it's the hairless monkeys (I'm one of them :).

No, no, it's the apes. Monkeys have tails. The tailless primates are
called apes.

Regards, Morton
 
M

M. Edward (Ed) Borasky

Morton said:
Truly wonderful. If there were a poster of that available, I'd buy it
and hang it where I could see it when I look up from my screen.

You didn't make an attribution, so I presume you don't know who
originated it. Too bad.

Regards, Morton
Right -- I don't know who originated it, but I'm guessing you could find
some guesses on Wikipedia. :)

I do know that Gerry Weinberg is the originator of "If we built our
buildings the same way we build software, the first woodpecker that came
along would destroy civilization."
 
J

James Britt

Avdi said:
Hi folks,

I wrote a blog post with the intentionally provocative title above,
which can be found here:

http://avdi.org/devblog/?p=18

While the title is a bit of deliberate hyperbole, I am genuinely
troubled about the popularization of monkey patching in the Ruby
community.

That so many people use the phrase "monkey patching" suggests many
people don't actually understand how to use it.

It's no more "patching" than reassigning to a variable is patching.

"ZOMG! You're changing something at runtime!"

Yeah, happens all the time. Use with caution; get on with life.

Maybe if people weren't so spooked by metaprogramming they'd see it as
yet one more feature of the language and use it appropriately, rather
than treating it like high-priest voodoo that seemingly works by magic.



--
James Britt

"Serious engineering is only a few thousand years old. Our attempts at
deliberately producing very complex robust systems are immature at best."
- Gerald Jay Sussman
 
E

Eric Mahurin

[Note: parts of this message were removed to make it a legal post.]

That so many people use the phrase "monkey patching" suggests many
people don't actually understand how to use it.

It's no more "patching" than reassigning to a variable is patching.


When you are talking about global (and to a certain extent class) variables,
there is an analogy. Most languages do have global variables, but they are
also usually discouraged (especially changing them) because of the
side-effects (state changes). Monkey patching is in this same vein, except
worse IMHO (the state changes can be more intrusive).

Like global variables, I don't think monkey patching is a good practice for
reusable, inter-operable, and maintainable code.

On the other hand, one nice time to use it is for creating a ruby-based
DSL. But, when making a DSL where monkey-patching helps, I think the monkey
patching should be done as a thin layer. The base functionality should use
no monkey-patching so that it can be used cleanly with other packages.
You'd likely have problems combining multiple monkey-patched DSLs together
if you couldn't disable at least some of the monkey-patching.

You might also say the same of global variables. If you use them, use them
only at the highest user/DSL/script/test level. The baseline reusable
functionality should just be passed that global variables and not use them
directly.

Eric
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top