How to write better code with Guard Clauses

avatar

image.png

In the spirit of the 10,000+ Hive Hackathon I am running, I am going to try to put out some tips and tricks over the next few days.

I also have a long-running Python Tips Series you can find here.
Look at the footer for links to the previous entries.

What is a Guard Clause

A guard clause a programming idiom that you are likely already using but are not aware there is a name for it. It is also something you are not likely using effectively.

A guard clause exits a loop or function when certain conditions are met early on.

Example 1

let inventory = 1
let cart = []

function addToCart(item){
  if (inventory > 0) {
    cart.push(item)
  }
}

addToCart('Book')
console.log(cart)

This simple code creates a variable to simulate an inventory and a shopping cart. There is an addToCart() function to add items to the cart. Before adding an item to the cart, it checks to see if the inventory is empty.

This code works perfectly fine and is easy to read. The problem comes in when the business rules become more complex and you start expanding the if statement.

Example 2

let inventory = 1
let cart = []
let userIsLoggedIn = true

function addToCart(item){
  if (userIsLoggedIn) {
    if (inventory > 0) {
      cart.push(item)
    }
  }

}

addToCart('Book')
console.log(cart)

While this is still relatively simple, you have likely coded far more complex examples many times. These types of nested if structures become very difficult to manage and change in the future.

Introducing the guard clause

A guard clause will exit your loop or function immediately. This avoids additional indents to your core condition statements making them far easier to read and maintain.

let inventory = 1
let cart = []
let userIsLoggedIn = true

function addToCart(item){
  if (!userIsLoggedIn || !inventory) return
  cart.push(item)
}

addToCart('Book')
console.log(cart)

By extracting the initial sanity checks into a dedicated if statement that exits immediately, your code is cleaner and far easier to maintain.

These examples are oversimplified, and real-life examples are far more difficult to work with.

Here's an example of an if cluster bomb from Reddit

const returnLinesRequiredData = (lineObj, id, key) => {

  if (id) {
    if (id < lineObj.stations.length) {
      if (key) {
        if (lineObj.stations[id].hasOwnProperty(key)) {
          return lineObj.stations[id][key];
        }
        return errorMsg;
      }
      return lineObj.stations[id];
    }
    return errorMsg;
  }
  return lineObj;

};

Source

Try to look over your existing code and see where you can implement guard clauses to improve the readability of your code.


Securely chat with me on Keybase

Why you should vote me as witness



0
0
0.000
9 comments
avatar

Will try to make some great codes...

0
0
0.000
avatar
(Edited)

I usually just make some big if conditions haha.

I also use something I love, I'll show it here.

Let's say you want to extract "avatar" from this object:

{
  id: 123456,
  data: {
    type: "message",
    user: {
      "name": "user123",
      "user_id": 1235612,
      "avatar": "https://example.com/avatars/user123_afk20s_3a.jpg"
    },
    body: "test"
  }
}

It could be problematic if there is no response.data.user because it's a system message, for example, or it's not a message, or if the user has no avatar, etc. Whenever the API's are poorly made and somewhat inconsistent on the keys, there is a very easy solution.

Instead of doing this:

let pfp;
if (response && response.data && response.data.user && response.data.user.avatar) {
  pfp = response.data.user.profile_picture;
}

We can do it all more easily like this:

const pfp = ((((response||{}).data||{}).user||{}).profile_picture||"https://example.com/avatars/default.jpg)

At any failure, instead of throwing an error, it goes to undefined and picks the empty object, and if the last property is still undefined, it grabs the default.

edit: (this is obviously not made for features that require you to display a specific error of what is lacking from the input)

0
0
0.000
avatar

I keep thinking that I need to learn to code. Or at least get the basics down. Seeing how I don't see a future where I'm not involved in crypto in some shape or form. Just logged into hive for the first time. Still have some learning on how things work now. But I wasn't at all surprised to see you still working hard. God Bless. :)

0
0
0.000
avatar

I keep thinking that I need to learn to code.

Same.

0
0
0.000
avatar

That's a great advice, we usually exit early and do all the error check before doing any heavy operations.

0
0
0.000
avatar

I know a lot of programmers prefer the guard clause with a lot of returns, but I prefer structured ifs as in Example 1 and 2 but write a short comment after the closing if bracket, if the if statement gets very long.

0
0
0.000
avatar

Funny, I never knew this was called a guard clause. I seem to use them more often now than ever in this new era of love for weakly-typed languages.

stuff like

function whatever(value){
    if (isNaN(value))
        return false;
    ...
    ...
    return true;
}

I wish Javascript were more like this (at least optionally):

private function whatever(value:Number):Boolean{
    // Throws an error if passed value is not of type Number
    ...
    return true;
}

With languages like JS I spend most of my time trying to debug where the hell the issue is even coming from. I suppose some people like the flexibility of not declaring types, though. I know you can use something like TypeScript but it would be nice if one could optionally static type and run it natively in the browser.

But I digress... Nice to know what it's called a guard clause!

0
0
0.000
avatar

I have heard it termed as the happy path. Where as you work your way down the function you return out the "un-happy" steps.

0
0
0.000