back to my site

javascript and me

2016.08.04

The following began as an essay on the dangers of leaky abstractions, but it quickly (and perhaps predictably) devolved into an essay about my feels on Javascript after years of writing it.

For some time, I primarily wrote Javascript. I first learned the language around 2007 while doing a workstudy programming job for my college. It quickly became apparent that there were some rough edges and that writing Javascript was challenging. Back then, however, the problem was less that Javascript is a poorly designed language and more that at that time the differences between browsers were still quite vast. Writing portable Javascript was extremely difficult.

Enter frameworks, who promised to smooth all of those cross browser gaps for you and give you some nice helpers, too. At the time, I found myself choosing between jQuery and Prototype.js.

Having promptly assured myself that Prototype was the future of Javascript development (ironically, I would have been wrong either way, but jQuery soon eclipsed Prototype in popularity and still does), I proceeded to delight myself writing AJAX-y, shiny, spinny things without that old dinosaur the backend getting in the way (I was young).

These frameworks enabled us to write a bunch more Javascript, and before long (primarily thanks to gmail), everything had to be a Javascripty AJAX filled "web app" in order to be taken seriously.

Around this time, people started to notice Javascript more. With the legitimization of, at the time, the "do no evil" Google, it was suddenly in vogue to care about Javascript. This brought many avant garde programmers convinced that all of the problems they ran into with coding could be solved with this new shiny bullet.

It was in this climate that node.js came about. At the time, I remember trying to get my head around Twisted and Tornado, two Python libraries for high performance network programming. I didn't have a good reason for this. It was hard. Twisted is very confusing and Tornado, at the time, was barely maintained experimental software.

I'll never forget the code snippet that led me down the path of writing almost nothing but Javascript for two years. It was the few lines of Javascript required to write a simple socket server. Immediately, my brain fizzled and popped with all of the cool networking stuff I could do, and I started the pernicious process of writing more Javascript than I did Python, a language that had served me beautifully for years.

node.js has one strength: it can teach you network programming in a really easy and fun way. I learned the hard way in the coming years that just about everything else was really hard.

At this point, I was deep into Javascript internals at my job and writing heaps of node.js for side projects at home. For work, I found myself optimizing advertisement delivery for a large media conglomerate. I'm not proud of this, and had no idea that that is what I'd end up doing when I took a job there. In a cold and detached way I'm proud of what I did to their ads: I took sites in crisis taking 30+ seconds to fully render some 19 ads onto a page and made them take 6 seconds.

This required learning exactly how browsers rendered HTML, how Javascript executes, how ads worked, and how to do things as quickly as technically possible in Javascript-and Firefox, Chrome, Safari, and IE 7-9. jQuery couldn't help me here. Nothing could save me.

Meanwhile, at home, I was realizing the limits of node.js. I found that every program of a sufficient length became unreadable and increasingly hard to debug. I found myself completely stuck when I found myself CPU bound. I had no idea how to tickle node.js in such a way that it would use as little RAM as possible. Testing was difficult and the ecosystem was becoming increasingly fragile (the seeds of left pad were planted long ago).

I continued to tell myself that Javascript really wasn't bad at all, just misunderstood. A fresh coat of paint would do wonders for it. I started playing with Coffeescript.

Coffeescript was neither the first (thanks, Objective-J) nor the last (it could have been beautiful, Clojurescript) "compile to Javascript" language I used. I wrote the most code in it, though, and had the most conflicted feelings about it. On the one hand, it took basic sharp edges of Javascript like anonymous function declaration and scope binding and made them smoother than ganache. On the other hand, it was completely nonsensical without a deep understanding of the Javascript language, was a nightmare to debug (source maps didn't even exist yet, not that they help enough in 2016), and had plenty of language warts itself.

As a combination aside and horror story I'll mention that I was (re)learning Haskell around when I started using Coffeescript heavily, producing this mutant atrocity.

Ultimately, I found myself with a deep understanding of how a thing worked (Javascript, browsers) and a day to day experience with software that let me down constantly (Javascript, Coffeescript, node.js, npm). My brain, instead of saying "Maybe no?" said "I can fix this." Now, every project I started devolved into "but what if I write this library?" or "what if *this* could compile to Javascript?" My side projects were dead before they started and my entire creative life became fixated on how to improve Javascript.

At this point it's getting close to 2013. Things have culturally plummeted at work and I find myself looking for a new job. Through luck and privilege, I get a job at a startup and am offered the chance to go back to backend programming. I was starting to fatigue writing Javascript and accepted eagerly.

Within a few months of not writing tons of Javascript I realized how miserable I had gotten with programming. I was ashamed of so many unfinished projects outside of pointless or small helper libraries for node.js. Every project went like this:

        idea
        -> sketch some docs
        -> stub out with JS
        -> get really angry at js
        -> obsess over how to fix js
        -> give up on project
        

and I wanted nothing more to do with it. I swore off Javascript.

In 2013 the Javascript world was salivating at anything to replace Backbone.js (I, in continuing to pick unpopular things, had been using sammy.js for all my client-side stuff). Ember and Angular arrived to rush in an even more intoxicated era of Javascript development than jQuery and its ilk did. I looked upon these libraries from the sidelines, no longer writing Javascript for "fun" nor profit. I saw the same things I saw in prototype, jQuery, Coffeescript: a bunch of clever people trying desperately to make Javascript easier to use.

At my previous job, I saw the worst that I thought the Javascript world had to offer. I reverse-engineered ads delivered via document.write that in turn delivered payloads of flash. I refactored countless lines of code cobbled together from Hot Scripts snippets. I sat, working the weekend, on an old iPad figuring out exactly why ads were loading 2 seconds too slow there using an emergency hand-written debugger. I sweated in front of old, virus choked communal office windows machines to figure out mystery exceptions in IE7.

I had not just looked into Javascript hell; I lived it and survived. I've since looked at Javascript development today; it's worse.

No matter how broken or upsetting the rotted advertisement innards were, there was solid ground under there. There was a language that executed on a virtual machine and I could understand how it would be parsed on the VM's various implementations. I could reason about its performance and what it did. Modern Javascript programmers don't appear to ever have this luxury. They have a stack of abstractions so deep that every debugging attempt involves understanding the quirks and edge cases of at least five different pieces of software (on a good day).

You start at the browser (still dealing with the occasional cross-platform difference and its solution du jour, polyfills), move to whoever generated your source maps, then your ES6 compiler, then your MSX/JSX compiler, then maybe an emberscript compiler, then maybe your asset pipeline helper, then your Javascript build tool...

I will never, ever advocate that programmers need to learn assembly programming. However, my experience with Javascript has been bizarre: even when my Javascript career seemed at its darkest, learning and understanding raw Javascript and how to make it function properly and performantly was never as painful as my attempts to get caught up to modern Javascript development.

I haven't written this essay to hate on Javascript. Nothing I've said here about my experience with Javascript is unique. I experienced the exact same arc with Perl and I'm sure there are myriad other examples of this kind of culture.

Programming is ultimately a creative discipline. Unlike other creative disciplines, it is recursive: the media we use (programming languages, compilers, interpreters) are themselves programs. There is always the risk that we slip into circular abysses of our own making and obsess over improving the very tools we use to create our art.

I'm not saying that we should stop working on innovative new compilers. Instead, I'm saying that we don't all need to be thinking about compilers. My dream is that programmers can follow their heart into whatever creative endeavor they believe in without being undone by their own tools.