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.