The lack of magic in Django
...or why Rails and Zope 2 were too magical.
There's been a bit of a discussion recently about magic from James Bennett and others. Also one of my friends recently had a play with Ruby and came to some conclusions. I did a bit of work in Rails a while back and it never did much for me I found large applications confusing and hard to debug.
If you ask me why I don't like Rails, there's a simple answer: it has too much magic.
We all know the quote about technology being indistinguishable from magic. In that law Arthur C. Clarke is describing how a lack of some basic underlying principles means that observers are unable to determine how things work and they seem magical. The same thing can happen in software, something happens when the following peice of code happens. But something magic has happened to make it do so and it's not obvious to the person developing the software because they are lacking some underlying peice of knowledge.
Now we can say simply that I didn't understand all of Rails and hence I was missing knowledge and hence it was all magic. I think that would be wrong, whilst I never got completely to grips with all of Rails, I knew the key parts well enough to be able to produce projects that worked. The test for this is reading someone else's code and applications and trying to figure out what's happenening and that's where Rails (and to a small extent Ruby) do magic to solve a problem.
Let's start with an example. Django has explicit models that declare the types of fields a model has. For example here's a Django model:
class Poll(models.Model):
question = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
Django (optionally) creates the SQL from your model and populates the database with it. Here's a model from Rails.
class Post < ActiveRecord::Base end
The schema is declared in a migration.
class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
t.string :name
t.string :title
[...snipped]
In Rails you write the migration, the schema is created in the database and Rails infers from the database your model (don't forget Rails will rename your table and try to pluralize it for, thanks Rails).
Now let's take a scenario where you are working on a project that has over 200 migrations. To tell what's in your model, you either a) wade through all the migrations that are relevant to your model or b) keep examining your database. It's not that point b) is bad, it's important to know what's going on in your database.
Explicit is better than implicit
In Django I'm explicitly saying what fields I'm expecting on a model, what they are and what they manage. I'm also declaring the methods and attributes on an object - or a namespace. I know what at a glance Poll.question (Django) is, figuring out what Post.name (Rails) isn't as obvious. Indeed there's nothing stopping me adding a method to the Post class called name. Rails is doing a looking up for you on the object to calculate the variable.
But it's doing more than that. For example:
class Sample < ActiveRecord::Base
def wtf
foo
end
end
What does Sample.wtf give you? Does the method foo exist on the object (and presumably in the inheritance tree), does the method foo exist somewhere else in the namespace, does anything else import foo into the global namespace, does foo exist on the database for that model?
To me this is not explicit and hence somewhat magical. That is my definition of magical, something is going to happen that is not obvious or clear. There's some implicit assumption that you know a whole bunch more information about the code and the context it's being run in.
As a developer reading this you have to do all that work to figure out what foo is, or do runtime analysis. To me this flies in the face of another rule:
In the face of ambiguity, refuse the temptation to guess.
Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.
This philosophy goes on through Rails in many different areas, here's some more examples.
Let's look at Zope 2 for a minute and Acquisition and DTML. Alright I'm picking at pieces that either no longer exist or are now explicit, but that's because the developers found way too many issues with the said technologies. Learning from past mistakes and not repeating them is an important thing to be able to do.
In Zope 2 there was a templating language called DTML that you could write on objects. When it was rendered it would act on the object in the database and then use Acquisition to figure out what to do next. Acquisition can be pretty mind bending, it doesn't mean it was wrong, but to many developers it was just far too magical.
Code that worked in one place, wouldn't work in another because the context it was being executed in wasn't right. Not in itself wrong, but very hard to figure out and debug so to many it became magical. Never mind the confusing syntax which differentiated between:
<dtml-var foo>, <dtml-var "foo"> and <dtml-var "_.foo">
I have to confess I don't have any concrete examples of this since all that code has long since vanished into a distant past, but I wanted to point out that it's not just Rails. Training developers in this was great fun, the principle was all well and good, but all too often I saw people resort to random guessing of code to get it working.
Technology is thought of as magical because something is happening that we don't understand. Frameworks that make decisions for you at runtime based on things you haven't explicitly declared can be seen as performing some magic. The level of magic has inverse relationship to the level of explicitness. Django is more explicit and much less magical. Rails is less explicit and rather magical. Zope 2 was very magical.
BTW: Yes, I'm quoting the Zen of Python, by Tim Peters. To read the whole thing start your Python and type "import this".
Comments
There are no comments.
Login to add comments

