Stop Writing Inhuman const

All code is for humans. In fact, the only value in higher-order programming languages is communication with other human beings.

Phrased as the reverse, code is not for computers. If code were for computers, binary would be the only acceptable programming language.

Code is for humans, as I have written about at length before.

We Forgot That Words Have Meanings

File this one as another of the tsunami of reasons I have to regret being a software engineer. Somewhere along the line, the siren-call of tech above all else washed away the plain truth that other human beings should come first, no matter what the tech is or how it makes you feel when you're writing the code.
Words mean things.
JavaScript's const is a shorthand keyword for the word "constant." Constant has a very specific meaning in the world: it is things that do not change.
Even if you're using const in a language where the variable is truly constant in every sense of the word (which JavaScript is not, in most cases), you unequivocally have other tools to store values, and you use them regularly. You use them regularly because the code you write is intended to communicate with the next person reading it.
The message the next reader consumes is either "this value is constant" or "this value is not constant." That's because "constant" is an English word that has a meaning, and any programming language using a keyword anything like const is leveraging the real English word to provide context and meaning to the next reader.

King var

var is shorthand for "variable." How beautifully succinct! How flexibly true!
For almost all practical purposes, and in almost all cases, a variable is what's desired, and a variable is what's being used. If you have an application where every single code path is purely deterministic in every sense: congratulations, you have a program with no data or user input. It is in this strange scenario that you might be forgiven for using const exclusively. After all, nothing can be dynamic if there is no input, and the return values from the function calls are already known ahead of time (if you're even using functions; what's the point of encapsulation and modularization when the entire program is hard coded?).
var should be your go-to, always-on, default.

Edgy let

JavaScript has a very powerful scoping paradigm called "Lexical Scoping." If that gives you unrest, I strongly encourage you to deeply read this book by Kyle Simpson. Lexical scope is basically just "scope you can read by looking at the source."
Some people think that lexical scoping is bad. Because of this, they petitioned to have two new types of variable declaration keywords added to JavaScript (ECMAScript). let is one of these. let uses "Block Scoping," or - basically - "every set of curly braces is a new scope."
There are essentially two situations where block scope becomes useful: when using arrow function expressions, and when writing loops. Using the former is itself inadvisable for many reasons, but arrow function expressions have a handful of extremely strong use cases. For these, block-scoped let variables are very useful. Similarly, writing loops is generally inadvisable, but in those situations when a loop is the best tool available, once again let's block scoping is invaluable.
Note what's being asserted here: if you need to send a particular message to the next reader, that's the time to opt into a new scoping mechanism. If the new scoping mechanism isn't necessary or if - as is the case in the vast majority of cases - lexical and block scoping are identical, sending a confusing message to the next reader is wrong.
The let keyword is a signaling mechanism to the next reader that there is a conflict between the scope on the current line, and the lexical scope it may inherit. let is a flashing red light with a klaxon that says "BLOCK SCOPING IS NEEDED HERE FOR SOME REASON! PAY ATTENTION!".
let is a for a small handful of edge cases, and only for these edge cases.

Rarified const

Finally we arrive at const. A shorthand for the English word "constant." There is almost nothing in programming that is constant. There's even less in JavaScript that is constant, because const isn't even the thinnest veneer of constancy at all.
Any use of const is explicitly a lie to future readers. There is no guarantee of constancy in any form.
That said, language is imprecise, and "constant" is a pretty well-understood word. It would be a shame to be unable to use it. We also have plenty of wise advice against ever modifying built-in objects (the only way to break constancy for boxed primitives) , so with our fingers crossed we can decide to use const for a small set of variable declarations.
The only acceptable time to tell future readers of your code that something is constant is when you can be reasonably sure that on a reasonable timescale (say, a few weeks) the thing in the variable won't be any different.
This is the definition of constant, and when the programming language trades on the familiarity of that word, it must also depend on the definition of it. Here are a few examples of constants:

const AVOGADROS_NUMBER = 6.02214076E23;
const PI_TO_TWENTY = 3.1415926535897932384;
const ONE_HOUR_IN_MS = 3600000;
const THE_BOSS_MAN_ID = 42;
const COMPANY_PHONE = "123-555-6789";

Remember: every character in code is a form of communication to the next reader. Therefore, the only valid time to communicate that something is constant is when the declared variable is holding something that's constant in the real world that the software is modeling.

So to recap our options for declaring a variable:

  1. var is king. Practically everything is variable. The default scope in JavaScript is lexical scope. Reach for var first to allow future readers to lean on that default and understand that you have no special case scenario.
  2. let is for those special cases. let is a caution sign at the side of the road for the next reader: "This code needed to opt into a special case of block scoping, so pay attention!" In general terms, you should only use let in loops and arrow function expressions.
  3. const is for telling the next reader that a value is a constant. It's for signaling that you want the reader to opt into a mental model that corresponds with the world around them, just like they always do for everything else. const says to the reader: "This is something you can understand just by reading what's 'on the tin.' It's a constant!"

It's by no stretch of the imagination a perfect measure, but this rule is very close: if you're placing anything other than a primitive String or a primitive Number on the right-hand side of a const declaration, you're writing inhuman code.

But What About Some Other Mental Model!?

A mental model other than the one everyone is using for everything, all the time? The one where everything seen - including source code - is interpreted through a human being's experience of the real world?
Indeed, let's put aside that mental model and - purely for the sake of the exercise - pretend that the standard human cognitive model may not apply.
I have been subjected to a number of explanations for why const should be used more frequently. Of these, the only two that made any internal sense were these:

  • const isn't about value mutability, so acting like the assignment to a const is a constant value is the real lie.
  • The value of const is signaling that you don't intend to re-assign the variable binding at a later time.

It's not about value mutability, it's about modeling the real world

The first of these is the most trivial: Indeed, practically nothing in JavaScript is immutable (with the exception of actual primitives, but even these are dynamically boxed and unboxed at any time, so it's very difficult to get a truly primitive value orphaned from any possibility of being a higher-order type). Because practically nothing is immutable, it's entirely not useful to think about const in terms of value mutability.
The way to think about const, like the way we should be thinking about all things, is: "What is the human who will be reading this next going to think when they see this?" We all use a litany of mental shortcuts and cognitive abbreviations to get through our lives, and const will always be expanded by the brain to "constant." It's just missing 3 letters!
The way to think about const is to model it after the real world that the software is modeling, and that the human reading the code lives in. This is the model for const, not value mutability.
Put more succinctly: The code is irrelevant! It's the people that matter!

Programming has never had - and should never have - an intent-based programming paradigm

The second of these explanations is far more nuanced, and as with all things more nuanced, makes me much angrier.
There is no place in software development for communicating some other-worldly future intent. Indeed, this tenet has been concrete in software for practically all the time that software has existed.
There is no mechanism to magically push an author's intent onto some poor reader's mental stack, because doing so is an absolutely horrific thing to do to another person. The reason software works is because readers don't need to maintain a mental registry of which intent was registered in which order, and whether that intent has been fulfilled yet.

const-infers-intent is practically unfalsifiable

Especially - especially - when there's no falsifiable statement for that intent. If const signals "intent to not re-assign the binding," the only way to determine if that intent is fulfilled is to read the entirety of the source code from top to bottom. Only then - and only at that moment - can you say the intent was fulfilled. Any changes to the source at all will obviate your verification completely and you'll have to start all over.
The only useful measure of whether a variable is intended to never be re-assigned is never re-assigning the variable.

The madness of const

There's zero concept of a pop-fly ball that will come crashing down into the code randomly at some later time.

The madness here is this: saying that const communicates intent implies that the wider world of development should introduce an entirely new mental paradigm: the intent-based program.
Nowhere in all of JavaScript (perhaps all of programming?) have we ever done anything remotely like this. Code is always about now. What is the code doing now. It's the only thing that matters. Even code that inherently deals with the future state of things - like Observables and Promises - does so by interacting with an object... now. A stream of data or a promise of a response is created immediately, and dealt with immediately. The author binds listeners to data entering the stream, or they handle different states of the promised response. In some cases, the author hands the response object off to some other set of code for it to handle.
In every case, some future behavior is dealt with right now in the code. There's zero concept of a pop-fly ball that will come crashing down into the code randomly at some later time. This is what the idea that const-as-intent implies. It's an entirely new and baffling mindset that expects a reader to ingest a line of code as "future intent" and then - at an unspecificed and unconnected moment later - recall the intent.
This is madness, and I refuse to acknowledge ideas as valid if they make software less readable, and less comprehendable. If intent-based programming is the future of our world, may God have mercy on our souls.

Once more, for the folks in the back

  1. The intent of the author is irrelevant. Programming doesn't deal with what your intent is or was.
  2. Value mutability is irrelevant. const should be used to match the mental model of constant things in the real world.
  3. var should be the default variable declaration choice.
  4. let should be used when certain special cases are encountered, like arrow function expressions and loops (use both with caution!). Use let to tell the next reader that block scoping is different from lexical scoping, and it's needed right here!
  5. const should be used to pull magic numbers (like IDs, range limits, special numbers, etc.) or repeatable primitive strings (like a phone number or a special phrase) out of the code. Anything "constant" in the real world can be const in code.