Trusting Magic Methods post

August 21st, 2007

One of the reasons I use CakePHP (well, other than the fact I get paid to :) ) is that I'm a believer in full-stack frameworks, and learning to build your application within the rules that the framework lays down is an exercise in understanding the problem domain and then figuring out how to solve it with the tools at hand. Most full-stack frameworks rely on what I call "magic methods", complex functionality that has been reduced to a simple API for the developer to use. A good example of this is the ability to create relationships between models. If you say "Foo has many Bar" and create that relationship in the model, you can access all the Bars that belong to Foo with a simple command: ~~~ $bars = $this->Foo->Bar->findAll(); ~~~
Talk about magic methods. All sorts of complexity in terms of writing your own SQL code to pull this information out is totally hidden from the developer and you are left with an easy-to-use API for getting that information. But, like anything, this comes with a price.

A common message that I see on the CakePHP mailing list is one from a user who is examining the SQL generated by command like the one I showed above. They question why it's being done this way. "The SQL isn't efficient, why does it have joins, etc". All valid arguments but reveal a huge problem that goes unspoken in programmer circles: trust is an issue when using someone else's code.

Look at this code: ~~~ $this->Media->hasAndBelongsToMany['Tag']['conditions'] = array('Tag.name' => $tag); $media = $this->paginate('Media', array('AND' => array('Media.approved' => 1, 'Media.published' => 1))); ~~~
That's a lot of serious complexity in two lines of code. This is part of some code I'm writing for a CDC project where I'm trying to paginate over a result set of media that have been given a particular tag. The current pagination method wasn't able to do what I wanted, so with some help from gwoo I found out what magic I needed to make things work the way I wanted. That code is amazing when you think about it, but do you trust it? If you use CakePHP you have no choice but to trust that the model methods are doing things the best way they can.

Being open source, there is nothing stopping people who have gripes about certain functionality to go in and fix it themselves, but how many people really do? 1% of the users? 1% of 1% of the users? It's a very, very small percentage, small enough that it becomes daunting at times to contemplate contributing to a popular open source project. So, instead you sit back, curse the developers and trust that whatever problem you want fixed will be fixed.

Adoption of any library or framework depends so hugely on trust, I'm surprised that it never comes up in the discussion. I guess it's so obvious that nobody has seen it. If I don't trust a tool to do what I want it to do, I won't use it. Witness my retreat (once again) from vim to Komodo. I don't *trust* my ability to make vim do the things I can do in Komodo, so I go back to using a tool that I do trust. Recently, it was the ease with which I was able to debug some code using XDebug and Komodo. It's something I could never do with vim because I don't trust myself with vim.

I'm not saying you need to have blind faith in a particular bit of software, because that's dangerous and leads to a lot of unfulfilled expectations. The path from fanboy to intense critic is a very short one, especially if you bought into the promise of something and it didn't deliver the way you wanted. I trust CakePHP for very different reasons, ones that obviously make me a fanboy. I liked what the framework was about, trusted that the magic methods were doing what they were supposed to be doing after getting to know the core development team, and now I earn a living off of trusting that CakePHP can do the job that I want.

The biggest lesson I've learned from thinking about this idea of trust is that you have to do your research and learn how the magic methods interact with each other. Could I have possibly come up with that line of code above that sets the hasAndBelongsToMany relationship in order for me to find all tags associated with a particular media file? I would have to say that before I asked gwoo about it, I was thinking I was going to have to set *some* value in $this->Media to get the pagination functionality to work but didn't know where to start. But now that I see the code I understand why it works. It's the dynamic setting of conditions for the hasAndBelongsToMany that forces the Media model (when used by the pagination functions) to pull in the associated Tag records. It's the ultimate hindsight: "Of *course* that's what you should do, it makes perfect sense in front of my grumpy face!";

So next time you are evaluating a piece of technology that you wish to start using, ask yourself if the reason you are reluctant to use it is because you don't trust it to work the way you are expecting it.