Friday, July 24, 2009

More About "Magic" In Rails

Joe Fiorini e-mailed me:

Overall, I really enjoyed the article. You hit some very important points that I needed to hear, such as discomfort with "magic" exposing programming deficiency. I have no problem using features like eval, mixins, open classes, etc. where appropriate.

However, when you say "magic", I'm not sure you mean the same thing as I and a number of other Rubyists I know mean. From your article, I gathered that by magic you mean the advanced metaprogramming techniques with which most Ruby newbs are unfamiliar. I tend to think of magic in Ruby as important things that are happening "behind the curtain" so to speak.

For example, when working on a Ruby on Rails app, we have a number of ActiveRecord models that include modules. Sometimes, those modules will use the "included" hook to pass has_many, belongs_to, etc. messages back to the class. Given that relationship definitions like that define a large part of the model's interface, I consider this magic and don't like seeing it done (plugins and extensions excluded assuming they are well documented). What is your preference in this instance or similar instances?

I got a similar kind of thing from a Python guy on James Bennet's blog. The answer is: there's no such thing as magic. A superstitious mindset is bad for you as a programmer. It makes no difference what you happen to be superstitious about.

This is a much broader complaint. I talk about the specifics of eval etc. because those are interesting specifics to me. However, the general remains true irrespective of the specifics. I'll give you an example. Consider the phrase "right now." Go into a programming meeting, or talk to a programmer about any system they're working on, and count the number of times that you hear the phrase "right now." Then spend a week or a month where you ban yourself from using that phrase.

There is only right now. Only the moment is real. Ask any Buddhist; ask any quantum physicist. "Right now" is a mental crutch. It separates you from the reality of the system you're building. "Magic" is also a mental crutch. It imposes unnecessary limitations on what you can do.

How I feel about this technique of modifying a model when including a module is how I feel about all the other so-called "magic" in the original blog post. It's not magic. It's a technique. It's a technique with risks and benefits. People fuck it up from time to time because it's a technique they don't have a lot of experience with. As I said before, it's no surprise that most people come to Ruby without the experience of code that modifies code, and that a lot of the confusion that Rubyists experience revolves around places where this type of code does things they don't expect. Any technique, you learn over time where it is and isn't appropriate.

In short I think if you ask this question at all you need to go back and read the original blog post. There are a lot of specific sentences that apply to other things, but the general thrust, I don't even understand how could fail to see that these are the same thing. You've got code modifying code, it surprises you, sometimes unpleasantly, so you put it in a box and you call it "magic." Now we don't have to think about it. Now it's just this scary thing that we can tell our children to be afraid about. That's horrible. Stop doing it.

Or at least, if you're doing it, acknoweldge it for what it is. You're being unscientific. You've chosen superstition over understanding as a guiding principle in how you write code. That's your perogative, but don't expect rational people to respect it. Any programmer who uses the word "magic" should be ashamed. It cuts the discussion short. It's a way of saying "here's something we won't discuss." But a conversation about when to use that particular technique, and when not to, is a conversation worth having.

My own thought is, use it wherever avoiding repetition would justify it. I see people do that in Rails apps all the time, and it's very useful if a lot of your models are similar to each other, especially if it's in ways that have a clear, obvious, necessary relationship to a particular aspect of your site's functionality. However, if that relationship is not clear or obvious to you, you should either take the repetition out, or acknowledge that is at least necessary; and in those circumstances, collecting the common functionality into one file which modifies multiple models the same way could make it clear and obvious to you why those identical or near-identical modifications are necessary.

Clear your mind of fear and say no to superstition. Fear is the mind-killer.