image: pixabay

Use “let” by default, not “const”

Re-assignment has been a long running debate in the programming world. In C++ there’s the “const” keyword, in Java there’s “final”, and in JavaScript recently with ES6 we now have “let” and “const”.

Let’s get the technical stuff out of the way to make sure we’re all on the same page. “const” and “let” are functionally similar in that they’re scoped to the nearest block and will throw a ReferenceError if accessed before their written declaration in the code (otherwise known as the Temporal Dead Zone). They’re intended to replace the more confusing “var” keyword which is hoisted to the nearest function and will not throw an error if accessed before its declaration in the code, instead it will have a value of undefined. “const” however adds one extra feature, in that once it’s assigned, it cannot be re-assigned again otherwise it will throw an error.

If you’re new to this, here’s a couple of exercises that will quickly explain the differences between these keywords. Run the following sample code snippet in your debugger, first using “var”, then using “let”, and finally using “const”.

(function () {
for (var i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000);
}
})();

And this one as well:

(function () {
var x = 10;
(function () {
console.log('Value is: '+ x);
var x = 20;
console.log('Value is now: '+ x);
})();
})();

If you’ve been working with ES6 code for a while, you’ve probably seen many different styles of writing ES6 code. One style in particular, is using “const” keyword for practically every variable declaration possible, while “let” is only used in cases where re-assignment is required. I use a different approach, I prefer to use “let” for everything, and reserve “const” for particular cases. This article will explain my reasoning as to why I use this approach.

“Const is expressive and saying that you don’t plan to re-assign.”

This is one of the first argument you’ll hear in favour of using “const” by default. There’s several problems with this statement though. It first of all implies that re-assignment is dangerous, completely unexpected, and should only be done in rare circumstances. There’s absolutely nothing wrong with re-assigning a value. Why in particular should this one operation be highlighted compared to more dangerous operations like modifying an object that was passed in as an argument to a function?

In regards to being expressive, it doesn’t actually tell me a lot if it’s used everywhere. I can easily tell if the value is re-assigned by reading the code itself. If the code is well written and the functions are small, it should be fairly obvious very quickly as to the purpose of a variable. However, what I cannot tell easily is if the value is important for other situations such as backwards compatibility. Is it dangerous for this value to be modified? Will it break something? What’s stopping me from changing it to a “let”? If everything is “const”, there’s nothing to indicate your true intentions for that variable.

While it is true that “const” is expressive, you diminish that value of that expressive behaviour by using it everywhere throughout your code.

Doesn’t stop references being modified

One of the most misleading things about “const”, is that if it’s referencing anything other than a primitive, you can still go ahead and modify that referenced object. Now for people who are fully aware of the behaviour of “const”, that’s normal and expected. However for less experienced developers that’s a problem. It’s very obscure when looking at “const” to think “Oh I know that object cannot be re-assigned, but it can still be modified”. To me it serves no benefits highlighting this one immutable aspect while the object itself is still perfectly mutable. It can very easily lead to unexpected side-effects which can introduce bugs. There are ways around this by using APIs such as Object.freeze if needed.

You should still use “const” if you need to prevent re-assignment of a complex object, but only if that reference needs to remain constant. If I don’t need that reference to remain constant, then I wouldn’t use “const”.

Time consuming using const by default

Aside from the fact you’re typing an additional 2 characters each time, you’re spending more time having to plan the future of your code. While I’m developing, I don’t want to have to think about the technical correctness of my code. Instead, I want to just get something working, and then go back over the code and make it more maintainable.

If I use “const” by default, I’m just opening myself up to frustrating situations. While I’m developing, I’ll probably re-assign at some point, get annoyed, and then swap that “const” out for a “let”. I can avoid those situations entirely by just using “let”. When I’m finished writing my code, is there any business benefit to reading over the code, and then swapping out instances of “let” for “const” if I didn’t re-assign them at any point? I would argue there’s none and that you’re wasting valuable time. Odds are that in the future, whoever is maintaining your code will have to swap out the “const” for “let” anyways, so it just makes maintenance more time-consuming.

I’ve seen many developers during code reviews leaving comments that look for perfection, but in the end, that only serves to delay the delivery of a project. The best approach is to ensure that the code is well-documented and thoroughly tested. That would provide more value, than ensuring the code is character perfect. My thoughts are the same when it comes to linting rules such as correct file extensions for JSX and JS files, is it really necessary? Will it cause a maintenance nightmare and lead to frustration? There has been plenty of times where I had to approve code that I wasn’t even close to happy with, but I knew that I could maintain it if necessary because it was still readable in the end.

Inconsistencies with function parameters.

Function parameters in JavaScript are re-assignable by default. Therefore, by using “const” inside a function, you’re purposely introducing inconsistent behaviour into your code. There are linting rules for check for this, but that’s something that you have to opt into and introduce additional tooling for, further complicating your workflow. And if you need to re-assign an argument for the sake of readable code, you’d have to workaround the tool.

There’s no real performance gains.

It’s a myth. There has never been sufficient evidence for this. Even in the scenario where there might be some marginal gain, I’ve never understood the need for micro-optimisations for the majority of web apps. As long as your app loads and responds in a reasonable timeframe for the user, it shouldn’t matter what you do inside your code. In either-case, if you’re reaching the levels of optimisations where browser implementations are starting to have a real effect, you’re probably better off using Web Assembly or using something closer to the metal instead.

Conclusion

In general, I rarely use “const” for declaring variables, because I fail to see the positive benefits of using it for variables that don’t need it and only see it being a hindrance. By preserving the use of “const” for important bindings, we more effectively communicate our intent to preserve that binding for anyone who has to maintain that code. Code styles are important, no doubt about that, but remember that they’re there to improve speed of development, spot potential bugs, and produce readable code.

Thanks for reading and comments are more than welcome!