What's the best way of writing readable javascript applications?

published Jul 29, 2015 03:11   by admin ( last modified Jul 29, 2015 03:11 )

Update 2014-08-02, see also these links:

Which take a Haskell/FP perspective on improvements

 

Understanding the code in a javascript application can be hard work. There are at least two reasons for that:

1. TIMTOWDI (There is more than one way to do it)

There's more than one way to do it, a term I believe originally coined by Perl creator Larry Wall. C++ and  Scala are also often said to suffer from different coding styles, while python does not. Javascript is by many felt as lacking a lot of features, but can be cajoled to emulate these features.

Different people have different preferences and decide to go about "fixing" these things in different ways. The result of this is that there are different ways of importing functionality, tracking dependencies, emulating a class base OO language and so on and so forth. All these extensions still rely on javascript syntax which makes them contrived in source code.

Even such a common solution as the object literal demands a lot of knowledge of the coder about anonymous functions, closures and so on to figure out what's going on. Also other ways of going about things become syntax-heavy:

(function() {
  this.Example || (this.Example = {});
  this.Example.Models = { Product: employee };
  this.Example.Collections = { Products: employees }

}).call(this);

On top of that there is a plethora of frameworks each one if their own idiosyncratic view of the world.

2. Javascript is asynchronous

"Callback hell" is a term often used for the default way of handling asynchronicity in javascript. It makes it hard to follow the program execution by reading the source code. There are other ways of handling asynchronicity in javascript, but then we are back to reason number 1 above: a plethora of solutions. Another aspect, put forth by my friend Mikael, is that javascript has exceptions, which doesn't sit very well with the structure of the language. I guess because it becomes quite unclear through what an exception is supposed to bubble up.

Ideas

What can one do in order to write javascript that is readable and understandable by another coder?

  • One way is to present upfront the unchangeable parts of the application, whatever they might be. Basically present the conceptual model for the implementation of the application.
  • Frameworks, as much as I am not fond of frameworks in general, do make this possible.  Frameworks suffer from that you need to lean them and then use their tools which often aren't as useful and universally applicable as the language underneath: You end up trying to screw in screws with a hammer.
  • One idea is to divide program flow into two categories: hierarchical and sequential, where the file indicates level of hierarchy. So there is a top file that deals with overall logic of the program, and then there are files that each contain code that carry out lower-level tasks. Ideally there should be no leakage of these abstractions: You should be able to rely on a file being its own universe, and ideally testable on its own. This is called a Multilayered architecture.
  • Another idea is to use some kind of actor model and disentagle callback hell into objects and messages.
  • Or write in another language altogether and transcompile it to javascript, hopefully with a source map.
  • Or use Dart or Typescript.
  • Typescript contains a lot of extensions to javascript on the language level (instead of on a framework level), which may make the syntax cleaner.

There is no publicly available code for tracing a node.js program line by line

Or at least I cannot find any. Jetbrains has acquired spy.js but that one is not open source. The node-inspector people seem less enthusiastic  Enhancement: Full trace of program execution · Issue #366 · node-inspector/node-inspector

There is something close to tracing though for node.js (and I have tested it):

(alpha) njstrace lets you easily instrument and trace you code, see all function calls, arguments, return values, as well as the time spent in each function.