Early Return or guard clauses blog post by Felipe Elia.

Improve your code with Early Return

For me, early return is the simplest and quickest technique to improve the readability of your code by 300%. It’s something I learned while collaborating with the WordPress core and I share it with everyone whenever I can.

The technique, also called guard clauses, consists of exiting the function as quickly as possible if some essential condition is not met. The most noticeable improvement is the removal of nested ifs. To make it easier to understand, let’s look at some code.

Imagine a WordPress function that makes changes to the search, but only if some conditions are met:

PHP
function felipeelia_change_search( $query ) {
	if ( ! is_admin() && $query->is_main_query() && $query->is_search() ) {
		$current_site = get_current_blog_id();

		if ( 1 === $current_site ) {
			$search_term = $query->get( 's' );

			if ( strtolower( $search_term ) === 'super special search' ) {
				$post_type = $query->get( 'post_type' );

				if ( empty( $post_type ) ) {
				    // Only here we are actually doing something.
					$query->set( 'posts_per_page', '1' );

					$tax_query = [
						[
							'taxonomy' => 'category',
							'field'    => 'slug',
							'terms'    => 'news',
						],
					];
					$query->set( 'tax_query', $tax_query );

					$query->set( 'order', 'asc' );
				}
			}
		}
	}
}
add_action( 'pre_get_posts', 'felipeelia_change_search' );

Now the version with early returns or guard clauses :

PHP
function felipeelia_change_search_early_return( $query ) {
	if ( is_admin() || ! $query->is_main_query() || ! $query->is_search() ) {
		return;
	}

	$current_site = get_current_blog_id();
	if ( 1 !== $current_site ) {
		return;
	}

	$search_term = $query->get( 's' );
	if ( strtolower( $search_term ) !== 'super special search' ) {
		return;
	}

	$post_type = $query->get( 'post_type' );
	if ( ! empty( $post_type ) ) {
		return;
	}

	$query->set( 'posts_per_page', '1' );

	$tax_query = [
		[
			'taxonomy' => 'category',
			'field'    => 'slug',
			'terms'    => 'news',
		],
	];
	$query->set( 'tax_query', $tax_query );

	$query->set( 'order', 'asc' );
}
add_action( 'pre_get_posts', 'felipeelia_change_search_early_return' );

Note that although the second version is a bit longer, the sequence of ifs and returns helps scan the code. Furthermore, the changes applied by our function are only indented once, instead of five!

Real-life example: early return in ElasticPress

Now another example, this time from the ElasticPress plugin. See how the Facets::is_facetable() method would look like without early returns :

PHP
public function is_facetable( $query ) {
	if ( ! \apply_filters( 'ep_is_facetable', false, $query ) ) {
		if ( ! is_admin() && ! is_feed() ) {
			if ( ! defined( 'WP_CLI' ) && ! WP_CLI ) {
				if ( $query->is_main_query() ) {
					$ep_integrate = $query->get( 'ep_integrate', null );

					if ( false !== $ep_integrate ) {
						$woocommerce = Features::factory()->get_registered_feature( 'woocommerce' );

						if ( $woocommerce->is_active() && function_exists( 'is_product_category' ) && is_product_category() ) {
							if ( $this->is_facetable_page( $query ) ) {
								return true;
							}						
						}
					}
				}
			}
		}	
	}
	return false;
}

Now the real version of the method:

PHP
public function is_facetable( $query ) {
	if ( \apply_filters( 'ep_is_facetable', false, $query ) ) {
		return true;
	}

	if ( is_admin() || is_feed() ) {
		return false;
	}

	if ( defined( 'WP_CLI' ) && WP_CLI ) {
		return false;
	}

	if ( ! $query->is_main_query() ) {
		return false;
	}

	$ep_integrate = $query->get( 'ep_integrate', null );
	if ( false === $ep_integrate ) {
		return false;
	}

	$woocommerce = Features::factory()->get_registered_feature( 'woocommerce' );
	if ( ! $woocommerce->is_active() && ( function_exists( 'is_product_category' ) && is_product_category() ) ) {
		return false;
	}

	if ( ! $this->is_facetable_page( $query ) ) {
		return false;
	}

	return true;
}

Much better, isn’t it?

There is no silver bullet

Early returns or guard clauses are great, but they’re not the solution to everything. If your code has a lot of else statements, for example, consider breaking the function into smaller ones or you’ll end up with returns scattered throughout your code, making it harder to understand.

If you liked the post, don’t forget to share it!

Felipe Elia

Associate Director of Platform Engineering na 10up, WordPress Core Contributor, Global Polyglots Mentor na comunidade internacional do WordPress e Locale Manager na comunidade WordPress Brasil.

Leave a Reply

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.