Much of the code I have seen in my career is a good example of the Pareto principle. 80% of the code in any codebase would deal with the “happy path” (what the software is expected to do), while the other 20% is left for tackling errors, exceptions, and edge cases.
Naturally, when learning to program, we encounter the if-else
statement (which happens to be present in most popular languages), and our brains immediately associate the if
with the happy path and the else
with the edge case scenarios. It is a natural safety mechanism - wrap your entire block of code in a big if
, and you’ll sleep better at night, knowing you’ve protected it from inappropriate inputs. For every other case, there’s that tiny else
at the end, right?
Unfortunately, it often leads to code that looks like this:
if (someConditionIsMet) {
// ...
// ...
// ...
// 100 more lines of code
// ...
// ...
// ...
// and 100 more
// ...
// ...
// ...
} else {
// Now, take care of that edge case
}
return someResult
One issue here is that the edge case handling is left at the very end, which makes it easy to overlook when adding code to the block above it. I bet most people reading the code won’t even recall that the else
exists until and edge case forces them to dig deeper.
But even that is something I could live with if it wasn’t that people tend to replicate the same pattern on multiple levels. As a result, code in real life looks more like this:
if (someConditionIsMet) {
// ...
// ...
// ...
// 100 more lines of code
// ...
// ...
// ...
// and 100 more
if (someOtherConditionIsMet) {
// ...
// ...
// ...
// 100 more lines of code
// ...
// ...
// ...
if (yetAnotherConditionIsMet) {
// ...
// ...
// ...
// 100 more lines of code
// ...
// ...
// ...
} else {
// Now, take care of that edge case
}
// ...
// ...
// ...
} else {
// Now, take care of that edge case
return someOtherResult
}
// ...
// ...
// ...
} else {
// Now, take care of that edge case
}
return someResult
That’s right - code nesting is a massive footgun and the easiest way to bring unnecessary complexity to any codebase. The human brain can only deal with a few different things at a time, so when faced with having to dig multiple levels deeper, it’s easy to forget an important thing that happened one level above. It’s one of the most frequent reasons I would bring back a pull request. Our business processes are complex enough; why must we make our lives even more complicated?
I am not the first to address this phenomenon - in fact, it even has a name. Jeff Atwood coined the term Arrow Code for this sort of deep nesting almost two decades ago.
Luckily, the solution is pretty simple.
Ditch that Else. Invert the logic. Tackle the edge case first. #
What if instead of that massive if-else
block, we decided to get rid of the else
and took care of it first? If we don’t expect something to happen regularly, wouldn’t it be better to check for it first, and return right away if it did? I mean it - tackle the small edge case and return early if you have to; otherwise, leave the happy path at the top level of your function.
if (!someConditionIsMet) {
// Take care of that edge case first
return someResultOrNothing
}
// The happy path can continue without a guarding block
// ...
// ...
// ...
// 100 more lines of code
// ...
// ...
// ...
// and 100 more
return someResult
The same idea can get replicated with multiple edge cases too:
if (!someConditionIsMet) {
// Take care of that edge case first
return someResultOrNothing
}
if (!someOtherConditionIsMet) {
// Take care of that edge case first
return someResultOrNothing
}
if (!yetAnotherConditionIsMet) {
// Take care of that edge case first
return someResultOrNothing
}
// The happy path can continue without a guarding block
// ...
// ...
// ...
// 100 more lines of code
// ...
// ...
// ...
// and 100 more
return someResult
So, how about ditching that else
?
References #
Have something to say? Join the discussion below 👇
Want to explore instead? Fly with the time capsule 🛸