news and interesting stuff from the codebender team

Main menu

[Developer says] Making assumptions explicit in Javascript

Javascript is a language that is, by design, difficult to debug. It is dynamically typed, it is based around asynchronous callbacks, almost all data types are mutable, almost no functions of the standard library are pure, and the list goes on. And these are not just flaws in the design; they are what makes the language what it is. It is therefore important to be equipped with the correct tools when setting out to correct your code. I am not talking about fancy debuggers and static code analyzers, just two very simple libraries. So simple in fact they do not even qualify as separate libraries. They are just a couple of hundreds of locs that you can copy right into your project and modify to your needs. My sample implementation for each is provided.

For most programmers it goes without saying that you should always keep your types in check. In javascript however that does not come automatically, you need to do it by hand. Of course humans are very bad at keeping track of anything so it’s necessary to find some way of tracking (or at least asserting) the types automatically, mostly so that when your code fails, it fails as soon as possible after something unexpected happens.

When designing the mechanism for type assertions we also want to promote two programming philosophies:

Favoring function composition to global state

And keeping functions as concise as possible, always preferring short code to a short stack.

For these two reasons we will make it the easiest to check function argument types, rather than generalizing. Also note that we assume that functions will much more often accept callbacks as arguments instead of returning. For these reasons we provide exactly two functions that will check types at runtime:

typecheck(arguments, ['number', 'function']);

Will throw an error if the arguments list is not a two element array containing a number and a function. The other method for this is typechecked:

typechecked(fn, ['number', 'function']);

This will return a wrapped function fn with exactly the above check.

This method, while making runtime checks rather than giving compile time guarantees, not only helps immensely in quickly finding the place of the error but also serves as code documentation. Let’s look at an example. Say we have this code:

Asserting that the types of your function’s arguments are what you assume they are can save a long time tracking down bugs but that is not the only pain when debugging javascript. The second most common source of errors, in my experience, is asynchronous closures being called in an unexpected order. Like most problems in life there is no perfect solution but we can do better than simply hoping things happen in the right order.

But what if the call queue is really, really busy and c is already in the queue when a is called? c will be called before b. This is a very contrived race condition and you will probably not catch it even in your tests without randomizing the scheduler. However, you can try to catch close calls by explicitly declaring your assumptions about the order in which code is executed, either in the code itself, or from the tests.

To do this we created checkpoints , which is also not extensive or complex enough to qualify as a separate package. The premise is simple: Each checkpoint is denoted by an integer and a stateful object (the CheckpointManager) throws when the checkpoints are called out of order. For example:

Conclusion

With javascript it is really easy to keep your assumptions about your code implicit, thereby hurting readability and allowing space for bugs. The point of this article is not so much to show off “2 cool ways of debugging javscript” code but to make a point about how a programmer should strive to make it easy and mandatory for themselves to express and verify even the most basic assumptions about their code and the problem they are solving. This is especially the ones that do not show up on the whiteboard.