Tuesday, April 06, 2010

Django vs. Grails

When I came up with the Five Technologies in Five Weeks project, I hadn't intended to compare any of the technologies to each other directly.  My original ordering for the technologies didn't have any head-to-head match-ups, but I just finished my Grails project, which was "right after" (modulo interruptions) my Django/GAE project.  And, while I was working on Grails, I kept comparing it to Django, and I felt compelled to write up my observations.

"All things being equal, which they never are" (Manager Tools), I'd choose Django over Grails for a new, green-field project.  However, given the constraint to run in a Java environment, I'd gladly choose Grails over the other Java/J2EE frameworks I'm familiar with.  (Django on Jython would be a contender as would Wicket, but I don't have hands-on experience with either.)  This is not a slam on Grails.

Here are some of the reasons I prefer Django over Grails:
  • Development Speed (not runtime performance): running (a trivial number of) unit tests for Grails on my machine took about 15 seconds (with Folding@Home in the background).  Django unit tests typically only take me a couple of seconds (for a trivial number of tests).  Integration tests in Grails were 30-40 seconds; Django might be 5 seconds.  Although Grails will reload the running webapp when you make a change, sometimes things are really buggered, which requires a full restart.  That seemed to happen more often in Grails than Django, and restarts take ~20 seconds vs. 2 seconds.
    Compared to the 5-7 minutes I've seen WebSphere to restart an application, Grails is blazingly fast.  But the 15-30 seconds that many operations take in Grails is long enough for the mind to wander, which is not good.
    Update: a commenter pointed out the (under-documented) interactive mode of Grails.  That speeds the edit-test-edit loop considerably.
  • Voodoo: both Grails and Django do some voodoo behind the scenes to reduce the amount of chimp programming (e.g., non-DRY boilerplate) the programmer has to do.  This voodoo involves topics of the high priests like meta object programming that mere mortals typically don't have to worry about.  Maybe it's just that I don't have as much "time behind the wheel" with Grails, but I feel like its voodoo leaks out a bit and is too voodoo-rific.  A Grails programmer has to be aware of which methods are dynamically added to a class in order to separate unit tests (no voodoo) from integration tests (full voodoo). And, although I like the power of dynamic finder methods like User.findByName(), it kinda bugs me that the code for that doesn't exist somewhere that I can see (you know, on punch cards!)  You don't see the dynamic methods on a Django class, but there are a lot fewer of them, so it seems less voodoo-rific.  Again, maybe more time behind the wheel of Grails would make me feel more comfortable.
    (As an aside, when I taught Python and Django in Spring 2009, the students didn't even notice the voodoo of fields on models in Django until I pointed it out to them.  Then, I exploded their heads with MOP.)
  • View Technology: I have a long-standing personal preference for templates instead of ASP/JSP-like mark-up languages, and I'd lump Grails GSP pages in the latter category.  Back in the day, Jason Hunter wrote an essay called The Problems with JSP that really stuck with me.  GSPs are much, much better than the Model 1 JSP pages that Hunter talks about, but they still feel similar enough to make me think about using something like Velocity with Grails.  Django kicks templates up a notch by having inheritance with templates, which I really love. (Even if Django didn't invent template inheritance.)
  • Dynamic Language Issues: This is a real unfair comparison because I've been using Python for 15 years, but I felt that errors in Grails/Groovy were more cryptic and hard to find compared to Python.  If I typo the named parameter to a method in Python, it lets me know immediately.  More than once in Grails I misspelled a named parameter to a constructor, which failed silently and then lead to a validation failure later when I went to save the object.  Some of that is from lack of experience with Groovy/Grails - i.e., learning what error messages really mean.  But still, Python seems to fail in a more helpful way.  (I'd say Python fails gracefully, but if anything it's the opposite: very loud - "you dumb-ass, there is no parameter called recipent" when I misspelled recipient.)
I don't mean for this to be a hit-piece against Grails; I really like Grails.  I look forward to using it some more.  It's just that, for what it's worth, I like Django more.


ラファエル said...

Development speed can't be compared between django, rails and grails. The JVM is a big villain but the ball and chain that ruins your agility is spring and hibernate.

To minimize the jvm boot slowness at least, grails has an interactive mode, 'grails interactive', that keeps the vm up.

Charles Anderson said...

Yeah, the interactive mode helps a lot. For my trivial number of tests, it made the times comparable with Django, and it certainly is fast enough to keep the mind from wandering.

I know the release notes for that feature (in 1.0.3?) said it saves the JVM startup time, but from my observation, it looks like the big win is starting the whole Grails environment. Once the OS caches are warm, the JVM starts pretty quickly, but getting to that first interactive prompt takes a while.

Anyway, thanks for that tip.

Codefisher said...

Been using Grails recently for a University project. It had to be written to run on the JVM, so the team that I am working in decided on grails.

I like grails, it has been much nicer to work with then say PHP - which tends to create spaghetti code. But at the same time, like you I don't feel it is in the same class as Django - which I have used, but maybe not as much as I would like. Django seams so much more stable, there are a lot less bugs (like some of grails lazy loading has caused me trouble). But Django is much older then grails, so that was expected.

And as you pointed out as well, when Python/Django fails it does so loudly and gives the exact location of where it went wrong. With Groovy/Grails I find myself struggling to find the case of the problem, particularly when it fails quietly.