• May 10, 2022

Guard Clauses - getting rid of if-else

I've been thinking for a long time and trying different techniques on how to be better at writing code. One of the many techniques/approaches is the so-called Guard-clauses technique. It is a very simple approach to structuring functions/methods. I will try to briefly introduce this technique.

The guard-clauses technique will allow us to write much more readable and sustainable code.

The so-called guard should be a boolean expression that evaluates to False if the function/method is to be continued. Otherwise, the design pattern of the function/method will terminate.

If the code is properly written with this design-pattern, a person looking at the code for the first time should know right away what happens if...

Why do we need guards like that?

  • The code will not be so much nested -> we will not get lost in indentation
  • Removing else branches will make the code much more readable
  • Expressions become terribly simple and we have control over what happens when the code fails

How does this work in practice?

If I have a code that looks like this:

def not_guarded_function(request):
   if request.user:
      if request.user.is_authenticated:
         data = request.data
         if data.get('success', False):
            return data
         else:
            raise Exception('Data not valid')
      else:
         raise Exception('User not authenticated')
   else:
      raise Exception('User not found')

So I can immediately notice that the code is almost impossible to navigate. With a bit of sleuthing, I can figure out what exception is executed after an if.

Python makes it a little easier by indenting, but even so, the orientation is very poor. Not to mention that if I have to meet PEP8, I'm about to not fit in the maximum number of characters per line.

If I use the guard-clause pattern, the code suddenly turns into perfectly clear and readable code:

def guarded_function(request):
   if not request.user:
      raise Exception('User not found')
   
   if not request.user.is_authenticated:
      raise Exception('User not authenticated')
   
   data = request.data
   if not data.get('success', False):
      raise Exception('Data not valid')
  
   return data

I can see right away what happens when... All our else blocks are gone - they're not needed!

The code is clear, and the main part of the function - the unindented - is the code to focus on.

As a result, the Guard-clauses pattern only inverted the conditions. It takes very little to turn your code into code that even a senior developer would be ashamed of.