Avoiding nested conditionals in your code

By Stephen Holdaway

1 Sep, 2012

Deeply nested code can be a pain to work with, especially when it’s unnecessary. To combat nasty nesting situations, I’m in the habit of doing something pretty straightforward: invert your if statements.

(Birds-)nested code

Take the following block of nested if statements for example:

function doLoginWithDetails(username, password)
{
    if(isSessionValid()){
        if(userExistsInDatabase(username)){
            if(isUserAllowedToLogin(username)){
                if(isPasswordCorrectForUser(username, password)){
                    // Successful login
                } else {
                    // Incorrect password
                }
            } else {
                // User not allowed to log in
            }
        } else {
            // User not in database
        }
    } else {
        // Session not valid
    }

}

The target code is in the middle here and has a nice path up to it, however connecting the dots for the else conditions isn’t quite as clear. While this code might be understandable in the heat of writing, if you decided later that userOwnsCat() needed to be true for a user to log in, and you wanted a specific error condition for it…things get messy, quickly. Essentially, this code is not maintainable.

There’s no issue with the logic in this nested example – it’s just badly organised. The very same logic can be written in such a way that it only ever goes one level deep, and there’s not much to it either.

Cleaning up the nest

Bird’s nests have no place in code, so it’s time to eradicate this one. Instead of using if statements to handle the desired condition, use them to handle the exceptional one:

function doLoginWithDetails(username, password)
{
    if(!isSessionValid()){
        // Session not valid
    }

    if(!userExistsInDatabase(username)){
        // User not in database
    }

    if(!isUserAllowedToLogin(username)){
        // User not allowed to log in
    }

    if(!isPasswordCorrectForUser(username, password)){
        // Incorrect password
    }

    // Successful login

}

This version is easier to read, hence more maintainable, and adding that userOwnsCat() check probably won’t be such a mission now!

A little extra flow control inside the if blocks is required to make this code behave the same as the nested version, but it’s nothing that can’t be handled by throwing an exception or returning from the function.

This no-nesting strategy only requires a small change in code structure, but it can make a big difference to readability when you otherwise might be dealing with some form of multi-level-soup. Use at your own discretion.