Guard Clauses vs. Nested Conditionals
An explanation of Ruby guard clauses how to use them as an alternative to nested if statements
2 min read
TLDR; a guard clause is a premature return (early exit) that "guards" against the rest of your code from executing if it's not necessary (based on criteria you specify).
Soon after I started my career as a Ruby on Rails developer I learned about guard clauses and how they can improve code readability.
Let's take a look at an example method with a standard
def example_method(parameter) if parameter == true # awesome code here else return nil end end
In this method we're doing something that's common: evaluating a parameter and:
- executing some code if our expectations are met OR
- executing some other code if our expectations are not met.
In this case:
- if the parameter is
truewe execute some "awesome code"
- if not, we just return
This method can be shortened to only two lines of code by using a guard clause.
def example_method(parameter) return nil unless parameter == true #<-- guard clause # awesome code here end
This was as short example to illustrate the concept of a guard clause. Now let's look at an example where a guard clause can help eliminate a code smell: nested conditionals.
def print_shipping_label(order) if order.outstanding_payments? nil else if order.address_incomplete? order.send_address_reminder else if order.standard_shipping? order.print_standard_shipping_label else order.print_priority_shipping_lable end end end end
This code is hard to read and follow what's happening in my opinion (just look at how many levels of indention are in the nested
Refactoring Nested Conditionals Using Guard Clauses
We can use guard clauses to refactor the nested
if/else statements and make the method more readable and easier to follow.
def print_shipping_label2(order) return nil if order.outstanding_payments? return order.send_address_reminder if order.address_incomplete? return order.print_standard_shipping_label if order.standard_shipping? order.print_priority_shipping_label end
I like guard clauses for these reasons, but you should use them where it makes sense for you and your team.
For example, some languages require explicit resource management and having a single exit point from a method would make sense to prevent memory leaks. However Ruby has garbge collection to automatically collect unused objects.
Guard clauses guard (protect) the rest of your code from executing if not necessary (based on certain criteria) and are placed at the top of a method.