The Javascript-Industrial Complex

Prelude

There’s a Djikstra quote that goes like

“It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration.”

This is a bit of hyperbole, to be fair. But what is really being said here?

History has shown us that BASIC (of the time) really was not a very good programming language, and it was holding the industry back. Djikstra also (earlier) promoted the idea of “structured programming”, also writing an essay in 1968 titled “Goto Statement Consider Harmful”, which helped promote into industry many of the paradigms that we have in programming languages today that were already popular in academia with languages like early Lisps.

Every idea we have that we want to share needs to be communicated in some kind of language. Usually, these are spoken languages (which often have texual representations), although they can be expressed in other media such as art, music, dance, symbols, or other kinds of expression. The languages that we speak/use influence the ways we express and relate to ideas. Programming languages are no different, the languages we use to express concepts shape the ways that we are able to express them. Some media are better-suited for expressing certain ideas than others: it would be challenging to try to explain general relativity through dance, but song is well-suited for expressing grief over a breakup.

What Dijkstra is (in my opinion, though not only mine) trying to say here is that BASIC programmers learn to express their ideas in terms of BASIC’s constructs. BASIC is in part the way it is because the constructs it exposes to the user are fairly easy for anexperienced programmer to implement in assembly, which early BASIC implementations were. These constructs are also easy to teach to new and young programmers, which is why the B in BASIC is for beginners’. But these constructs are not very robust. It’s impractical to develop useful abstractions over behavior and data in order to compose and reuse code, which is an ability taken without question in even very limited kinds of modern languages. So instead of using better techniques, BASIC programmers are forced into other ways of programming (often very un-DRY ways) and learn to express their ideas in the unrobust and error-prone styles. Even when you go to more sophisticated programming languages, you’re already in the mindset to express ideas in terms of BASIC and it shapes the code you write even without you realizing it. When computers were simpler and there was less “stuff” to go around, this wasn’t a huge problem, but let’s put a pin in that for now.

Industry eventually realized that BASIC was a terrible programming language for writing complex applications in. Of course you can do it, but it’s not easy. Go read the Djikstra paper on GOTO (or any analyses on it), honestly, that covers it better than I could in this (or want to). BASIC gave way to the “structured programming” and for a while C was in vogue, then people started wanting to think that object-orientation was the way to go. Objects helped a lot with modelling complex application design problems with large teams, but we’re starting to see lots of people dislike them and give some veeery compelling reasons for it. Meanwhile functional programming languages were over there in the ivory tower having a good time inventing all the cool stuff that industry would go and adopt later.

// TODO workshop ^that section a bit

Part 1 - Conception

I’m just gonna copy a snippet from the Wikipedia article in here:

During these formative [1993-1995] years of the Web, web pages could only be static, lacking the capability for dynamic behavior after the page was loaded in the browser. There was a desire in the burgeoning web development scene to remove this limitation, so in 1995, Netscape decided to add a scripting language to Navigator. They pursued two routes to achieve this: collaborating with Sun Microsystems to embed the Java programming language, while also hiring Brendan Eich to embed the Scheme language.

Netscape management soon decided that the best option was for Eich to devise a new language, with syntax similar to Java and less like Scheme or other extant scripting languages. Although the new language and its interpreter implementation were called LiveScript when first shipped as part of a Navigator beta in September 1995, the name was changed to JavaScript for the official release in December.

Did anybody catch that? They initially wanted to use Scheme, the Lisp derivative. That could have been the world we live in. But Netscape’s management wanted the developers developers developers as opposed to a better technology, preferring a technically unsound path in the interest of perspective profits. But let’s put another pin in that.

Low-level Technical Critisisms

I don’t want to go too deep into the criticisms of JavaScript, there’s countless more detailed criticisms of that. But I think it’s useful for framing so I’ll go over the major red flags.

JavaScript is weakly typed and does type juggling. The two main kinds of data structures are arrays (basic stuff) and objects, which are key-value mappings from strings to ther values. Accessing fields of objects that don’t exist don’t return hard errors, they return magic undefined values that are distinct from null. The classes that ES6 have are basically just syntactic sugar over this object system, and use a prototype system in order to do inheritance.

Due to all the type juggling and the way the language is usually used, JavaScript naively implemented is pretty slow. But since so much of the modern web relies on it, runtimes have extreme memory footprints and resort to lots of fun tricks in order to squeze performance out of code. This comes at the cost of massively increasing the attack surface for exploits like sandbox escapes. And V8 can still be pretty slow, even with the massively increased footprint (using GCC C++ here because it’s a good baseline for “optimal”).

Speed isn’t everything of course, but what people do forget is that end users do care about UI latency. Apple puts a lot of effort into ensuring its UI toolkits (especially on mobile devices) have smooth 60 FPS animations. The undue memory footprint is a contributor to this because it also makes cache misses more common and on lower-end hardware this can force swapping, which is so slow for anything UI-related even with SSDs.

Part 2 - Some Justifications

Scripting languages vs “other” languages

I hear a lot that “JavaScript is a scripting language! It’s supposed to have dynamic types!”, which is true. But Python shows that you don’t have to go that far with wibbly-wobbly semantics in order to be useful. Python makes a distinction between objects and dictionaries, as objects do a lot more and have semantics you’d associate with being instantiated from classes, and dicts (which are like JS’s objects) are just for grouping data.

// TODO extend

Modern toolchains for JavaScript usually use linters and extensive testing to ensure that bad behavior doesn’t occur. All that is what static types are supposed to handle. So they went and invented TypeScript, which to be fair is a big improvement, but since the types are very bolted-on and it needs to interoperate cleanly with a largely untyped ecosystem, the benefits that can be gotten out of it are more limited than they could have been. TypeScript also still suffers from many of the other issues I describe in later sections, and it can hamper the optimizations that V8 tries to do so in some cases ends up being slower than untyped JS.

Flexibility and ease of refactoring

(I realize this section reads a bit like just putting up my side of a holy war, but I’m including it here for completeness’ sake.)

This is really a flawed argument and people who make this often have never used a statically typed language for a serious project. Static types make refactoring easy. With a dynamic types (especially one as loopy as JS) the programmer keeps many invisible invariants about how the code works in their head. These may be written in documentation (or other stuff for linters perhaps), but there’s no runtime/compiler that can identify if an invariant has been violated. Static type systems when used effectively (even less-sophisticated ones) give the programmer a massive benefit here because it allows them to immediately eliminate entire classes of errors like type confusion.

Linters and really extensive testing infrastructure are a way to gain back some of the confidence that’s lost by giving up static types. But linters are heuristical and can’t catch every case. Using tests to verify that functions/etc behave correctly when given the right types and don’t break in horrible ways when given the wrong ones is the biggest waste of time ever. That’s what type systems prevent. And that doens’t account for situations with more sophisticated type systems allow you to model data structures in ways that completely prevent the programmer from doing the wrong thing. It’s a really common experience even for intermediate Rust programmers to find “if it compiles, it probably works”.

There’s also a surprising number of people around that like, don’t know about type inferencing? Any serious language these days will have type inferencing so you don’t have to worry about being explicit everywhere. With Go becoming more popular I suppose this is less of an issue (although Go sucks and you shouldn’t use it, either). Even Java has a limited form of type inference now. Rust without type inference would be abominable due to the use of iterator/future combinators that are so common.

Part 3 - Manifestations: A Case Study

There’s this federated chat protocol called Matrix. It’s pretty cool, I use it every day, it even supports end-to-end encryption. But it kinda sucks. The reference desktop client, Element, is a React app in Electron, which like sure is neat because you can run it in the browser. But the only people I know that do that only do that because they don’t want to run another web browser to use the client. There exists other clients including the

So let’s take a look at a few things from the protocol spec that are a result of the developers being prompted to see things all in terms of the web.

// TODO

Part 4 - HTML5 & ES6

(I wasn’t really sure how to best place this section.

Internet Explorer didn’t properly implement the same standards as the rest of the world for a long time because of Microsoft’s legacy from the 90s and early 00s. (I’ve talked about this pattern of behavior before.) So because of this it was really hard and expensive to build sophisticated webapps, so they shifted a lot of effort onto desktop software.

So at some point Microsoft finally said fuck it and made Chakra for the next generation of IE that supported all the standard things that developers expected from Firefox/Chrome/Safari/etc., so it became way easier for devs to build things on it. Around this time JS was going through a standardization and expansion period around when ES6 (the technical standard for JS is “ECMAScript”, because of historical reasons) made it actually useful, and polyfills became a thing to rewrite use of newer features in terms of old ones.

Part 5 - Systemic Effects

This is the part where now that I’ve set the stage, we can get to the main argument.

“The death of Internet Explorer and its consequences have been a disaster for the human race.”

(Really a more accurate version of this quote would have been with the web as a whole maybe? But I couldn’t resist.)

// TODO all the shit

So now we’re going into this cycle of requiring more memory and compute on our devices to run UIs that are slower and work less well in numerous metrics than UIs a decade ago. That’s not accessible and when you write software like this, that says to people who can’t afford to continually upgrade to modern hardware is that they don’t deserve to run the software that you write. If your goal in the software you write is to benefit people and improve their lives, then you’re doing a bad job.


Articles Index