Monday, September 3, 2012

Keeping JS Sane

Keeping JS Sane:
I gave a talk at #DDD10 this Saturday about keeping JS sane, I had some questions after about the list of things I ran through so documented in all its glory are my current thoughts on development with a dynamic language like JS.
Javascript sucks and it doesn’t matter
Yes, you can add arrays to numbers to strings to whatever else you like and get bizarre and hilarious results. Yes, you can write code with more callbacks than Lara Croft on a dating website – but if you’re doing these things in production code then you deserve what is coming to you – and that’s before I even ask if you have tests which would exercise that code and raise the common errors anyway.
And yes, it’s a dynamic language and you don’t get all that compile-time goodness you’re used to, you don’t get all of the re-factoring support you’re used to in [insert your IDE here] and you don’t get all of the magical intellisense that helps you from having to think but you do get all the other advantages inherent in a scripting system which I’m not going to talk about because man it’s a boring subject and it has been talked to death by a million language nerds and shouldn’t you just be writing Clojure anyway?
Tldr: Most of the craziness in JS won’t ever affect you, and if it does your tests will save you.
Use JSHint/JSLint
This is not just something that is a ‘nice to have’, this is not something that you read in a blog entry and go ‘oh, we should do that sometime’, this is genuinely something you should be setting up at the very start of a project and integrating with whatever editor it is you are using to give you and your team feedback as you pour your efforts into a project full of files.
Yeah – there are two of them and yeah you need to pick one. If your team is really going to have an argument over it, just flip a coin and move on because the difference is pretty much that JSLint tends towards being a crockford-style-enforcer rather than just a sanity checker, this is irrelevant for most people.
In each of your projects, you can have a .jshintrc file dictating what my preferences are for that directory – and you can turn off a lot of the warnings if you have a case of the ‘I know better’ (or your code doesn’t use semi-colons). This doesn’t stop the instant feedback every time you save being incredibly useful in shortening the feedback loop between code-change and code-breakage.
To get and run jshint on your project, the following steps are all you need.
- Install nodejs

- type ‘npm install -g jshint’

