Tuesday, August 10, 2010

I @#$%&* JavaScript!

I am by no means an expert web developer or particularly familiar with JavaScript, which might influence my distaste for it. My experience is mostly from making small changes in moderately complex existing applications.

JavaScript was originally intended as a small domain-specific glue-language to ad some client-side behavior to otherwise largely server-side web applications: do some client-side input validation, dynamically modify the page based on user input, etc. But with the growing popularity of AJAX style web-applications, much of the JavaScript client-side code has grown into monstrosities - mostly because of a lack of inherent support for modularity and encapsulation.

One of the most important properties a language environment should support in order to scale to large projects is a way to divide an conquer. There should be a way for one programmer to build upon the work of others without having to understand the implementation details of these building blocks which might be called libraries, modules, packages, interfaces, objects, widgets, components, etc. depending on the on the language. This should also include the ability to debug in the context and level of abstraction in which the code is being written.

For example, if an error occurs as the result of an API call, the error should be reported in terms of the API and the parameters passed through it and not just by a location in somebody elses low-level library code, where supposedly something has gone wrong, most likely because I make a mistake in an API call.

My experience with complex JavaScript libraries and frameworks (in particular the Closure library) is that the benefits one would expect to gain from using high-level libraries and components is greatly reduced, by having to debug and understand so much of the provided low-level code - just because the language system does not provide the necessary isolation. If the inclination is to write everything from scratch, so that at least I understand the code which I have to debug in my application, then the language environment has failed from a scaling point of view.

In this case, the blame not only lies with JavaScript as a language but as much with the ecosystem it lives in. De facto, JavaScript runs in a set of target environment, which are typically the most popular browsers (IE, Firefox, safari, chrome, etc.). The debugging support of these browsers is still severely lacking - even though things have gotten a lot better with things like the firebug extension to Firefox or JavaScript console built into Webkit. Part of the problem also comes from the fact that the typical JavaScript application is never self-contained but interacts with and depends on the DOM representation of a page in the browser and depends heavily on the quirks with which this particular browser renders the page and interprets the CSS attributes. The trickiest part of using a 3rd party JavaScript UI widget is often getting the right kind of CSS definitions loaded in the right order in the page where the JavaScript code is being executed.

Since rich AJAX style web applications are compelling and powerful to use, there will have to be a way out of this mess. JavaScript and its browser based ecosystem will either need to grow the features needed to support large scale software development, a higher-level language abstraction will be layered over it to make it more productive to programmers or a whole new web programming model will be created.

For an example of the second approach, GWT is an interesting step in the right direction. GWT is a compiler based approach, where an AJAX web application is written in Java and then compiled into JavaScript as the target for execution in the browser - relegating JavaScript to a kind of assembly or virtual machine language. The app can be partially debugged naively in Java. However because of browser quirks, much debugging is still required on the browser, where the abstraction breaks down again as it did for high-level languages before the existence of source-level debuggers: write code in a high-level language and debug the generated machine instructions.

Another advantage of this high-level compiled language approach is that the execution engine in the browsers is now only used by code generated by compilers and could be more easily optimized or even replaced by something new altogether simply by close collaboration between whoever builds the compilers and the JavaScript engines in the various leading browser platforms.

However there is another unfortunate issue with the GWT approach: while it can be scaled up pretty well to very complex AJAX web applications, it cannot be scaled down as easily to the simple tasks JavaScript was originally designed to do. This would leave a world where for simple things one would still use hand-written JavaScript and for complex applications a compiled AJAX framework like GWT. With an obvious discontinuity when a once simple application grows into a complex one and unfortunately, this is a pretty common case in real life.

Among the languages I commonly use, Python has the best properties of scalability from a software development point of view. It seems to be easily approachable by novice programmers and is now commonly used to teach a gentle introduction to programming for non-technical users. It is sufficiently high-level and low on framework-overhead to do simple things simply and easily and yet has just enough rigor and structure to scale to some pretty amazingly large-scale projects. It is an embeddable language and while it's standard distribution is quite a bit bigger than JavaScript, it would not be impossible to embed into a browser a library to manipulate the DOM. In fact some efforts to do that seem to exists. While Python was nowhere near as mature and proven in 1995 as it is today, one is left to wonder what would be the state of web programming, if Netscape had chosen to embed Python into its browser instead of JavaScript...


  1. While I entirely sympathise and agree with your complaints regarding the JavaScript ecosystem, I am not sure I understand why you think JavaScript lacks modularity (encapsulation is a thorny debate, at least for Perl programmers like me, but just as in Perl, there is enough in JavaScript, it seems to me, to achieve a decent bit of encapsulation, albeit without full access control). The advantages for Python over less strict/religious OO languages, again IMHO, will come down, for the most part, to one's training and preferences.

    IMHO the lack of well defined modules and APIs is not due to insurmountable language deficiencies, but because of the nature/history of web development. Which leads to my biggest gripe with any web development: the intermingling of logic and presentation, however much templating and MVC frameworks attempt to address this problem.

    Anyway, that's enough unsubstantiated opinion ;-). A small nit: the Javascript console/inspector is not Chrome, but Webkit.

  2. What do you make of Objective-J/Cappucino and the like?

  3. Ravi, you are right that my gripes are more with the current ecosystem of web client development, even though JavaScript as a language isn't exactly helping.

    My main problem is for projects which have 10s of thousands of lines of code split over hundreds of files, a bunch of third-party frameworks and widget sets thrown into the mix. Native support for modules, namespaces or even classes would surely help to make things better. Yes, you can do OO in JavaScript, but it feels a bit like doing the same in C or assembly - mostly by convention and very fragile to user errors. Structure enforced by language syntax would help for very large projects.

    In reality most large projects use some hacky ways of concatenating all the code plus dependencies into a few files, with all kinds of problems like namespace clashes and loosing any idea where a particular piece of code came from in the source-tree.

    Also error messages seem to typically be given in the simplistic "single-file" mentality, indicating just the line number where something went wrong. More module aware languages typically refer to errors by stack trace, assuming that the user might naturally not be aware of the code below a certain levels of the call stack.

    From what I can get skimming the Cappucino site, this is a high-level framework layered on top of JavaScript for web-app development. I think this is certainly the way to go, but in order to be a good "machine language", JavaScript will need to grow support for "source level" debugging for any of the frameworks layered on top of it.