Flat is Better Than Nested Versus Single Entry Single Exit

Dec 26, 2019

Programming

By: Brandon Quakkelaar

As a program ages and receives updates, more and more features are built. Logic is added and the code becomes increasingly more complicated to read and understand. So programmers invent recommended practices to manage the complexity and to preserve the maintainability of their code. The goal being to keep the cognitive load to minimum levels so the programmer can work efficiently.

One popular practice is to avoid nested code when possible. The concept that flat is better than nested has been widely adopted by Python programmers who have included it in their guidelines. Python programmers aren’t the only group to adopt this preference. Resharper, a code analysis tool popular among C# programmers, recommends coding styles that result in less nested code.

However, there’s another recommended practice called Single Entry Single Exit (SE/SE). When applied in modern languages, it avoids exiting a program early with multiple return statements. This can be in direct opposition to “Flatter is Better” since an important flattening technique is to return early.

public bool ValidatePassword(CredentialService credentials, string username, string password)
{
  var isValid = false;

  if (credentials.Exists(username))
  {
    if (!credentials.IsLockedOut(username))
    {
      if (credentials.IsHashValid(password, credentials.GetHashByUsername(username)))
      {
        LogValidPassword(username);
        isValid = true;
      }
      else
      {
        LogInvalidPassword(username);
      }
    }
    else
    {
      LogLockedOutUser(username);
    }
  }
  else
  {
    LogMissingUsername(username);
  }

  return isValid;
}

The above code is sound. But we can easily see that nested if/else statements quickly become difficult to read. And keep in mind that as this codebase matures it’s very likely that additional features, checks, and logging will be required by business rules.

Now, consider how easy it is to read the following code which uses multiple early returns to exit.

public bool ValidatePassword(CredentialService credentials, string username, string password)
{
  if (!credentials.Exists(username))
  {
    LogMissingUsername(username);
    return false;
  }

  if (credentials.IsLockedOut(username))
  {
    LogLockedOutUser(username);
    return false;
  }

  if (!credentials.IsHashValid(password, credentials.GetHashByUsername(username)))
  {
    LogInvalidPassword(username);
    return false;
  }
      
  LogValidPassword(username);
  return true;
}

My preference is to favor returning early. Composing functions such that there’s just one final return statement results in code that has more else statements and multiple levels of nesting. I find these are characteristics of code that’s difficult to read. A return is the end of function execution. When an early return is encountered then the final result is apparent. This is not the case when setting a result value which can be changed later in execution. So a programmer would need to handle the cognitive load of more scenarios with SE/SE than they would if early returns were adopted.

Origins of Single Entry Single Exit

The history of SE/SE is rooted in a pioneering programming paradigm called Structured Programming. The term Structured Programming was coined by Edsger Dijkstra. It’s used to refer to programming with control structures like if/then/else, loops like for and while, and subroutines. This paradigm gained popularity thanks in part to a 1968 letter called Go-to Statement Considered Harmful by Dijkstra.

The argument for SE/SE has it’s foundation in the argument against using goto. Essentially highlighting the benefits of avoiding flows that become tangled and obfuscated to the programmer. Such code is referred to as spaghetti code. Bertrand Meyer criticizes returning early, and even advises against using continue and break in loops. He writes about it in his book called Touch of Class: Learning to Program Well with Objects and Contracts. Regarding return, continue, and break he says they “are just the old goto in sheep’s clothing.” His opinion seems to be overly dogmatic to me because those three keywords always jump to the same place. A return will always exit the function. A continue will always jump to the top of the loop. A break will always exit a loop. Those behaviors are consistent and programmers have mental models for them and they’re transferable to other programs. This is unlike goto which requires the programmer to hunt for the location that’s being jumped to, and therefore they must create a new mental model for each flow.

Final Thoughts

SE/SE was for a time when the programmer handled the structure of the function rather than just the logic of it. Languages that offered multiple points of entry to subroutines can certainly become confusing. I agree that multiple entry points is a practice to be avoided. But, modern languages offer greater protections than earlier ones. So the rule is less relevant today.

My disagreement is specifically with the single exit rule. I think flat code is just easier to understand than nested. Now, there are cases where a single exit is important. Multiple exit points could certainly be an issue in languages like C which require you to clean up after yourself. But the rule is not nearly as useful for languages that have features like garbage collection, try...finally, and using blocks.