DEV Community

Cover image for Guard Clauses Explained
Maxime Gélinas
Maxime Gélinas

Posted on • Updated on

Guard Clauses Explained

What is a guard clause?

A guard clause is a technique derived from the fail-fast method whose purpose
is to validate a condition and immediately stop the code execution if the condition is not met by throwing a meaningful
error instead of leaving the program to throw a deeper and less meaningful error. Let me show you an example:

public User GetUser(string userName)
{
  if (userName == null) throw new NullArgumentException(nameof(userName));
  // Continue if the condition is fulfilled...
}
Enter fullscreen mode Exit fullscreen mode

In the previous example, the if block act as a guard clause by protecting the GetUser method against any null
userName arguments. Like that, we are free to write the rest of the method without having to worry about checking if
userName is null.

Why using guard clauses?

Guard clauses simplify code by removing useless nested branching conditions, by returning meaningful errors.

Before guard clauses:

public void CreateUser(string userName, string password)
{
  if (userName == null)
  {
    // Do something...
  }
  else
  {
    // Do something else...
    if (password == null)
    {
      // Do something...
    }
    else
    {
      // Do something else...
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

After guard clauses:

public void CreateUser(string userName, string password)
{
  if (userName == null)
    throw new NullArgumentException(nameof(userName));
  if (password == null)
    throw new NullArgumentException(nameof(password));
  // Do something...
}
Enter fullscreen mode Exit fullscreen mode

What to guard against?

Preconditions

Preconditions are the conditions that have to met before the method execution. Basically, the method preconditions will
always depend on the method arguments. A good example of a method precondition is a non-null argument.

public User GetUser(string userName)
{
  // Requires a non null `userName` argument.
  if (userName == null) throw new NullArgumentException(nameof(userName));
  // Execute the method purpose...
}
Enter fullscreen mode Exit fullscreen mode

Postconditions

Postconditions are the conditions that have to met after the method execution. Basically, the method postconditions will
always depend on the value returned by the method. A good example of a method post-condition is a non-null or a not
empty string.

public User GetUsers()
{
  // Execute the method purpose:
  var users = this._repository.GetUsers();
  // Ensures `UserRepository.GetUsers()` returns no null users.
  if (users.Any(user => user == null))
    throw new Exception("UserRepository.GetResult() a null user.");
  return users;
}
Enter fullscreen mode Exit fullscreen mode

As you can guess, post-conditions are not mandatory since you know exactly what's coming out of the method. The only
time you should consider using post-conditions is when you have to use unreliable calls (i.e. external methods or
protected methods) to get the result of the method as the example above.

Public vs private

In both cases, preconditions and post-conditions should not be checked for private methods since the class itself is the
caller of these methods, you can trust what goes in goes and out of them. So you don't have to validate them.

How to handle guard clauses exceptions?

Ok, it's time to introduce a very important rule about guard clause:

Guard clauses exceptions should never be caught.

What this means is that most of the time, you should let the caller hit those exceptions because most of the time, guard
clauses will guard against scenarios that should never happen like null arguments. What a null argument means? Most of
the time, a null argument is a bug so should we catch a bug and taking the chance of never discover it? No! Instead, we
want to let the application fail immediately so that we can discover the bug before deploying it to production during
the development process.

But what if we have preconditions that don't rely on bugs? What if we have preconditions that could occur sometimes like
business logic preconditions? The solution is to expose your guard clauses!

Why exposing guard clauses?

Sometimes, you have to guard against business logic which means the condition may not be respected and it's not
necessarily a bug. In those cases, a possibility is to expose the related guard clauses under a public boolean method to
let the caller branch around this boolean method.

Implementation:

public bool CanCreateUser(string userName, string password)
{
  return !this.UserNameExists(userName) && this.IsStrongPassword(password);
}

public void CreateUser(string userName, string password)
{
  if (!this.CanCreateUser(userName, password))
    throw new Exception("The user can't be created.");
  // Create the user...
}
Enter fullscreen mode Exit fullscreen mode

Usage:

if (!CanCreateUser(userName, password))
{
  Console.WriteLine("Invalid username or password.");
  return;
}
CreateUser(userName, password);
Console.WriteLine("The user has been successfully created.");
Enter fullscreen mode Exit fullscreen mode

Why creating a guard class

It's a good practice to encapsulate our guard clauses inside a class dedicated to providing guard clauses so we could
reuse the logic and write more readable guard clauses. Here is an example of such a class:

public static Guard
{
  public static void Requires(Func<bool> predicate, string message)
  {
    if (predicate()) return;
    Debug.Fail(message);
    throw new GuardClauseException(message);
  }

  [Conditional("DEBUG")]
  public static void Ensures(Func<bool> predicate, string message)
  {
    Debug.Assert(predicate(), message);
  }
}
Enter fullscreen mode Exit fullscreen mode

In that implementation, the Requires method is used to validate preconditions and the Ensures metd is to validate
post-conditions. The interesting point about this implementation is the use of the C# Debug class coming from the
System.Diagnostics namespace. The main point of the Debug class is that it will execute in debug mode only and can't
be caught by the caller so it's respect the "never catch guard clauses exceptions" rule. Also, the Ensures method uses
the Conditional C# attribute to make sure the code inside the method will run in debugging only so the performance
will not be affected in production because remember the guard clauses bugs will be detected before deploying in
production! But just to be sure, we protect ourselves in the Requires method by throwing a GuardClauseException if the
precondition isn't met because the code after it will probably fail anyway or cause data inconsistency we don't want.

After all, here how we could use the Guard class:

public User GetUserRange(int from, int to)
{
  Guard.Requires(() => from >= 0, "Received negative from.");
  Guard.Requires(() => to >= 0, "Received negative to.");

  var users = this._repository.GetUsers();
  foreach (user in result)
    Guard.Ensures(
      () => user != null,
      "UserRepository.GetResult() a null user.");

  return users;
}
Enter fullscreen mode Exit fullscreen mode

Summary

In this article, we have seen how guard clauses can help us to discover bugs before deploying in production and to make
our code more readable.

We have also learned, why guard clauses exceptions should never be handled and how to expose guard clauses as a
draw-back when they rely on business logic.

Finally, we learned how to create our own guard clauses implementation under the Guard class.

Top comments (0)