Sunday, July 07, 2013

Considerations for JavaScript in Modern (2013) Java/Maven Projects

Disclaimer: I'm a Java developer, not a JavaScript developer. This is just what I've picked up the last years plus a little research the last days. It's just a snapshot of my current knowledge and opinions on the day of writing, apt to change over the next weeks/months.

We've gone all modern in our web applications, doing MVC on the client side with AngularJS or Ember, building single-page webapps with REST backends. But how are we managing the growing amount of JavaScript in our application?

Yeoman's logo (not necessarily the conclusion of this blog post)

You ain't in Kansas anymore

So far we've just been doing half-random stuff. We download some version of a library and throw it into our src/main/webapp/js/lib, or we use it from a CDN, which may be down or unreachable when we want to use the application..

Some times the JS is minified, other times it's not. Some times we name the file with version number, other times without. Some times we get the latest library of master branch and name it with the commit-id in the file name, other times we write the commit-id into the top of the included file, or maybe forget about it completely (but please note the source in the commit message at least!). We write CoffeeScript/Typescript that generates JavaScript and we check both files in.

Sigh.

This is a mess. Add to it the mess of just including all JavaScript libraries into the global scope and loading them in random order. 

So, there are some camps out there who are figuring out how to solve this.

There's the Google/GWT/Closure camp, which should be very appealing to Java developers, doing everything "properly" with Maven and Ant plugins. There's also James Ward's WebJars.org which looks promising, but the library coverage is still kind of scrawny.

On the other side, there's the traditional webapp makers, the PHP/Ruby/Node camp(s) which depend on having either of these tools on the command line and using them during development and during the build process. 

My gut feeling is that the latter camp is where the most interesting activity is coming from. These guys have the most experience with hacking and building together tools for easing web development, and they're busier than ever. 

Why not just use Maven plugins and WebJars?

There are plenty of maven plugins for doing JavaScript processing, and you can treat JavaScript libraries as regular runtime dependencies if you want (just overlay them into your webapp).

However, Maven plugins and webjars are regularly far behind the respective Node/Ruby tools because:
  • Maven plugins are tedious to create, so most library devs can't be bothered, and leave it to volunteers from the community.
  • Many library devs don't use Maven anyway.
  • Many library devs are mainly JavaScript developers. Maybe they know a little Ruby or PHP at the most. Probably a lof of them hate Java.
  • The maven plugins use older versions of tools since they are lagging behind. This will be annoying for you as you may have to start patching plugins to get the latest fixes from the JS library in question.
And Maven projects has a tendency to suck things into their tediously slow lifecycle, and depend on the IDE  to speed things up. This works great for some few big languages and libraries, but it's impossible for our IDE's to keep up with the myriad of web development going on out there (although IntelliJ IDEA is doing a fine job of trying).

So, let's run through the various things we want to do with our JavaScript:

Automating tasks, scripting

How do we run the whole build, and trigger other JavaScript processing? We need a scripting tool for automating tasks: copying stuff around, running optimizers, etc. Some place we can call into during the Maven build (using maven-exec-plugin or something like that).

Equivalent in Java land: Ant, Gradle, bash/bat

Solution: Grunt

Alternatives: Pure Node.js? Bash/bat.

Downloading, managing dependencies

Manually downloading JavaScript libraries and upgrading them is a hassle.

Equivalent in Java land: (parts of) Maven, Ivy

Solution: Bower. Based on Node/npm. No central repository, but central registry, uses Git for downloading.

Alternatives: JamJS. Doing it manually.

Managing dependencies at runtime

Since JavaScript has no packages or namespaces, this can get messy quickly, so you need to handle which libraries come into play in which places.

Equivalent in Java land: Maven, packages, classloader fiddling, OSGi

Solution: RequireJS

Alternatives: YepNope. Anything that implements CommonJS or AMD.

JavaScript MVC framework

I'm not sure if that's a good term for it, but the point is to place logic in organized places, and get features like dependency injection and separate your JavaScript from the DOM as much as possible. If you're just working the DOM with JQuery, you're doing it wrong (cause you can't unit-test it).

Equivalent in Java land: Spring MVC, Struts

Solution: AngularJS (from the Google camp) or Ember (from the Rails camp)

Alternatives: Knockout.js, Backbone.js and many others. There's a real framework war going on.

Running unit-tests and web-tests

I haven't researched too much in this direction, as we've just followed the AngularJS recommended choice of the Karma test runner. I like the concept that I can run pure JavaScript unit tests without firing up a browser.

This choice is likely to depend a lot on what other tools you pick. Regarding mocking, I've heard good things about Sinon.js which will work with any testing framework.

Writing JavaScript or generating it

Instead of writing "raw" JavaScript, you stick use the friendly CoffeeScript, the strongly typed TypeScript or the functional ClojureScript. All these transform into JavaScript in the end, although the first two are compiled with Node.

The reason why you should do this is that JavaScript has a lot of sharp edges that can bite you if you don't know the language well.

You could also skip this step and save yourself the extra build-step and level of source-mapping.

Finding the JavaScript source of compressed JavaScript

Make sure you get this right or debugging will be a right pain. The point is to be able to look up where in which source file you are inspecting from your browser, and source maps are the key. There are many tools that can generate source maps for you, but I'm not sure which one I would use yet. Not sure how this applies to CSS (see below).

Styling

Now I know this isn't part of JavaScript, but I'll do some notes about CSS here since it's often handled in the same part of the build. You do not want to type CSS manually, as the standard has moved very slow since its inception.

I'm still kind of fresh in this area. There's a CSS-preprocessor called LESS, and there's another one called Sass. Now Sass comes in two different syntaxes: the original .sass (which is kinda whitespace-sensitive stuff), and there's the new .scss which looks a lot more like CSS than Sass does.

There are also some things called Compass and Bourbon, which are both some kind of mixin-managers for Sass, which allow you to not have to deal with vendor-prefixes and other goodness. Both of these are Ruby-based, so

I'd think I'd go for Sass with .scss syntax these days, probably with Bourbon added.

By the way, here's a recent podcast episode from TheChangelog on this subject.

What about Yeoman?

It's somewhat of a starter kit for client-side applications. It features several of the above mentioned technologies in an "opinionated" stack, including Grunt and Bower. I think I'd definitely take this for a spin, as it encompasses the tools I would choose today.

Conclusions

Make sure you are left with a stack that lets you keep a quick roundtrip. Save file, refresh in the browser, change is there.

  • Stick to Node/NPM. Node is supposedly working well cross-platform now. 
  • Grunt is great. Give up on trying to do everything with Maven. 
  • Avoid Ruby? The way I see it I have to drag in Node/NPM at some point anyhow, and if I can then do without managing Ruby versions on my systems, all the better. JRuby via Maven is also an option. (counter-point: Sass, Bourbon and Compass tend towards Ruby)
  • Write pure JavaScript or try out CoffeeScript. I'd say Typescript is a bit too bleeding edge for now (though I may be wrong). ClojureScript is too different from the resulting JS, unless you're using Clojure on the server-side.
  • Frame your JavaScript in a framework like AngularJS or Ember. These have a great ramp-up for getting newcomers started on building single-page applications with tests.
  • Use Sass. With SCSS syntax because that is most similar to normal CSS.

---------------------------------------
PS: If you enjoy podcasts like I do, I recommend JavaScript Jabbers. A lot of the things above I picked up from a few of their episodes here: