Antique shield hanging on a wall
Photo by Paweł Czerwiński

Quick Tip: Guard Clauses

Reducing nesting and improving readability


There are a few things that I find come up more often than others in code review. Whether I'm the one doing the review, or I'm receiving code review from a peer, there are some things that are just more obvious to someone with an outside perspective. One of these things, I've found, is the concept of guard clauses or early returns.

Let's take a look at a block of JavaScript code as an example:

function validateUser(user) {
  if (user.email) {
    const hasValidEmail = validateEmail(user.email)

    if (user.password) {
      const hasValidPassword = validatePassword(user.password)

      return hasValidEmail && hasValidPassword
    }
  }

  return false
}

Using if statements, we've set up some logic that translate to something like this:

If a user has an email, then validate the email. If a user has a password, then validate the password. If the email and password are valid, the user is valid. Otherwise, the user is invalid.

It's a bit hard to translate this sort of structure into pseudocode or plain English as we've written it, but what if we flipped the script a bit. What if we start with the conditions that denote an invalid user instead?

If a user doesn't have an email, they are invalid. If a user doesn't has a password, they are invalid. If a user has a valid email and password, they are valid.

Let's see if we can translate this into code:

function validateUser(user) {
  if (!user.email || !user.password) {
    return false
  }

  const hasValidEmail = validateEmail(user.email)
  const hasValidPassword = validatePassword(user.password)

  return hasValidEmail && hasValidPassword
}

Now we've got a much more concise, more coherent method that's logically equivalent (proven by some tests) to our previous one. This concept of "inverting" your conditions and pulling them up to the top of the method is called a "Guard Clause". Rather than nesting logic underneath positive/assertive conditions repeatedly, we can determine the negative conditions up front and bail out of a method right away.

When you're deep in the implementation of some logic, it can be hard to see opportunities to invert conditions like this. During code review, though, a fresh pair of eyes is great for identifying areas where this particular refactor can be made.

Further Reading: