Hopp til hovedinnhold

Teknologi / 4 minutter /

8 reasons I bash on JavaScript

There are thousands of blog posts with the theme “JavaScript is bad”. I aim to make this a bit different by adding a bit more constructive spin and propose another way.


Sacha Greif’s article about 21 Bad Front-End Habits to Drop in 2021 was an interesting read and created some good discussions in the internal front-end chat here at Kantega.

There was one habit that frankly hit me personally: Complaining About JavaScript.

Note: The following opinions are my own and does not represent the rich variety of opinions among the great IT consultants of Kantega.

To be honest, bashing a language is generally counterproductive and can even be a bit hurtful to those that enjoy working with JavaScript.

So why do I do it? Honestly, it feels good sometimes to act out violently at something that creates frustration 😁 Just kidding (kind of).

I’m not going to be too one-sided in this post. What brilliant developers has achieved with JavaScript the past 25 years is nothing short of amazing. We should all be in awe.

YouTube, Netflix, Facebook and Candy Crush are a few examples of complex and popular apps built on tons of JavaScript.

Honestly, most consumers are not even slightly bothered by something being built with JavaScript.

Still, I am guilty of being one of those people bashing on JavaScript on Twitter on occasion. I will shamelessly defend my position in this post.

Most feature-rich compilation target ever

My take on “bashing” JavaScript is not about me wanting JavaScript to vanish. Far from it. I just want as little JavaScript in my source code as possible.

If my front-end application demands just some HTML and CSS and 20–30 lines of JavaScript, I don’t mind some quick old JS.

But when it gets heavy on client side logic, I would rather build my front-end code in a more powerful language that compiles to JavaScript.

The solution lies in an alternative language, because JavaScript frameworks alone can’t rectify the downsides of JavaScript.

There are lots of exciting languages like Elm, PureScript, ReScript and ClojureScript that gives you a much nicer development surface, compiles to optimized JavaScript, and still allows using JavaScript as a lower level language for the special cases.

JavaScript is not the worst thing in the world, but the envy towards those who are daily writing in a more beautiful language is growing every day.

The opinions below explains some of the reasons why I rather want to compile to JS rather than write JS when writing complex front-end logic.

1. It’s just as easy to write crappy code as it is to write good code

You can write amazing and stable applications in JavaScript. That’s a plain fact with tons of undeniable proof to back it.

Powerful patterns such as pure functions, immutability, map/reduce/filter and even monads (Promises and async/await) are all great features of JavaScript. And with good test coverage, you’ll probably be fine.

The problem is that it’s just as easy to write crap, even for a highly skilled developer, so no one should feel bad about this ❤️

When pressed on time, every human will cut corners in JavaScript, because the easiest path is the natural path for a human being.

There are so many tutorials that will lead you on to bad habits. And such a feature rich language has traps at every corner.

JavaScript is built on some very good ideas and a few very bad ones.
Dougles Crockford (JavaScript: The Good Parts)

Even if you love JavaScript, please learn another language anyway if you can. It will make you a better JavaScript developer 💪

2. JavaScript is not null-safe, and that’s a source of eternal despair and financial loss

You can just google the “billion dollar mistake” if you haven’t heard of it.

When I set my brain into thinking how much money these types of errors costs businesses every day, every year, at the expense of creating value, the veins on my temporal lobe starts to throb at an alarming pace. It feels like trying to grasp the size of the universe.

undefined is not a function is a familiar line of wild nullishness in JavaScript land. The errors are often hard to debug.

It's natural to think “it is what it is” and it has to be this way, but it doesn’t. There are several great production-ready languages that will completely eliminate these types of problems. Yes. Completely eliminate.

A language like Elm can pretty much guarantee you won’t have any runtime exceptions in production, and one of many reasons for this is that values like null and undefined don't exist at all in the language. It’s being strict so you don’t have to.

PureScript and Kotlin are also examples of null-safe languages that will remove a considerable amount of silly bugs.

3. TypeScript is just halfway there in terms of type-safety

TypeScript is basically JavaScript with types. I use it everyday at work, and it truly makes life a bit better.

It’s a typed superset of JavaScript, which means that it adds some good stuff, but also holds on to all the bad stuff of JavaScript. Because it’s basically JavaScript, unsafe code is still easy to write.

Liberal use of escape hatches like the any type can also go a long way to completely undermine the advantages of a strict type system.

How ReScript differentiates itself from TypeScript by not being a superset is very well formulated:

TypeScript’s (admittedly noble) goal is to cover the entire JavaScript feature set and more. ReScript covers only a curated subset of JavaScript. For example, we emphasize plain data + functions over classes, clean pattern matching over fragile ifs and virtual dispatches, proper data modeling over string abuse, etc. JavaScript supersets will only grow larger over time; ReScript doesn't.

TypeScript has decent compile-time type-safety, which is great, but the runtime type-safety is non-existent, which still allows a bunch of bugs creeping into your app from external sources.

There are great libraries like purify-ts that can address these issues with the codec pattern, but it will always be an opt-in.

It also demands that you know when to use it, and deliberately choose to write it like that. That takes discipline and willpower, and a developer shouldn’t need to have these things 😄 Just kidding (kind of).

4. Node.js, npm, vulnerabilities, deprecations and more despair

The Node.js and npm ecosystem is so powerful and frictionless and let’s you hit the ground running with all kinds of magic spells, but it can also lead you to a pile of mess.

npm install will generate a massive dependency tree that can and will create problems. Breaking changes and security issues will smash your face in any time and without warning.

It's all in the hands of the individual developers, and there has been some nasty horror stories:

