Do you tend to overthink stuff? If yes, has that ever ended up positively? If the never-ending spiral of “what if” has absorbed you into its nothingness at least once, you know what I’m talking about. And if coding is a somewhat simplified version of how us humans think and make decisions, then maybe it’s not a great idea to recreate the “what if” spiral in there, is it?
Well, that’s a lot of “if”-s for just a couple of sentences…
If you’re still confused about where I am going with this, I don’t blame you. Let’s just clarify one thing: “if” statements are not inherently bad, and having them in your code is perfectly fine. Overusing and overcomplicating them that’s what we need to avoid. There are some extremely practical alternatives out there that happen to be heavily underestimated by many coders but can be much more adequate than the good old “if” in certain situations. Let’s check out a conditional structure that I’m sure every single one of us faces daily:
var state = someRecord.state;
if (state == 'new') {
doThings();
} else if (state == 'work_in_progress') {
doStuff();
} else {
if (state == 'closed_complete' || state == 'closed_incomplete') {
doThatOneParticularThing();
} else if (state == '' || state == undefined || state == null) {
doNothing();
} else {
displayMessage();
}
}
The above functions are just made-up pseudo-code for the sake of simplicity. Even the one that’s supposed to do nothing. Unfortunately, at some point, you will witness a 10-rows long piece of code that does nothing.
The structure we see there probably didn’t look like that initially. It probably took several new requirement cycles and various developers to achieve it. You’ll often see similar conditionals spanning over dozens of code rows, sometimes containing literally unreachable branches. It’s pointless to even start on readability here, but can you imagine adding more logic to that thing over there?
Let’s think of an alternative solution. Believe it or not, there is one good thing about this “if” – it has a finite number of possible outcomes, and all of them are known to us. While that might not sound like much, it’s a luxury. We have an excellent option at our disposal for this kind of situation – the “switch”. We can wrap one in a neat little function:
function helpMeDecide(someRecord) {
if (!someRecord.state) return;
switch (someRecord.state) {
case 'new':
return doThings();
case 'wip':
return doStuff();
case 'closed_complete':
case 'close_incomplete':
return doThatOneParticularThing();
default:
return displayMessage();
}
}
Right off the bat, we get rid of one of the “else if”-s from the original construct by validating the existence of our input and exiting the function immediately if it’s missing. This type of early return is called a guard clause, and it’s one of the few good ways to use a standard “if”. A couple of interesting points here – first, your function can have multiple returns, which is perfectly fine. Second, an “if” statement doesn’t need the curly brackets if it only contains one line of code.
Then comes the even better part – when we have a finite number of known possibilities, we can easily place them in a switch statement. It’s very easy both to read and to add/remove branches from it. In many cases, it also works faster, so we have a performance benefit, although a very small one. Another cool feature you can notice here is how cases can be stacked when they belong to the same branch. In our example, both ‘close_complete’ and ‘close_incomplete’ will trigger the same function.
The default case is optional. It’s the equivalent of an “else” – it will run if no other case above has. Please also note that you can use “break” statements instead of returning. You need to have either one, though. Otherwise, the next cases will be evaluated, and the default will be triggered instead of breaking from the switch.
Let’s check another seemingly simple common use of an “if”:
var someVariable;
if (someRecord.state == 'wip') {
someVariable = true;
} else {
someVariable = false;
}
In this one, we have a problem before even entering the statement, and that’s the variable. Declaring one without initializing it is a very bad practice, especially for the “var” type, which we’re forced to use. The thing is there’s no reason not to initialize your variables. If you don’t know what data type to expect, there’s a good chance you’re doing something wrong at an earlier stage in your code.
Then we get to the conditional itself. In the end, we have an overall of six lines of code, handling a variable and two possibilities. Those six can easily become one:
var someVariable = someRecord.state == 'wip' ? true : false;
It’s called a ternary operator, and it’s easily one of the best things I’ve witnessed. It has two main parts, separated by a question mark – a condition and a set of possibilities. Those are, on their part, separated by a colon – the left one is returned if the condition evaluates to true, and the right one for false.
This operator can be used to initialize variables, as a return of functions, and many more, making it an incredibly powerful tool, packing a lot of logic in a very compact form. It can even be nested – both its true and false outcomes can be ternary operators too, although that’s an inception level I’m not going into at this point.
Here are some interesting ways of harnessing its power:
- manipulating numeric values:
var bool = false;
var value = 10;
value *= bool ? 3 : 5;
Based on the value of a Boolean variable or an expression, we can increment, decrement, multiply or divide numeric values differently.
- manipulating strings:
var stateString = 'closed_';
stateString += someRecord.state == 'closed_complete' ? 'complete' : 'incomplete';
While the example may not have much value in terms of logic, it shows how a string may be incremented with different values based on some condition. This one is great for building coma-separated lists of sys_id’s, that can be easily processed afterward. The whole thing happens on a single line instead of building complex “if” statements.
I saved the best part for the last. It’s a combination of the approaches I already mentioned. Ternary operators can be wrapped in functions that make those pesky validations almost pleasant to look at. Here’s one we’ve all seen:
var validation;
if (someRecord.state != 'cancelled' && someRecord.timeStamp != '' && someRecord.created_by == gs.getUserId() && someRecord.active == true) {
validation = true;
} else {
validation = false;
}
One of my favorite bad practice combinations – an uninitiated variable and endless “if” condition. Here’s how we can easily deal with that. First of all, we don’t want that ugly condition to spread like that. Let’s introduce some order to this chaos – we’re building an array:
var validationConditions = [
someRecord.state != 'cancelled' ? true : false,
someRecord.timeStamp ? true : false,
someRecord.created_by == gs.getUserId() ? true : false,
someRecord.active
]
Those are all the same conditions, evaluated separately as elements of an array. All except the “active” one, since it is already a Boolean field value – we just take that as it is. The bottom line, when it all evaluates, we’re left with an array of Booleans. What are we doing with that next?
Validations like that one are extremely common. They are essentially all the same in terms of logic, only the condition values differ. We’ll need to reuse this approach multiple times for various tasks within the same ServiceNow instance, so why not make this solution a part of a utility Script Include and just pass a different conditions array every time? Something like this:
function validation(conditionsArray) {
var failedConditions = conditionsArray.filter(function (e) {
return e == false;
})
return failedConditions.length ? false : true;
}
Using JS array methods is somewhat counterintuitive in the beginning, but once you get ahold of them, they are invaluable. In this case, the .filter() iterates over the elements of the array with a callback function that only returns the ones evaluating to false. The result is a new array containing all the false values if any. If that array has a length, that would mean we have at least one false, i.e. a failed validation. Otherwise, we’re all good, and the ternary operator will return true.
I’d like to finish this by reiterating that “if” statements are a core part of any programming language and should not (and could not) be dismissed entirely. There are cases in which an “if” is inevitable. The thing is, those cases are pretty much the only time they should be used.
For more insights, check out the latest article from the series – “JavaScripture: Maintainable Code“.
Subscribe to our newsletter to be the first to get the latest news and insights about Digital Transformation, ServiceNow, and more.