Don’t overuse instance_eval and instance_exec


Not sure how known this antipattern is. I’ve seen some libraries that are very good at not doing it, but I’m also seeing lots of Ruby code that does use it without reason.

What I’m talking about here is the common usage of using instance_eval on a block to make it possible to use other methods inside of that specific block. If you want to do stuff based on method_missing inside of a block, this is the way people generally use.

So what’s the problem with it? Well, the problem is that blocks are generally closures. And you expect them to actually be full closures. And it’s not obvious from the point where you write the block that that block might not be a full closure. That’s what happens when you use instance_eval: you reset the self of that block into something else – this means that the block is still a closure over all local variables outside the block, but NOT for method calls. I don’t even know if constant lookup is changed or not.

Using instance_eval changes the rules for the language in a way that is not obvious when reading a block. You need to think an extra step to figure out exactly why a method call that you can lexically see around the block can actually not be called from inside of the block.

There are ways around instance_eval usage in a library – you can always assign self to a local variable outside of the block, and then call methods on that local variable. How ugly does that sound?

In almost all cases, if a block needs to handle method calls on a specific object, it should send that object in to the block as an argument. Take a look at routes in Rails – they could have been defined with instance_eval, but they’re not. There is no reason to use instance_eval for this case. Rails send in a route instead. File.open doesn’t use instance_eval with the block. It could, but instead it sends in the file. This is because there is no need to use instance_eval, and sending in the object as an argument gives the definition more power.

This doesn’t mean instance_eval should never be used. That’s not true, it’s a hugely useful feature, but it should definitely be used with good taste. If you’re unsure when to use it, don’t!