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.
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.
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
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.
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.
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.
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.
Finally we arrive at
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:
varfirst to allow future readers to lean on that default and understand that you have no special case scenario.
letis for those special cases.
letis 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
letin loops and arrow function expressions.
constis 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.
constsays 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.
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:
constisn't about value mutability, so acting like the assignment to a
constis a constant value is the real lie.
constis signaling that you don't intend to re-assign the variable binding at a later time.
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!
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.
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.
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.
constshould be used to match the mental model of constant things in the real world.
varshould be the default variable declaration choice.
letshould be used when certain special cases are encountered, like arrow function expressions and loops (use both with caution!). Use
letto tell the next reader that block scoping is different from lexical scoping, and it's needed right here!
constshould 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