allenc allencheung

Coffeescript: Joyful and Unreadable

I had an interesting chat with a visiting engineer today about Javascript app frameworks, specifically Backbone.js. Backbone is probably the most popular one out there right now, on account of having excellent documentation and being minimalistic and non-intrusive. They even have a section telling you how awesome they are.

Anyway, he was telling me how the combination of Backbone and Coffeescript made for happy JS app development. Coming from a Ruby enthusiast, the structure that BB provides along with the nice syntax of CS and the cleanliness of writing pure client-side code with an abstracted API backend was a “joyful experience”.

Strange, I thought it made for some terse, unreadable code.

Of course, I’m hardly the expert in this area; the exposure I’ve had comes from playing around – and failing – with the BB + CS (+ jQuery + underscore.js, which comes with BB) stack, namely to try to layer a JS app on top of WordPress via the WordPress JSON API. I wanted to be hands-on with the technologies, and they did exactly as advertised, providing the base for an app and some really nice simplifications along the way. For me, though, the trouble came from using all of them together.

Let’s review. First, there’s:

  • Underscore.js, a perfectly nice, minimalistic library that adds a bunch of nice utility functions and a simple templating library.
  • jQuery, the DOM manipulating wonder that’s the simultaneous cause of joy and suffering for web devs. It’s great when you can move 5 pieces of DOM around on one line, it’s terrible when I have to debug it.
  • Backbone, which adds additional model, view (really more like a controller), routing and API “sync” support, along with some conventions to tie itself back into the DOM.
  • Coffeescript, which tries to make everything look and feel like Ruby, but has to leak the underlying Javascript at times as the languages don’t directly translate.

When I put all of them together, the result is some really terse but dense code. I found myself writing lines like this:

fetchRecent: (num) ->
  AC.api.getRecentPosts num, (posts) =>
    @reset (new AC.content.Post(post) for post in posts)

But I wasn’t sure whether my code would be a good sample of good BB + CS code, so I Googled and found some other samples:

render: =>
  # Set the name of the page
  @el.find('h1').text("Editing #{@model.getName()}")</p>

<p># Render the content
  @el.find('.ui-content').html(@template({venue : @model}))

There is so much happening on each line across multiple libraries that I’m finding myself spending extra effort to understand what’s being done. For example, for that last line of code:

  • @ is a CS shorthand for JS’s this.
  • Because we’re inside an event handler, we have to use render: => to preserve this to mean the backbone object.
  • el is a backbone convention that a view uses to reference a DOM element. Since we have jQuery convention makes it a jQuery object as well.
  • @template refers to a template we set up previously, using underscore’s built in templating system. It takes in an object of key-value pairs, which the backbone model object @model provides.

Maybe I’m a bit old-school JS, but having to context-switch across multiple systems on just one line of code is a lot of work.

I think of JS as a powerful but not exactly feature-rich language – constructs like classically inherited classes, public/private methods and namespaces are basically hand-built by people who truly understood its strengths and limitations. These imperfect abstractions we’re layering on top makes for easy writing at-the-moment, but much more difficult reading, not to mention the debugging nightmare.

That said, if making Javascript look and act kinda like Rails makes Rails devs happier, maybe it just means I’m not Ruby enough.

By allen
allenc allencheung