DEV Community

Cover image for Avoid indentation hell with early returns
Nevulo
Nevulo

Posted on • Originally published at nevulo.xyz

Avoid indentation hell with early returns

Fundamentally, programming in any language consists of executing code, and the code that gets executed changes depending on what state of the program the user is in.

For example, if we're making a game and the user has just won, we might want to make the background change to a green color. On the contrary, if the user loses, we'll change the background to a red color. Our program is simple enough in its current state, and we can visualize how it might look:

function game() {
  // ... game logic
  const winner = user.winner;
  if (winner) {
    background = "green";
  } else {
    background = "red";
  }
}
Enter fullscreen mode Exit fullscreen mode

That's simple enough to read.

What if we wanted to make it so when the user wins AND has a score over 900, they see a gold background? And just to spice things up, if a user has won but has a score of less than 300, they'll see a purple background.

function game() {
  // ... game logic
  const winner = user.winner;
  const score = user.score;
  if (winner) {
    if (score > 900) {
      background = "gold";
    } else if (score < 300) {
      background = "purple";
    } else {
      background = "green";
    }
  } else {
    background = "red";
  }
}
Enter fullscreen mode Exit fullscreen mode

It's not too difficult to represent all of these conditions in code, but you might be able to spot the problem with this code: if your boss wanted you to add more special cases like, for example, showing a rainbow background when someone has won with a score over 1500, or showing a black background if the player died.

All of these "branches" quickly grow out of control and it can start getting hard to follow what's happening, as the condition for showing the red background if the user hasn't won is in an else block which is pretty far down. For some, it takes effort to try and read this code to figure out what it does, and you might find yourself jumping up and down the code to fully understand it.

Let's try and take a different approach. Let's flip this on its head. Literally: let's flip the first if statement to check if the user is not a winner first.

function game() {
  background = getBackgroundColor(user);
}

function getBackgroundColor(user) {
  const winner = user.winner;
  const score = user.score;
  if (!winner) return "red";
  if (score > 900) return "gold";
  if (score < 300) return "purple";
  return "green";
}
Enter fullscreen mode Exit fullscreen mode

This is the same code as we saw above, including our special conditions to show a purple background only if the user has won the game and has a score of less than 300, and showing a gold background if the user is a winner but only if they have a score of over 900.

By returning as early as possible, it becomes a lot simpler to read this code because we know everything below the if (!winner) ... line won't be executed unless the user is a winner. If the user is not a winner, we return early setting background to red.

This has a few advantages over our code from earlier which used else heavily:

  • removes unnecessary code
  • reduces logical complexity
  • improves readability

Compare this block of code, which includes an if-else pattern to handle logic:

function game() {
  // ... game logic
  const winner = user.winner;
  const score = user.score;
  if (winner) {
    // 1. if "winner" is true...
    if (score > 900) {
      // 2. if "winner" is true and score is > 900
    } else if (score < 300) {
      // 3. else if "winner" is true and score is < 300
    } else {
      // 4. if "winner" is true and score is > 300 and < 900
    }
  } else {
    // 5. if "winner" is false...
  }
}
Enter fullscreen mode Exit fullscreen mode

(the numbers in the comments represent the way I personally read this code, from top to bottom)

... with this block of code which uses the early return pattern which is arguably a lot simpler and less lines of code:

function game() {
  // ... game logic
  const winner = user.winner;
  const score = user.score;
  if (!winner) return; // if "winner" is false
  // below will only execute if "winner" is true
  if (score > 900) return; // if "score" is > 900
  // below will only execute if "score" is < 900
  if (score < 300) return; // if "score" is < 300
  // below will only execute if "score" is > 300
  // this final statement will only be executed if:
  // * "winner" is true
  // * "score" is < 900
  // * "score" is > 300
  return;
}
Enter fullscreen mode Exit fullscreen mode

That being said, it's worth mentioning there's a time and place to use early return pattern, just like there's always time to use if/else. Both work just fine, but at the end of the day if you're not the only person who needs to read your code, it helps to try and make it as understandable as possible. There are certain cases where returning early is better, but there's also times where returning early too often can cause more confusion. This post on StackOverflow by Mark Kegel sums it up fairly well I think: it comes down to common sense and what you're trying to achieve with the function. Too much of anything is never good.

Top comments (0)