- type ‘jshint ./src/*’

By setting this up in your build process/editing process/etc, you are pretty much covering half of the ‘good things’ automatically leaving you able to spend a bit more time focusing on the large rather than the small.
Write tests, no not those tests, the other tests
Yeah, testing is a pain, and yet even if we have a nice suite of tests for our backend .NET/Java/Whatevs we often don’t bother writing any for our front-end because it’s difficult to isolate logic for testing away from the browser, and testing code inside the browser means setting up godawful tooling and build scripts. This high initial pain putting people off is understandable, which is why for the most part the trick is ‘not to bother with all of that’.
I’m a fan of headless browsers, of which there is again a choice of two main players (Zombie and Phantom), both can be driven from Javascript to test Javascript, and both can be set up with little effort as part of the build scripts. Let’s show you how easy this actually is with zombie.
- Install nodejs

- type ‘npm install zombie’

- Write code like the following in a JS file
var Browser = require('zombie')
, client = new Browser()

client.visit('http://localhost', function() {
client.fill('title', 'This is an awesome blog entry')
client.click('submit')
})

- Run the file with ‘node myfile’
Combine this with a test runner like mocha (npm install -g mocha) and you have a way to test pretty much everything across your web application. Yeah – you won’t be able to test animations and transitions and whatever but you can at least make sure your functional code works effectively.
You can even do crazy things like grab handles to the real JS objects present within the application and assert on them, so for example:
var title = client.execute("$('#title'))
title.css('background-color').should.equal('#FFF')

Or
var app = client.execute('global_application_object');
app.currentView.should.equal('/ponies')

All of this assumes that you can easily bootstrap your application and that you have decent in-memory implementations of things like persistence (if you want to be able keep your tests fast). In .NET this could mean using SQLite with NHibernate for example or if you’re starting from the beginning maybe just writing everything to in-memory implementations and doing your real persistence work later in development (a sane way to work anyway tbh).
Anyway, that’s up to you – I would advocate that for *most* logic in *most* applications that this sort of headless full-stack testing is a better use of time and energy than striving for isolated unit tests for most scenarios (You can always drop down to testing components in isolation where necessary anyway – follow the pain and all that).
By having a half decent suite of tests that describe the behaviour expected from the application, you’ll likely catch a lot of silly mistakes that would otherwise slip through to production. The tooling now exists to make testing easy so there is little excuse not to do it.
Use coffeescript
I actually don’t like Coffeescript all that much, I don’t really like Ruby all that much either – the best languages are the ones with a few constructs to pick up and run with instead of 15 million ways to avoid writing an if statement or for-loop.
That said, JS isn’t very terse, Coffeescript is – and I value that sometimes, I especially value it in my tests where I don’t have the same problems of mapping between the languages as I would in the multitude of browsers that we have to support these days.
Consider this test:
Scenario("Basic Lobbying", function() {
var context = new ManualContext()
, bob = null

Given("A server in a clean state", function(done) {
context.start(done)
})
When("bob connects", function(done) {
bob = context.add_client_called('bob', done)
})
Then("bob should have a canvas displayed", function() {
bob.should_have_element('canvas')
})
And("bob should be told he is the only one", function() {
bob.clientCount().should.include('only player')
})
And("bob should be waiting for other players", function() {
bob.isWaiting().should.equal(true)
})
And("bob shouldn't see the text input controls", function() {
bob.can_see_text_input().should.equal(false)
})
And("bob shouldn't see the paintbrush controls", function() {
bob.can_see_paintbrushes().should.equal(false)
})
And("bob shouldn't see the time left", function() {
bob.can_see_time_left().should.equal(false)
})
})

Now written in Coffeescript
Scenario "Basic Lobbying", ->
context = new ManualContext()
bob = null

Given "a server in a clean state", (done) ->
context.start done
When "bob connects", (done) ->
bob = context.add_client_called 'bob', done
Then "bob should have a canvas displayed", ->
bob.should_have_element('canvas')
And "bob should be told he is the only one", ->
bob.clientCount().should.include('only player')
And "bob should be waiting for other players", ->
bob.isWaiting().should.equal(true)
And "bob shouldn't see the text input controls", ->
bob.can_see_text_input().should.equal(false)
And "bob shouldn't see the paintbrush controls", ->
bob.can_see_paintbrushes().should.equal(false)
And "bob shouldn't see the time left", ->
bob.can_see_time_left().should.equal(false)

There isn’t a huge amount of difference, but I really value the lack of noise in Coffeesript-driven-tests and it saves from bothering with more hideous ways of doing things like Cucumber/etc.
Don’t use Coffeescript
Enough has been written about this in every single mailing list item that even dares to mention coffeescript since the dawn of time, but basically it turns out that JS is nice enough in most cases and Coffeescript is just a nice way of keeping things terse and learning JS. It can cause a lot of problems itself (usually around debugging – even though browsers are beginning to implement source-code maps, that’s not going to be universal) and you end up having to learn JS and map it to the CS in your head as you write it (at least to begin with)
Whatever, I advise you learn JS and perhaps allow CS for places where you feel terseness really adds a pile more value.
Manage your dependencies
Before getting into a conversation about module systems in Javascript, it needs pointing out that having a web of dependencies between your own code files is a bad thing. Like a lot of the pains in JS, the best way to avoid the pain is to avoid creating the pain in the first place.
For example, if you find yourself missing ‘re-factoring across the entire solution’, you should question why you need that in the first place, if you have libraries which are in use across in so many places across the code-base and they’re not stable and shipped as separate packages with a sensible backwards compatability story then you should be questioning this instead of falling back on tooling to cover up a pain.
Thinking in terms of components can be really useful, a component within your project should have a single task to do and should hopefully just expose a single object to do that task. It should ideally be standalone or at least declare its dependencies explicitly somewhere.
This is somewhat a double edged sword for Javascript, there is no in-built module/package system which means a dozen competing ways of managing code across an application now exist. That competition means that (hopefully) the community has got this whole thing pretty much nailed.
CommonJS
CommonJS seems a bit over-cooked (like most specs) if you go to read the standard itself, but given the most popular implementation of CommonJS isn’t even an implementation of CommonJS (the NodeJS implementation) there isn’t much point in doing that.
What it effectively boils down to is that rather than relying on code files sticking things into the global namespace, and a build process/include order that takes into account the dependencies, that the code files themselves declare what their dependencies are.
So, instead of
(function(exports) {
var privateVariable = null
function EventBus() {}
exports.EventBus = EventBus
})(window)

and
(function(exports) {
EventBus.publish('I am loaded now')
})(window)

And a build file that looks something like
cat eventbus.js consumer.js >> app.js

We can have
var privateVariable = null
var EventBus = function() {}
module.exports = EventBus

and
var EventBus = require('./eventbus')
EventBus.publish('I am loaded now')

And a build script that simply does
browserify consumer.js -o app.js

There are actually a dozen tools implementing this, that one is Browserify which you can get by having nodejs and running
npm install -g browserify

This however comes with caveats, the main one being that your debugging experience in the browser starts being a bit lame when errors no longer correspond to actual lines of code in the editor. (Having one giant file instead of all those little ones). The synchronous manner in which files are included in CommonJS-ish systems doesn’t lend itself to the web very well.
This is where AMD comes in
Asynchronous Module Definitions
The main implementation of this is requirejs, and our initial use of it will look very much like the above, but with a ‘bit more noise’
define(function() {
var privateVariable = null
var EventBus = function() {}
return EventBus
})

and
define([ './eventbus' ], function(EventBus) {
EventBus.publish('I am loaded')
})

The key visible difference here is that rather than require the files and assume they’re instantly present, we instead present the files we rely on, and say “When you have them, please execute my callback”
What this means is in the browser world, we can do the following without any build process
<script src="require.js" data-main="consumer"></script>

And the browser will download all of the original files individually.
In production, we can instead optimise by running a build command such as
r.js -o consumer out=app

And include that in a similar fashion
<script src="require.js" data-main="app"></script>

Therefore getting the advantage that having a single optimised and compiled file bring.
What I’m not going to do is say “use X” at this point, this really does depend on your application, whether you have an SPA, a website, a large project a small project etc. Just consider that if you have a build script that you need to manually keep in sync with your code dependencies that you might want to bring in *something* to help you.
Don’t fight Javascript
If you have code that looks like this
DefineNamespace('Company.Foo')
Company.Foo.Vegetable = DefineClass({
init: function() {
console.log('This is a ctor, honest')
}
})

Company.Foo.Courgette = DefineClass({
init: function() {
this.super()
console.log('This is also a ctor')
}
},
Company.Foo.Vegetable)

Then consider that this is not quite how things were intended to be. Namespaces are a holdover from the times we didn’t have decent module systems in JS (and from traditional enterprise languages like C#, Java, etc), and having weird classical inheritance simulations mean having to debug and trace through weird classical inheritance simulations.
var Vegetable = function() {
console.log('This is a ctor, honest')
}
var Courgette = function() {
Vegetable.call(this)
}
util.copyAllMethods(Vegetable.prototype, Courgette.prototype)

The only valid excuse for having weird other-language-simulations is if you’re writing a cross-compiler and this is code-genned code.
Pattern enforcing frameworks do not give you clean separation
MVVM,MVC, MV*, MUUUMY, leaving aside the bizarreness which is the enterprise developer’s love of data-binding, a reason oft-quoted for using a tool like Knockout is “it enforces separation between presentation and logic” and “it means our code will be organised and consistent”
We’ve heard this before in the Silverlight world (yes, I have done my time there, even recently – it pays the bills), MVVM gives us testability and re-use and separation and and and…
Then you look in the code and you have a 4000 line view model which nobody is actually testing because it’s too hard. Separation between presentation and logic is arbitrary and often unuseful, we’ve all heard the rants on that one (especially with respect to templating languages), seperation between logical units of functionality is much more useful and only happens if you’re listening to the code/tests/pain/etc
That’s not to say don’t use any of those things, but be mindful that you’re still going to have to work hard to keep your code meaningful and organised, and that’s not something you can download a library for.
Embrace the tool-chain
We can use nodejs – I don’t mean writing your application in nodejs, I mean installing nodejs and using some of the great tools that are available in NPM to drive your build and test processes. Just because you’re writing Java or .NET doesn’t mean you should restrict yourself to the often shoddy tools available in those platforms.
Already we’ve seen a few packages that can help us there (zombie, jshint, mocha, browserify, r.js), but there are a whole wealth of testing packages, asset management packages and other such tools that can form an essential part of your build chain when doing the JS front-end for an java/.NET backend.
Writing tests in Javascript to test Javascript and not needing to fire up a browser or go through contortions getting .NET to exercise the front-end has some real value and will save time, and hooking in the tool-chain to the usual build process means getting the best of both worlds in the right places.
Adding ponies will help too, but mostly concentrating on keeping things small, understandable and idiomatic (whatever that means) will keep your JS from becoming a write-only mess.










DIGITAL JUICE

No comments:

Post a Comment

Thank's!