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.

5Tech - Week 2: Grails

For the second project and technology in five weeks I chose Grails.  I was very curious to try Grails on on a "real"/non-tutorial project to confirm its usability and productivity.  On my last contract gig, I was in a non-development role (configuration management) on a project that was using JSF 1.1/Spring/Hibernate and WebSphere, and their development looked really painful by my standards.  For example, they'd spend 5-7 minutes to redeploy the app just to inspect some HTML change in the JSF page.  I was looking to Grails to provide a much more productive environment.

The project involved creating a system to process events and route them to users.  It was inspired by a code base that I worked on for a previous client.  (This was done with their permission.)  They have a large code base that includes "application functionality" like this event routing, as well as a lot of "technical functionality" that they rolled themselves years ago before modern tools like Spring and Hibernate were created and became mainstream.  In a way, this was a small prototype to research the feasibility of reimplementing the application functionality on a more modern platform - Grails.

All in all the project went very smoothly. Because Grails is comparatively mature, there exists a fair amount of documentation, including numerous books.  I leaned heavily on Grails in Action by Smith and Ledbrook.  I was able to follow their examples and adapt them to my application easily.  There just weren't any serious gaps or surprises.

Using Grails to create domain classes (database entities) was a breeze.  It's so nice to just declare the fields and their constraints and have Grails "do the right thing."  You don't have to bother with annotations, let alone XML configuration files.  Creating controllers to process web requests is trivial, and being able to scaffold them to get the basic CRUD functionality in place immediately was very conducive to high productivity.

I also liked the idea that services are a first-class concept in Grails (along with controllers and domain classes), which makes it very easy to program with them.   Grails wires them into the classes that use the services via Spring, but again, you don't have to monkey with Spring's applicationContext.xml file.  Finally, adding the REST interface for incoming events was almost trivial, especially since I already had the service in place.

Grails (like Rails) treats testing as a core concept, not something you wave your hands at after the fact.  It has the concept of unit and integration tests, and there are a number of functional test plug-ins, too.  I did a fair amount of unit and integration testing, which was a real live-saver.  Due to the very dynamic nature of Groovy, many of my typos were not caught in the compile phase, but exercising the code in tests did catch those.  I had very few issues when I ran the actual web application.

A lot of developers are unclear on the difference between unit testing and integration testing.  For better or worse, Gails makes some clear distinctions.  A lot of the Grails voodoo (e.g., dependency injection and adding dynamic methods to classes) is not available in unit tests, or you have to add it yourself via mocking or manual injection.  Thus, there is a simple distinction: if it runs under "test-app -unit", it's a unit test, otherwise it's an integration test.  This bit me once early on when I wrote a test that needed that higher-order functionality, but I put it in a unit test.  First I got a null pointer exception because the service hadn't been injected, then I got a missing method exception because the save method on the domain object hadn't been added dynamically. However, the fix was simple enough - move the test to the integration folder, and it ran fine with -integration.  Then, I copied it back, added a few mocks (very easy in Grails), and I had a unit test, too.

In terms of "project management," this step of the Five Technologies suffered from some "life happens" distractions.  Rather than running Monday through Friday, it ended up being Tuesday through Monday, and some of those days were a bit short-changed.  When I was on task, the Pomodoro Technique continued to be effective, and I replaced my dead watch/timer with a dedicated Android application called Pomodoro Tasks - it's even open-source.

In conclusion, Grails was another success for the Five Technologies in Five Weeks project.  I got the application functionality done that I expected.  Grails was very usable and productive - no nasty surprises.   What's next?  Probably, NetBeans Platform because I have documentation for it already whereas I'm waiting on some Android books.