I have grown used to the package registry of Elm that has enforced semantic versioning so you will never hit a breaking change without being deliberate and never cause unintended side-effects. I feel confident that dependency hell wont’t hit me at all.

Since there can’t be hidden side-effects in Elm, stuff like npm audit is not really a thing, and it purges a bunch of both security-related and functionality-related issues from your JavaScript-fatigued mind.

I believe new shiny stuff like Deno and Snowpack are emerging with a new paradigm of bundling in JavaScript. Still I am still not confident that it will provide the same security and peace of mind, because JavaScript will always be JavaScript for other mentioned reasons. Still, I am very positive to these kinds of developments.

5. More powerful languages are better suited for making user interfaces for the web

React and other similar libraries has been an enormous innovation in the front-end development scene, but is JavaScript or TypeScript necessarily the best language pick?

Did you know you can write React in completely different and exciting compile-to-js langauges like PureScript, ClojureScript and ReScript?

In fact, ReasonML, which has evolved into ReScript, was created by the React creator Jordan Walke.

ReScript has first-class support for JSX, is fully statically typed and has pattern matching! It’s weird that this fact is so rarely mentioned in React themed podcasts as it’s a mature and safe language pretty much tailor made for React.

Elm, my clear favorite of the mentioned languages, was one of the main inspirations of both React and Redux. It really shaves down the amount of extra tooling you need to make great safe applications. You don’t need Babel, Typescript, React, Redux, webpack or any of that stuff because it’s all basically built-in language features.

The tree-shaking in Elm is insanely good. Almost no unused code gets shipped, so the bundles are tiny compared to React.

All these languages has interoperability with JavaScript (in different ways), which means you actually don’t need to miss out on the features of JavaScript, and bad practices are harder to do even when using JavaScript libraries.

6. No pattern matching

As soon as you start getting used to pattern matching in a mature functional language, you will intensely miss it when going back to JavaScript and cry bitter tears when being forced to write if-else.

The switch-expression comes somewhat close to pattern matching, but it doesn’t allow you to do deep pattern checks in the way you can with for example Elm:

Look how beautifully you can match patterns with complex variants in a type-safe way. Doing the same with if-else quickly becomes a mess.

7. JavaScript can’t handle the truth

Truthiness checks is a common pattern in dynamic languages and a common practice.

One problem is that truthiness is inconsistently implemented across all these languages. In JavaScript, 0 is for example a falsy value, so code like this could potentially be a pain to track down in a large codebase.

In Elixir, 0 is a truthy value. In Python, 0 is falsy like JS. Empty arrays or objects are on the other hand falsy in Python, and the oposite in JS.

To be fair to Elixir, only false and nil is falsy. That makes perfectly sense. But this concept of truthiness is in my opinion broken in other dynamic languages and can not be repaired. You need a reference card to remember how it works in every language.

If JavaScript would make their truthy checks right, it would break millions of apps, so there is no return from it. This is a source of creeping bugs and adds to the “billion dollar mistake” in my opinion.

To be honest, I use truthiness checks in JavaScript all the time without worrying to much because I am accustomed to most of the quirks. I still think it’s inherently bad.

Just remember to favor === over == of course.

8. JavaScript is very accessible for beginners, but requires expert architectural knowledge for a complex project

This is kind of a dilemma.

JavaScript is maybe the best language for a beginner to learn, but when it reaches complexity, it’s a whole other story. This is because of the great freedom you get with JavaScript.

When you make something powerful, it also requires great responsibility.

I think Kent C. Dodds hits the spot on my point with the pitch of his React course:

Building React applications demands that you make expert decisions before you write the first line of code. You’re responsible for building a cohesive, maintainable code-base that will help your team succeed and build a React application that serves your customers’ needs. You’re faced with hundreds of decisions. If you don’t choose correctly users will suffer. Your team will suffer. You’ll be stuck with your early bad decisions for the next 5 years at a minimum.

In my opinion, that is primarily not the fault of React, but mostly the fault of JavaScript. How can you really expect to get everything right from the beginning?

JavaScript gets insanely hard to refactor when you have a big application. TypeScript makes it easier, but many typed functional programming languages turns big refactors into a trivial todo-list of changes served by the compiler.

Fearless refactoring, less mental overhead and far less decisions to make is on the other hand what you will get with typed functional languages.

Bottom line

In conclusion, I think a better language will provide more peace of mind, a superior developer experience, increased maintainability, less bugs and a new insight into new programming patterns that will considerably widen your horizons as a developer.

Balancing it out: Some drawbacks of non-mainstream languages

To balance out my bias a bit, I will also highlight some possible pitfalls of niche languages that are not yet mainstream.

  • There is a learning curve and therefore both a cost and a risk going for a new language in your company. PureScript and Elm can be a challenge to learn if you are used to the JavaScript mindset. Be a positive advocate, and try not to shame those that don’t see it the same way even though you are excited.
  • Learning resources are not as plentiful as in JavaScript, so again, having an advocate, even when the team motivation is on top off is really valuable.
  • The developer pool is smaller for recruiters, but Elm-based companies actually claim that they get really passionate skilled developers knocking at their doors wanting to work with Elm, so there are also potential upsides.
  • A more responsible language could demand more from you initially, at the reward of demanding less of you later on.

It’s great on the other side of JavaScript, come join the dark side :)

Are you interested in some of the mentioned languages? Have some fun with one of them in a side project and seek out the communities. Most of them have a Slack with plentiful of passionate advocates to help you out when you’re stuck 😊

Some exiting front-end languages to explore: