A fucking rant about fucking const vs fucking let

I didn't want to be the guy write this post, but I am because Chris told me to so blame that fuckin guy. I'm a victim here.

But you're all using const in JavaScript too much and it does bug me just ever so little.

1. const doesn't do shit and we all know it

For starters, let's agree that const is fucking useless and we're inventing meaning...

const THIS_SHOULDNT_CHANGE = { right: "?" };
THIS_SHOULDNT_CHANGE.right = "wrong";
THIS_SHOULDNT_CHANGE.fucking = "deal with it";

const does not imply any kind of immutability of the value itself, it only implies immutability of the binding.

Which... Cool.

2. The linters can't save you now

Let's also eliminate the idea that we could somehow statically enforce that const values are immutable with a linter.

Like, sure, you could for the simple case:

const FOO = { prop: true };
FOO.prop = false; // LINT ERR >XD

But then like... side effects exist and shit will hurt you

export function mutatesProp(obj) {
  obj.prop = false;
}
import { mutatesProp } from './mutatesProp';
const FOO = { prop: true };
mutatesProp(FOO); // no lint err :(

And yeah, type systems like Flow and HiHaveYouTriedTypeScriptYouShouldReallyTryTypeScriptIWriteTypeScriptAndImFuckingObnoxiousImGonnaStickMyHeadInAMicrowaveAndScreamTypeScriptâ„¢ could do something with this, but like... they don't... sooooo

3. No it's not useful for compilers

You might be thinking, "Oh but it can still be used to optimize code or whatever"

You would be right in thinking that compiler optimizations would make it interesting enough to use. But...

You would be wrong in thinking that const is at all useful to optimizing compilers.

It's already really really easy to identify a binding and figure out if it's never reassigned. All of the relevant optimizations are already possible without const ever being a thing.

Also, V8 and friends have been doing these optimizations without const for a really long time. Benedikt or Franziska or whoever can correct me if I'm wrong, but Babel definitely can already do this shit.

4. So what are we going to do?

First off, like... we don't have to do anything. There is no reason you would ever have to use const, and whereever we are using it, we could just as easily use let and our code wouldn't be impacted in any way.

Honestly, if you just want to stop here and never use const again and never question it further, you will save literally whole... minutes of your life.

But like... we have const so why don't we use it for something ...right? We could like use it to communicate.

Sure.

5. But what we've chosen to communicate is annoying as fuck

So, lots of people (okay maybe not you, you special little shit) are doing this... thing where they change every let variable which is never re-assigned to const.

let answer = 42;
neverChangeTheAnswer(answer);
justKeepDoingOtherStuffWithTheValue(answer);

They would see the above code and say "that let should be a const so that the reader knows at the declaration of answer that it never changes."

And many of them will include linter tools for enforcing that rule on every variable declaration, even sometimes applying the "fix" automatically.

const answer = 42;
neverChangeTheAnswer(answer);
justKeepDoingOtherStuffWithTheValue(answer);

"That's better."

But... is it better?

Did you really mean to communicate that we should never change the answer or do we just happen to never change it?

What if we came along later and decided we are okay changing the answer? But now it's a const and we aren't really sure if we're going to break someone's expectation for answer if we do change it.

There are lots of bindings that we happen to never change that we're totally fine if they do change. In fact, that's probably true for most bindings. It's not something your code will generally care about.

But by automatically applying this rule to every binding no matter what the author's actual intent was, we actually throw out our opportunity to communicate something.

6. So what should we actually use const for?

It's probably still a good idea to communicate that you really don't intend for something to be changed.

But in order to communicate that, we need to only use it when appropriate.

const I_ACTUALLY_GIVE_A_SHIT_IF_THIS_CHANGES = 42;

If you want some good rules to follow, here's my best attempt:

1. Only use const at the top level scope

const HERE = "is okay";

function nested() {
  const NOT_HERE = "never seen a reason to";
}

I'm pretty sure I've never seen a binding created in a function that will actually be upset if it changes. I never use const inside nested scopes, and I don't intend to. Why? Because my mom raised me right (see my usage of semicolons), that's why.

2. Avoid using let at the top level scope

On the other hand, if you need to use let at the top-level scope, that's a sign you have some sort of global singleton state which could cause you problems.

let globalState = "hmmm...";

function fn() {
  globalState = "this seems fragile to test...";
}

And like... that's basically it...

And for the love of all that is not shit, please don't try enforcing this with a linter. Let people communicate what they want to communicate. Ya'll drive me crazy with that shit.

Now get the fuck out of my house.