And a one, and a two, and a one-two-three-four… good code is like rock ‘n’ roll. It just rolls out nice and easy, with a simple but catchy rhythm, it’s logical and tightly structured but never boring. You can’t help but smile when you’re under its spell. I guess it boils down to both being a form of expressing our understanding of that most basic mathematical order that makes our universe tick.
So that’s what we’ll go over in this one – rock ‘n’ roll, JavaScript, and a tiny bit of entropy to make things more interesting.
Let’s start with that entropy thing. It’s the inherent drive of things to reduce themselves to the space dust they were made of. Entropy is relentless, and it has power over everything and everyone. Well, almost everything. I’m pretty sure rock ‘n’ roll is safe. “Paint it black” will make people feel things as long as we exist. Play “Get out of Denver” and try to stay perfectly still for the entire song. I dare you!
Everything else ages and decays, though. Everything needs some kind of maintenance, otherwise, it falls victim to the all-powerful entropy. And that includes JavaScript code. The thing is, a moment comes when maintenance is no longer an option worth considering, and whole functionalities are getting re-written from scratch, costing much more time and effort. But, following a few good practices here and there, we can at least delay that moment.
I don’t think we can possibly fit all good practices in terms of maintainability in one article, so let’s start with the basics.
Functions
We all know it’s possible to just lay our code out in a single script. So, when would it be a good time to encapsulate a piece of it in its own block?
Well, there’s a simple principle in all kinds of programming, not just JS: “Don’t repeat yourself”. So, for any piece of code that you meet twice or more in a single script, you’re perfectly justified to export in a function and then call it when needed. The same principle is valid on a larger scale, for example, in ServiceNow environments – if you have the same logic in more than one script, maybe it’s a good idea to export it to Script Include and just call that one when needed.
There’s also another situation that requires exporting code to another module or, in ServiceNow – to a Script Include. It’s to avoid the notoriously bad practice of blindly relying on JS hoisting and calling function declarations before defining them. Here’s what I mean:
var myVeryOwnFunctionExpression = function(someArgument) {
return someArgument;
}
This is a function expression. It can only be used after we’ve reached these lines of code in our script where it was defined. So, this won’t work:
myVeryOwnFunctionExpression();
var myVeryOwnFunctionExpression = function(someArgument) {
return someArgument;
}
It will return an error, as it should. This, however, works fine:
myVeryOwnFunctionDeclaration();
function myVeryOwnFunctionDeclaration(someArgument) {
return someArgument;
}
It’s a function declaration, and it gets loaded by the compiler before any code is executed. So, in a way, it moves the definition of the function at the beginning of the script like you should have done in the first place. Calling a piece of code like that somewhere in the top part of a long file and then defining it somewhere down the bottom is terrible for readability. Everything that is hard to read is automatically hard to maintain.
Using the expression is a better option, as it prevents us from engaging in bad practices.
But here comes the counterargument: if we have a huge load of function definitions at the top of our script and then a little bit of business logic at the bottom, isn’t that also somewhat unreadable?
And yes, it is. If our functions are so complex, then we can just avoid the clutter and export them to a Script Include. Leave only the business logic part in the script and inject everything we need from the outside.
Single responsibility
Speaking of functions, here’s a good one – do you like all-in-one devices? Do you use your built-in laptop speakers, or some fancy headphones that give you something specific that you need, be it quality or convenience? Do you use your integrated GPU for gaming, or do you have a powerful discrete one that’s built for that purpose only?
The thing is, all-in-ones may be doing all their stuff in an adequate manner, but adequate is not great. When you need excellence, you go discrete – it’s the same with functions. One function needs to do one thing only, not all of the things, all of the time.
Let’s put it this way: when it’s time to add new functionality, it’s not a good sign if you need to change the existing one. It needs to be as independent as possible in doing its own thing. So, try building your code in a way that allows it to expand without changing anything.
If/else
Here’s the deal, if-statements are clumsy, and the more complex they get, the harder they are to read. So, what can we do about this:
var anExtremelyImportantFunction() {
var result = ''
if(myConditionsAreFullyMet === true) {
result = 'one possible outcome';
} else if(myConditionsArePartiallyMet === true) {
result = 'another possible outcome';
} else {
result = '';
}
return result;
}
First and most important, we eliminate the ‘else’. Then we simplify the rest:
var anExtremelyImportantFunction() {
if(noConditionsAreMet) {
return '';
}
return myConditionsAreFullyMet ? 'outcome one' : 'outcome two';
}
The early return handles the edge case right at the beginning, so it wouldn’t bother us when structuring the actual business logic. The ternary operator is perfect for cases with two possible outcomes. If we have more than two, a switch statement provides much more clarity, works faster, and is easier to extend in the future.
Script tags in HTML
This one is maybe more than a decade old at this point, but you can still see it in use, and it’s downright inexcusable. First, it’s incredibly unsafe and easily exploitable.
Second, when we reach the <script> part, the rendering of the whole page stops until whatever’s in it finishes executing.
Third, unless it’s some incredibly simple static page, you would have separate JS files with all the logic grouped by modules there, why would anyone export parts of it to the place where only visuals should dwell?!
It’s even worse doing it in the ServiceNow environment, where we have a whole AngularJS Client Script dedicated to that purpose.
The only time when we have any excuse for doing that is when dealing with Google Analytics, but this also is handled in a different manner in ServiceNow.
There are a lot more of these that we could consider following in future articles.
If you want to know more about JavaScript insights, read the article “JavaScripture: The case against “for” loops”.