Cut the Else

27.01.2024

For my first blog, I wanted to start with a bold statement. How else could I capture the very scarce resource of time? In the next few minutes, I want to explain why we would be better off without an else.

Simple value assigning

First I want to focus on cases where assigning a value to something is based on a single if and else. Think about the following case:

let x;

if (condition) {
  x = 0;
} else {
  x = 1;
}

Now, imagine the same thing but without the else statement. Can you already imagine how that would look like? Take a moment to think about it! I’ve even given you some space to ponder.







































The solution is:

const x = condition ? 0 : 1;

I do realize, that I somewhat cheated here. Because, if you are really strict, using a ternary operator is indeed an else. But I will let it slide because it differs greatly in semantics and I need to prove my point. The second case is way more readable.


If you don’t agree you are welcome to read my explanation: First of all, in the if example the declaration is a widow. You don’t know what is happening with the variable and it’s value. By using the ternary, the value is directly assigned and it’s usage is a lot clearer. The second advantage is that the variable can be immutable.

Digression to the coalescing operator

I want to go into more detail based on the ternary principle. We can improve readability by using the coalescing operator. A few languages use this principle. It is quite powerful. In null and undefined checks, we can simplify the ternary operator even more. First, we need to specify the top condition as the following:

const x = condition !== undefined ? condition : 1;
let x = undefined;

x ||= someFunction();
x ||= someOtherFunction();
x ||= anotherFunction();

console.log(x);

If we compare this with an if based approach it would look something like this.

let x = undefined;

x = someFunction();
if (!x) {
    x = someOtherFunction();
}
if (!x) {
    x = anotherFunction();
}

console.log(x);

Of course, the second part does look nice, clean and all. But can you really win against the neatly formatted lines from the first example? In a blink of an eye it is obvious what the code does. It tries to assign a value that is not undefined. On the other hand, in the second example the idea only comes to mind, when every if is checked.

Complex value assigning

There is still a huge chunk of code that cannot be maintained without an else, if we only use the methods described until now. Let’s take a look at them now.

let x;

if (condition) {
    const a = function1();
    const b = function2(a);
    const c = function3(a, b);
    x = c;
} else {
    x = 1;
}

In our ternary example this case would look horrendous.

const x = condition 
    ? function3(function2(function1()), function1())
    : 1;

First things first, this is not performant, since calling function1 twice results in repeating the same calculation. This is no problem when the function is small, but I/O Operations would be a huge blow to the performance of the code. All that, to not improve the readability in the slightest. This is where encapsulation comes to the rescue.

function calcX(condition) {
    if (condition) {
        const a = function1();
        const b = function2(a);
        return function3(a, b);
    }
    
    return 1;
}

const x = calcX(condition);

This approach relies heavily on the concept to use an immutable variable wherever possible. I highly recommend using const variable, because the value cannot change unexpectedly. Now, we don’t know what happens during the assignment process, but in most cases we don’t care and a describing name is enough. In the rare case, where calcX doesn’t work correctly or undergoes some changes, it is more straight forward to go into the function and fix the problem in a more focused manner, without the distracting code around. It’s all about setting the right focus in your code, so your brain doesn’t have to do acrobatics early in the morning.

Finally, I want to suggest using switch instead of if, if else and else.

let x;

if (value === "a") {
    x = "case a value";
} else if (value === "b") {
    x = "case b value";
} else {
    x = "fall back value";
}
let x;

switch (value) {
    case "a":
        x = "case a value";
        break;
    case "b":
        x = "case b value";
        break;
    default:
        x = "fall back value";
}

This only works when checking for equality but where applicable it can increase readability. I understand that this is a bit more personal opinion and both are quite simple. But my experience has shown that the second case is easier to read.

So, I should never use an else ?

No, these guidelines cover maybe 99% of cases. The other 1% don’t need an else but are more readable when using one. Always strive for the most readable solution and think for yourself: “What looks more readable for myself?“. Code often goes through many iterations until it’s optimal, both for readability and performance. Ideally, you have code snippets from the past, where you can check if you can read them easily. If not, then perhaps rework your approach. Keep in mind, that code is always simpler to write than to read.

If you like my ideas don’t wait and try to incorporate them in your code now!