The Power of Guard Clauses

There are various practices to make code more readable and sometimes even faster. For me readability is always the most important aspect, but what if you could make code easier to read AND faster?

Let's start with a simple code example:

function double(n) {
  if (typeof n === 'number') {
    return n * 2
  } else {
    return throw Error('double only takes numbers')
  }
}

A very simple function. One recommendation you might have stumbled over already is to avoid else as much as possible. In this case, it's a very valid choice to make the code just a hint more readable.

function double(n) {
  if (typeof n === 'number') {
    return n * 2
  }

  return throw Error('double only takes numbers')
}

That code looks quite okay, right? What about the next one?

function isValidPassword(pwd) {
  const includesNeededCharacters = pwd
    .match
    // Lot's of regex logic here
    ()

  return pwd.length > 8 && includesNeededCharacters
}

By glancing at the code, nothing seems wrong. It works perfectly and is doing what it's supposed to do. There's only one thing we should fix.

When looking at the last line of the second code example, you see that we actually qualify the passed-in data. We not only check if the password has all the special characters we require, but we also check the length of the string.

What this example and the first one have in common is the fact that we return quite late in the code for something we know that should fail and even prevent further stuff from happening.

Let's do a short refactor and let's break it down in detail.

function double(n) {
  if (typeof n !== 'number') return throw Error('double only takes numbers')

  return n * 2
}

function isValidPassword(pwd) {
  if (pwd.length <= 8) return false

  return pwd
    .match
    // Lot's of regex logic here
    ()
}

What we did here was to break out of these functions very early, as we know that the minimum length is required or the value passed in needs to be of a certain type. We don't need to verify anything else after that case.

Reject early with guard clauses

The first line that returns false, is called a guard clause. It basically guards the rest of the function and checks if some data fulfills the minimum requirement to be allowed to move on the body in the function.

Of course, these are two simplified examples, but realistically you will stumble over functions that will benefit from this pattern very often. Functions that have if/else branches are often good contenders to be refactored to leverage a guard clause and simplifying code paths is always a win.

Refactor a complex guard to its own function

Sometimes, your guard clause might be quite complex. Let's look at the following example.

function postComment(data) {

if!(
  tokenIsValid(data.token)
  && data.userName === current_user.name
  && data.userId === current_user.id
) return response.error('Please sign in again')

// post comment logic
}

The guard clause of this function looks quite complex and might be hard to validate when briefly glancing at it. Aim for simple guard clauses to fully leverage their potential and keep yourself flexible. We could refactor the function to encapsulate the guard into its own function.

function requestComesFromValidSession(data) {
  return tokenIsValid(data.token)
  && data.userName === current_user.name
  && data.userId === current_user.id
}

function postComment(data) {
if!(requestComesFromValidSession(data))
  return response.error('Please sign in again')

// post comment logic
}

I hope by now you get the gist of it, and maybe you were even using the concept a few times already without knowing its name. If not, I can very much recommend adopting this pattern as it makes your code easier to read and potentially even faster. I know it helped me!

Roman Kuba

Roman Kuba

Austria