The case of the else statement
Architecture happens at all levels of the application. We got the high level architecture such as diagrams and charts laying out the architecture of the solution and we can go all the way down to how functions are structured and how the code is written. Often when people hear “architecture” they tend to think about those high level things, like UML diagrams and the overall application structure, but that’s just a part of it. Every developer is an architect at some level, and software architecture will take place when code is written.
Another thing about software architecture is that some changes have a big impact on how we author our systems, while others might not matter in the grand scope of things, and this post will discuss something which more often than not falls into the latter category - the else statement.
An example
Take the following example, which is based upon w3schools if-else example:
public String getGreeting(int hour, String name) {
String greeting = null;
if (hour < 10)
greeting = "Good morning";
else if (hour < 20)
greeting = "Good day";
else
greeting = "Good evening";
return greeting + " " + name;
}
This snippet is simple enough. We take an hour, which there are 24 of in a day, and a name. Our output should then be a greeting tailored for that person and the time of day. So if we send in 9 in the morning and Brian, then the output should be “Good morning Brian”.
Let’s now take a look at the same solution, just where we pretend that else doesn’t exist:
public String getGreetingImproved(int hour, String name) {
return getTimeOfDayGreeting(hour) + " " + name;
}
private String getTimeOfDayGreeting(int hour) {
if (hour < 10)
return "Good morning";
else if (hour > 20)
return "Good day";
return "Good evening";
}
The original version, and the rewritten version is exactly the number of lines left, even when counting the empty line between them and the extra brackets.
Being forced to split up methods
Early on developers learn that methods should do one “thing” and that methods should be small. The problem is that words like “thing” and “small” are ambiguous at best. When is a method too big? What is a thing? There’s simply no right answer to these questions.
What I have found while developing without else is that we’re often forced to make multiple methods, and the methods which are created turns out to be very natural and fitting. I have yet encountered a scenario where I have felt the overall look and feel of the code has become detrimental due to the extra methods.
One would think that this would add many unnecessary lines of code to the code base, but even that doesn’t seem to be the case. Looking at our example we see that both solutions contain the exact same amount of lines, including brackets and empty lines! My experience is that the amount of business logic lines goes down, but extra stuff like method declaration, empty lines and brackets generally keep it on the same amount of lines or less.
Having to split up methods due to the lack of else often indicates that the method is trying to do more than “one thing”. By removing else we are introducing a natural way for our code to be broken up. While it is not the only metric we should use to judge method sizes it is at least a concrete metric which can be used while developing.
More sensible guard clauses
Take the following example:
public void doSomething(...) {
//Code
if(isOkay) {
//More code
} else {
throw new Exception("An error has occured");
}
}
This is a common example, where we want something to happen if things are okay, but if it is not okay we want to throw some form of exception. On paper this sounds like a perfectly reasonable approach. We can, however, get a cleaner and simpler solution if we simply drop the else:
public void doSomething(...) {
//Code
if(!isOkay)
throw new Exception("An error has occured");
//More code
}
The functionality of the method has not changed, but we have cleaned it up by simply not using else.
Reducing code duplication
Another thing I’ve often seen is duplicated code within the if and the else. There are many reasons why this can happen, but by not using else we are forcing developers to consider when to initialize values. Here’s an example I’ve seen plenty of iterations of over the years (even wrote a few of them when I started out as well):
public void setState(...) {
//Code
if (stateIsthis) {
this.value1 = "something";
this.value2 = "something else";
} else {
this.value1 = "something";
this.value2 = "something else";
this.value3 = "Some third value";
}
//Code
}
One could argue that this is a beginner mistake, and I would tend to agree. Yet I see it happen all the time by people which cannot be considered beginners. In a world without else the code above would not be possible, rather we would be forced to write something like this:
public void setState(...) {
//Code
this.value1 = "something";
this.value2 = "something else";
if(!stateIsThis)
this.value3 = "Some third value";
//Code
}
By most accounts the initial design of this example can be considered bad code. It shouldn’t happen, but it does. Removing else does not protect us from bad code, but as we can see it makes some instances of bad code more difficult.
Make good solutions the path of least resistance by making bad solutions harder
One can make bad solutions without an else statement. By pretending that else does not exist won’t automatically make our solutions robust, scalable and beautiful, but it does make some bad patterns a little more difficult.
Whenever the industry tells us not to do something it is restricting us, but in a good way. We are not using goto’s because it leads to bad software, even though it is more “freedom” in using goto. By pretending that goto doesn’t exist we have been able to create bigger systems with far less bugs. Yet it wasn’t goto which was the core issue, it was how developers used it. I wouldn’t be so bold to insinuate that “else statements considered harmful”, but I do believe that our solutions can become a little better if we ignore it.
I am a big fan of guiding users into a better solution. Sometimes this can be based in game design. Other times it can be a suggestion from the IDE, or in our case by making a good solution easier compared to the lesser ones.
Kotlin’s way to deal with nulls works in the same way. What Kotlin does is:
Forcing developers to explicitly mark null-able variables with “?”, and if it is a local variable it must explicitly be initialized to null or some value
Forcing developers to either check for null conditions or override the need for check by using the “!!” operator
Here we see that some of the core design elements in the language itself tries to nudge developers into immutability, by making mutability just a little more annoying. Kotlin could, most likely, be made to just assume that something exists or else it would throw an exception, but instead it explicitly implemented barriers to mutability just to move people closer to immutability.
Appropriate else usage
Previously I mentioned Kotlin, which has one pretty good usage of else. Kotlin has expression bodies which basically allows us to write functions without brackets (block bodies). If we take the example and rewrite it in Kotlin It would have looked like this:
fun getGreetingImproved(hour: Int, name: String): String = "${getTimeOfDayGreeting(hour)} $name"
private fun getTimeOfDayGreeting(hour: Int): String = if (hour < 10) "Good morning"
else if (hour > 20) "Good day"
else "Good evening"
Even better, we would probably have used a when statement, which is basically an unbroken switch:
fun getGreetingImproved(hour: Int, name: String): String = "${getTimeOfDayGreeting(hour)} $name"
private fun getTimeOfDayGreeting(hour: Int): String = when {
hour < 10 -> "Good morning"
hour > 20 -> "Good day"
else -> "Good evening"
}
In both of our Kotlin examples else is required since we have no block body. In scenarios like this I am fine with using else.
Else is not the problem
Let’s get one thing clear - else is not the issue. The issue is us, the developers. We make crummy code not because of else, but because we are fallible. What is clear is that else has become a facilitator for crummy code. What we see is that it becomes more difficult for developers to write bad code when they have to reconsider how to do it without an else. Removing else is not solving the problem, but by removing it we are leading developers down a cleaner path.
Some have probably thought that my examples in this post have only improved because it was bad code to begin with. And to those who thought that, yes you’re right. Thing is though, even the best developers can write bad code now and then. By removing else we are putting in hindrances for making these problems in the first place. Let me be the first to note that one can author horrible code without else, but some issues get more difficult to accidentally write.
As always there’s no silver bullet and else in and of itself is not bad practice, nor bad design. One can write excellent code using else, but we have to consider else as an optional statement. If we view else as optional we must then look at what we gain from using it versus not using it. Due to the reasons laid out in this post I think we would garner more from simply not using it. After all developers are always searching for big and small actions to improve the reliability of their code while maintaining readability. One of those small changes can simply be to pretend that else does not exist.