Unconditionally: Refactoring Nested If-Statements for Cleaner Code

There’s a lot to say about writing clean code: Earth’s smartest people have spent a hundred or so years discussing the topic, and it doesn’t look like they’re winding down.

As deep as the subject is, though, we as WordPress developers can still make lots of simple improvements to our code quality that can benefit the work we do every day. Today, I’m sharing one practice that’s been a consistently useful part of my code toolkit: de-nesting conditionals.

What Nested Conditionals Are, and Why They’re a Problem

What do nested conditionals look like? Let’s crack open the functions.php of a theme I’ve recently been working with to find out. (I’ve changed variable and function names from the theme’s name to “demotheme“):

nested conditionals sad

Do you see how the code gets further and further from the left side of the screen? That’s largely because of nested conditionals: if-statements inside if-statements. (The screenshot above also contains a lot of nested foreach loops contributing to the problem, but those are out of scope for this article.)

Why This is Bad

Deeply nested conditionals make it just about impossible to tell what code will run, or when.

The big problem with nested conditionals is that they muddy up code’s control flow: in other words, they make it just about impossible to tell what code will run, or when. Quick, look at line 120 in the screenshot above, and tell me under what circumstances that line of code will actually run. Just about impossible, right? That’s the problem.

To my mind, the problem is that nested conditionals carry state as baggage that you can’t forget about or take for granted. Rather than get all the necessary checks (the individual if-statements) out of the way so that we can do our work, we’re carrying those checks around with us everywhere we go—our code lives inside them.

How This Would Look in the Real World

In the real world, this approach to conditional logic would be like a high school teacher asking the following question:

“If you are alive, and if you are a human, and if are a student in high school, and if you are a student at this high school, and if you are in 11th grade, and if you are enrolled in AP History class, and if you are presently attending AP History class, and if I am currently talking directly to you, would you please explain the significance of the Louisiana Purchase?”

I hate my teacher, because he’s carrying around a ton of conditional state with him everywhere he goes. All those checks should’ve been taken care of prior to the question itself, since if those checks aren’t satisfied then even asking the question makes no sense. Instead, though, my stupid teacher insists on embedding his questions inside dense garbles of conditional logic, and I’m thinking of dropping out.

The Fix: Gateways Instead of Bubbles

Our history teacher is creating “bubbles” of conditional logic. His actual interesting code (his history questions) lives deep inside these bubbles:

if-bubbles

Bubbles

Code that’s isolated deep in multiple “bubbles” of logic structures is very hard to read or evaluate.

I like to structure my logic checks like gateways: they check whether an important thing is true, and if it isn’t, they exit early.

Instead, I like to structure my logic checks like gateways: they check whether an important thing is true, and if it isn’t, they exit early. (These are also called “guard clauses.”) When all the gateways have been passed through, the important code is simply displayed, un-nested: since we haven’t exited already, we know all our important conditions are true.

if-gateways

Gateways

This is like real life. A good history teacher doesn’t have to ask you whether you’re a human and a student at the school, because you’ve passed those conditions already. The conditions act as gates: if you weren’t human, you wouldn’t attend school at all, and if you weren’t in AP History, you wouldn’t be in the room to hear the question. You’ve passed all the gates, so everyone can assume you are who you should be, and the teacher can just get straight to the business at hand.

Code Example

Let’s make this concrete by looking at two simple code examples: first in bubble-style, then in gateway-style.

Bad: Bubble-Style

$is_first_thing_working = true;
$is_second_thing_working = true;
$is_third_thing_working = true;
$is_fourth_thing_working = true;

if( $is_first_thing_working === true ) {
	if( $is_second_thing_working === true ) {
		if( $is_third_thing_working === true ) {
			if( $is_fourth_thing_working === true ) {
				return 'Working properly!';
			}
			else {
				return 'Fourth thing broken.';
			}
		}
		else {
			return 'Third thing broken.';
		}
	}
	else {
		return 'Second thing broken.';
	}
}
else {
	return 'First thing broken.';
}

Good: Gateway-Style

$is_first_thing_working = true;
$is_second_thing_working = true;
$is_third_thing_working = true;
$is_fourth_thing_working = true;

if( $is_first_thing_working !== true ) {
	return 'First thing broken.';
}

if( $is_second_thing_working !== true ) {
	return 'Second thing broken.';
}

if( $is_third_thing_working !== true ) {
	return 'Third thing broken.';
}

if( $is_fourth_thing_working !== true ) {
	return 'Fourth thing broken.';
}

return 'Working properly!';

Notes on Example

The difference between the two code snippets above boils down to a key distinction:

  • The bubble method asks if important conditions are true, and only runs code if they are true.
  • The gateway method asks if important conditions are false, and immediately issues exit instructions for each condition if it’s false.

The bubble method forces nesting, because you have to check “true, true, true, true” before you get to the code you want to run. Each “true” check is a level of nesting—a condition your code has to live inside.

The gateway method is not nested: as you see, the code is never more than one layer of logic deep. This is because once a given gateway is passed, we can forget about it completely. In other words, since we didn’t exit after our $is_first_thing_working check, we automatically know that $is_first_thing_working is true for the rest of the code. It’s like real life: if you’re sitting next to me in history class, I know you’re a human, a student at my high school, etc.—or else you would’ve never been in my class in the first place. No need to check.

A Practical WordPress Example

Let’s create a real-world-ish WordPress example using WordPress’s wonderful conditional tags. This example actually exists, in bad/bubble form, in our current post on the topic:

Bubble Style (Bad)

// Enqueue specific CSS and JS when the special-page page template is in use
add_action( 'wp_enqueue_scripts', 'wpshout_special_page' );
function wpshout_special_page() {
    if ( is_page( 'special-page' ) ) {
        wp_enqueue_script( 
            'special_js', 
            get_stylesheet_directory_uri().'/special.js'
        );
        wp_enqueue_style( 
            'special_css', 
            get_stylesheet_directory_uri().'/special.css'
        );
    }
}

Do you see how this whole language is wrapped inside an is_page() check? Bulky, bubbly, nested. Let’s do this instead:

Gateway Style (Good)

// Enqueue specific CSS and JS when the special-page page template is in use
add_action( 'wp_enqueue_scripts', 'wpshout_special_page' );
function wpshout_special_page() {
    if ( ! is_page( 'special-page' ) ) {
        return;
    }

    wp_enqueue_script( 
        'special_js', 
        get_stylesheet_directory_uri().'/special.js'
    );
    wp_enqueue_style( 
        'special_css', 
        get_stylesheet_directory_uri().'/special.css'
    );
}

Much nicer, right? If we’re not using the special-page template, we don’t want to do anything else with this function. Here, that logic check is now a gatekeeper—not a bubble—and once we’ve passed the gate we can move forward with confidence.

Beyond Conditional Love

I hope this has helped get your mind turning about the way you write code in WordPress, and conditional logic in particular. Getting in the habit of using the “gateway” approach to conditionals has made my own code much more concise and pleasant to write. Thanks for reading!

Image credit: PhotoAtelier


5 Responses

Comments

Pingbacks