I never write nested ifs

Andy Chan
4 min readDec 8, 2021

--

I never write nested ifs.

Nested if statements make code hard to understand. They require the reader to hold state in their head while thinking about what the code is supposed to be doing.

Getting rid of nested ifs is about making code easier to understand for others.

Using the patterns in this article, I have been able to avoid writing nested ifs in my code and have confidently refactored legacy code with even the most deeply nested conditional structures.

Pattern 1: Invert the conditional

This is a trivial example of a nested conditional.

def get_pie(self):
if self.has_cream:
if self.has_banana:
return "banana cream pie"
return "boston cream pie"
return "apple pie"

We can flatten this nested if by inverting the conditional:

def get_pie(self):
if not self.has_cream:
return "apple pie"
if self.has_banana:
return "banana cream pie"
return "boston cream pie"

Although we no longer have to read nested conditionals, we’ve introduced a negative conditional, which can be difficult to read. We’ve also moved the return "apple pie" logic to the top of the method. In this case, that logic is pretty straightforward, but if it were more complex, we might want a different approach that kept the complex logic further down in the get_pie method.

Pattern 2: Combine nested conditionals using boolean algebra

Let’s rewind a bit and try something else to avoid the tradeoffs of the inverting the conditional pattern. Here’s our original example again:

def get_pie(self):
if self.has_cream:
if self.has_banana:
return "banana cream pie"
return "boston cream pie"
return "apple pie"

We can flatten this nested if by using boolean algebra to combine the cream and banana conditions for the first condition and move the cream condition further down with an implied not banana condition:

def get_pie(self):
if self.has_cream and self.has_banana:
return "banana cream pie"
if self.has_cream: # implied "and not self.has_banana"
return "boston cream pie"
return "apple pie"

Great! We’ve been able to avoid using a negative conditional and we maintained the order of the logic in our get_pie method, if those things are important to us.

Although we no longer have to read nested conditionals, we’ve introduced a compound conditional, which can be difficult to read, especially if it gets lengthy or is not trivial like in our example here.

Pattern 3: Move the nested conditional into an abstraction

Let’s rewind a bit and try something else to avoid the tradeoffs of the using boolean algebra pattern. Here’s our original example again:

def get_pie(self):
if self.has_cream:
if self.has_banana:
return "banana cream pie"
return "boston cream pie"
return "apple pie"

We can flatten this nested if by moving the nested conditional into an abstraction:

def _get_cream_pie(self):
if self.has_banana:
return "banana cream pie"
return "boston cream pie"
def get_pie(self):
if self.has_cream:
return self._get_cream_pie()
return "apple pie"

The nested if is still there, but now it’s hidden inside the _get_cream_pie abstraction, and it can even be mocked and tested. However, using an abstraction only makes sense if the conditions being abstracted are related to each other. If not, this pattern will make things even harder to understand as you won’t be able to pick a good name for the abstraction. In addition, using this pattern means having to write, maintain and test more code.

We can avoid this overhead by using a more simple pattern, such as inverting the conditional…

There is no secret bullet

Each of the three patterns above have their place — there is no one best pattern to use, each with their own advantages and disadvantages. It’s the art of being a software developer to make good decisions on which one is the most appropriate based on the problems you are modelling in your application.

(Although I would dare say that all of these are more appropriate than using nested conditionals.)

Bonus Pattern 4: Don’t write conditionals in the first place

Wait, there’s more!

There is one pattern of if statements where you can avoid writing conditionals altogether. I call this the mapping pattern:

def get_pie(fruit):
if fruit == "apple":
return "apple pie"
if fruit == "banana":
return "banana pie"
return "humble pie"

We can replace all of these if statements with a dictionary data structure:

def get_pie(fruit):
fruit_pie_map = {
"apple": "apple pie",
"banana": "banana pie"
}
default_pie = “humble pie” return fruit_pie_map.get(fruit, default_pie)

This might be more difficult to apply if a complex conditional other than fruit were used, but one advantage of this pattern is that the mappings can be abstracted or changed easily if different mappings are required. Also, if the size of your map is large, this runtime complexity of this pattern is O(1).

Another alternative to using the dictionary data structure is to use inversion of control. That is, instead of controlling the flow inside the get_pie method, let the caller control it:

class Fruit():
def make_pie(self):
raise NotImplementedError
class Apple(Fruit):
def make_pie(self):
return "apple pie"
class Banana(Fruit):
def make_pie(self):
return "banana pie"
def get_pie(fruit: Fruit):
if isinstance(fruit, Fruit):
return fruit.make_pie()
return "humble pie"

Astute readers might notice that this pattern doesn’t actually allow you to avoid writing if statements; it just moves the responsibility for the if statement to the caller. In a way, this pattern is the opposite of the moving the nested conditional into an abstraction pattern — instead of hiding the logic in the abstraction, you hide the logic in the caller.

You never write nested ifs

There are some prerequisites that I’ve glossed over in this article, such as having to use the multiple exit pattern vs. single exit pattern. However, using the patterns in this article, you should never have to write another nested if statement again.

I would like to know:

  • Did this article help you eliminate nested ifs from your work?
  • Is there a situation where you prefer to write a nested if instead of using one of these patterns?
  • Is there a pattern that I missed?

Leave your feedback in the comments.

--

--

Andy Chan
Andy Chan

Written by Andy Chan

Software Engineer (ex Lawyer)

Responses (5)