DEV Community

Jemal Ahmedov
Jemal Ahmedov

Posted on

Flat is better than nested

Nesting code is a common feature in a programming language. While it has some advantages, it is mainly considered an anti-pattern. There are a number of problems when nesting code:

  • The code is hard to read
  • The context is hard to understand
  • Hard to maintain, whilst developers will try to avoid it

Let's explore different ways to invert nested conditions and make code easier to work with.

Executing functions

Inverting conditions

Consider the following example code:

function payBill(user = { isLoggedIn: false }, amount = 0) {
  if (user) {
    if (user.isLoggedIn) {
      if (amount > 0) {
        // pay the bill
      } else {
        throw new Error("Invalid amount");
      }
    } else {
      throw new Error("User needs to login");
    }
  } else {
    throw new Error("User doesnt exist");
  }
}
Enter fullscreen mode Exit fullscreen mode

This code has a nesting depth of 3 and this will probably continue if we need to cover different validation scenarios. Simply by inverting the conditions we can reduce the nesting depth to 1 as follows:

function payBill(user = { isLoggedIn: false }, amount = 0) {
  if (!user) {
    throw new Error("User doesnt exist");
  }

  if (!user.isLoggedIn) {
    throw new Error("User needs to login");
  }

  if (amount <= 0) {
    throw new Error("Invalid amount");
  }

  // Pay bill
}
Enter fullscreen mode Exit fullscreen mode

This code will work exactly the same way as the first example. The only difference is that it is easier to read, understand and maintain.

Early return

This is another solution that can be used in function as a guard clause before executing the function.

For example, instead of wrapping everything in a condition like this:

function doSomething(myParam) {
  if(myParam){
    // Do something here
  }
}
Enter fullscreen mode Exit fullscreen mode

You can simply revert the condition and stop the execution of the function when the condition is not met.

function doSomething(myParam) {
  if(!myParam){
    return;
  }

  // Do something here
}
Enter fullscreen mode Exit fullscreen mode

Loops

Building custom logic within loops is something common that we all do. We can definitely improve the way we execute loops by reducing the nested conditioning.

Here is an example with nested conditions:

const myList = ["one", "two", null, "four", "", "six"];
for (let i = 0; i < myList.length; i++) {
  const item = myList[i];
  if (item !== null) {
    if (item !== "") {
      // Do something with current item
      console.log(item);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Here is an improved version:

const myList = ["one", "two", null, "four", "", "six"];
for (let i = 0; i < myList.length; i++) {
  const item = myList[i];
  if (item === null) {
    continue;
  }

  if (item === "") {
    continue;
  }

  // Do something with current item
  console.log(item);
}
Enter fullscreen mode Exit fullscreen mode

By using the continue statement, the loop stops the current iteration of the loop and continues with the next iteration. This stops the nested conditions and makes the flow more linear, either stopping the iteration to go further down the block or continue with the next item.

The same can be done with .forEach loop.

const myList = ["one", "two", null, "four", "", "six"];
myList.forEach((item) => {
  if (item === null) {
    return;
  }

  if (item === "") {
    return;
  }

  // Do something with current item
  console.log(item);
});
Enter fullscreen mode Exit fullscreen mode

The return statement plays the same role as continue statement in for loop.

Conclusion

Minimizing nesting and applying these simple tricks can make your life much easier. It's not only going to make the code more readable and understandable, but it will also improve your coding style. Your teammates will thank you for that later.

Top comments (0)