Why should you return early?

When I started my journey as a computer programmer I had written my code in a various way. I met some condition and I just put the correct statement regardless of the general concept or something more deeply. “It works, that’s it” – and the code base grew and grew in time. Thousands of written methods later I had discovered that a lot of these little pieces of logic could look better if I reverse some condition within them. Unconsciously, I’ve started following the rule which I had later called the “fail fast” rule.

I realized I hadn’t had the one way to check conditions within the single method. Sometimes I let execution if the expression was true. Another time I had waited for the false result to show the error message before the main part of the code was executing. In my head, I discovered various styles of checking conditions.

Two approaches to check requirements

The most basic style of checking if something meets the requirement is to simply put the expectation into the condition. There is one of example. I want to send a message only if the variable $email is the valid e-mail address and if the variable $message is not an empty string. Otherwise, I want to throw an exception.

function sendEmail(string $email, string $message) 
{  
    if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
    	if ($message !== '') {
        	return Mailer::send($email, $message);
        } else {
            throw new InvalidArgumentException('Cannot send an empty message.');
        }
    } else {
        throw new InvalidArgumentException('Email is not valid.');
    } 
}

Above snippet is one-to-one what I wrote before. Is it correct? Definitely. Is it readable? Not quite.

What is wrong with moving requirements literally to the code?

We can point 2 main problems with sendMail() method:

  1. It has more than one level of indentation within the method body
  2. The success path isn’t knowing without analyzing the flow of method

Since the first point concerns the “Clean Code” side, let me focus on the second one. The Shown code has a couple of branches and the return statement isn’t clearly visible (I mean, you have to look for it). This example is really dummy and short but try to imagine the longer method with more arguments and conditions (or even probably more than one return statement). Have you tried? Some pieces of code are awful but the small change could improve readability drastically. The only thing you have to do is reverse conditions.

function sendEmail(string $email, string $message) 
{  
    if (! filter_var($email, FILTER_VALIDATE_EMAIL)) {
		throw new InvalidArgumentException('Email is not valid.');
    }
  
    if ($message === '') {
      	throw new InvalidArgumentException('Cannot send an empty message.');
    }
  
    return Mailer::send($email, $message);  
}

I admit that I also remove one else after the second condition. I know, it’s subjective but in my opinion, this code looks much cleaner and prettier than the previous version. And the only thing I did is reversing conditions within if statements.

What I reach by reversing conditions?

First, the max indentation level within method body is one and we got rid of the nested condition. Thanks to these operations, the code is more readable and it’s easier to understand. The final instruction (so this is what we really have to do) at the end of the method, so it’s easy to find. The flow within this method is clear and visible.

Now you probably guess what I meant by calling this approach “fail fast”. Unfortunately, I figured out that Jim Shore, together with Martin Fowler had defined “fail fast” before I created my very first “Hello World” app. The goal is almost the same but I decided to change the name of my approach to “return early”. I don’t have to say that later on, I figured out that this is already defined pattern. But I’m glad I had noticed it on my own! Because of this, I’ll continue using the second name, which in my opinion is more accurate for purpose of this article.

Now, let’s get a closer look at the “return early” concept.

“Return early” concept

Basically, you should know what is the “return early” rule. Nonetheless, I want to try to define this concept by myself

Return early is a conception of writing functions (methods) so that the expected positive result is returned at the end of the function and the rest of the code should cause as soon as possible the termination in case of divergence with the purpose of this function.

You don’t believe, how long I have been thinking about the formal definition for this. Anyway, this is a general conception. What is “positive result”, “termination” or “function purpose”? I’m going to explain these issues more precisely.

Follow the “happy path

The pot of gold is at the end of the rainbow“. Do you remember this story? Maybe did you find one? This is similar what the function that follows “return early” concept looks like. The expected, success result of the function is always placed at the end of this function. Just look at the above sendEmail example. Our goal was to send a message for the e-mail address. This is our “success path” or, as the header says, the “happy path”. Happy, because this is the expected result and this is the reason why we call this function.

happy path diagaram
The main path within the function is clearly visible.

By placing the final operation at the end of the function we always know how our “happy path” looks like and what is the “function purpose” (besides its name of course). This is our expected positive result. No guessing, no analyzing – quick look should be enough to find out what is the goal.

Get rid of bad cases early

Code execution is a making constant choice. Our “happy path” is the best result that we got from the function. But not always the execution goes in the right way (in the context of function purpose). That’s ok. You can come back to our sendEmail example. If someone provides an invalid e-mail address then he’ll cause an expected bad case. We don’t want to pass an invalid e-mail address to the further execution. We should terminate the execution or simply return from function earlier.

The termination could be either the return or the exception. The choice is yours. You should use the way that is more convenient for you or what it would fit in a particular case.

Many people may feel awkward by using more than one return statement per single function. If the language gives you a possibility to explicitly define return value. I don’t see any reason why you shouldn’t use it. Even more gladly when it gives you real advantage e.g. better readability and maintainability.

The Bouncer Pattern

That is simply what I described in the previous section. It could be called as “Assertions” or even “Guard clauses“. It means exactly the same and it prevents a code execution in case of the invalid state. Take a look at this code snippet:

function matrixAdd(array $mA, array $mB) 
{
 	if (! isMatrix($mA)) {
        throw new InvalidArgumentException("First argument is not a valid matrix.");
    }
  
  	if (! isMatrix($mB)) {
        throw new InvalidArgumentException("Second argument is not a valid matrix.");
    }
  
  	if (! hasSameSize($mA, $mB)) {
        throw new InvalidArgumentException("Arrays have not equal size.");
    }
  
    return array_map(function ($cA, $cB) {
        return array_map(function ($vA, $vB) {
            return $vA + $vB;
        }, $cA, $cB);
    }, $mA, $mB);
}

The first part of the code is simply responsible for checking if an operation is able to proceed. The return statement contains the main logic of our function. In the ideal world, the first part of the code wouldn’t be needed.

Return early from the controller’s action

Controller’s action is a great candidate to use a “return early” approach. Actions often do much more things before they return a final response. Let’s consider below updatePostAction method that comes from the PostController.

/* PostController.php */

public function updatePostAction(Request $request, $postId)
{
    $error = false;
    
    if ($this->isGranded('POST_EDIT')) {
        $post = $this->repository->get($postId);

        if ($post) {
            $form = $this->createPostUpdateForm();
            $form->handleRequest($post, $request);

            if ($form->isValid()) {
                $this->manager->persist($post);
                $this->manager->flush();

                $message = "Post has been updated.";
            } else {
                $message = "Post validation error.";
                $error = true;
            }
        } else {
            $message = "Post doesn't exist.";
            $error = true;
        }
    } else {
        $message = "Insufficient permissions.";
        $error = true;
    }

    $this->addFlash($message);

    if ($error) {
        $response = new Response("post.update", ['id' => $postId]);
    } else {
        $response = new RedirectResponse("post.index");
    }

    return $response;
}

Such a big piece of code here. It has one return statement and a lot of nested conditions. I don’t know what you feel looking at this code snippet but I feel that it should be a better way to do this. Another code sample utilizes the “return early” approach.

/* PostController.php */

public function updatePostAction(Request $request, $postId)
{    
	$failResponse = new Response("post.update", ['id' => $postId]);

    if (! $this->isGranded('POST_EDIT')) {
    	$this->addFlash("Insufficient permissions.");
      	return $failResponse;
    }
  
  	$post = $this->repository->get($postId);

  	if (! $post) {
    	$this->addFlash("Post doesn't exist.");
    	return $failResponse;
  	}
  
    $form = $this->createPostUpdateForm();
    $form->handleRequest($post, $request);

    if (! $form->isValid()) {
        $this->addFlash("Post validation error.");
		return $failResponse;
    }
  
    $this->manager->persist($post);
	$this->manager->flush();

	return new RedirectResponse("post.index");
}

Now you can observe that this action has a clearly visible the “happy path”. It’s not one-to-one as the previous examples but it follows the same conception. If you expect that something may go wrong, check it and return early! Above code has a one-level indentation and, in my opinion, is much more readable than before. Just note, I don’t need to use the else keyword anywhere!

Return early from recursive functions

Also recursive function always should have a terminator that can looks like “return early” statement. This is simple example:

function reverse($string, $acc = '')
{
	if (! $string) {
        return $acc;
    }
  
  	return reverse(substr($string, 1), $string[0] . $acc);
}

Objections about “return early” conception

The main question is – what is the advantage of this concept? Why should I care about this? Or when I shouldn’t?

Problem 1: Code style is subjective

We, as programmers, spend much more time reading code than writing. That’s common true. Therefore, we should write code which is easy to read and understand. I proposed to react on falsy state first, not only because it improves readability but also it ensures that the main gear within the function is always at its end (or really close). In other words, we see expected success result as our “happy path”.

But the code style is very subjective. Someone may feel better with single return statement or with multiple assignments instead of writing the same method call again and again (like addFlash in the above code snippet).

functions placeholders
Could you imagine these two hypothetical functions? Which of them seems to be simpler?

Problem 2: Sometimes “return early” is an exaggeration

I know a lot of cases where applying “return early” approach has a negative effect on the code. Think about these all setters where you have to set a field value when the param is different than false:

public function setUrl($url) 
{
	if (! $url) {
        return;
    }  
  
  	$this->url = $url;
}

That’s not a good way to improve code’s readability. You should know where and when you can use common techniques. This situation definitely shows that sometimes is good to ignore rules.

public function setUrl($url)
{
	if ($url) {
    	$this->url = $url;
	}
}

Problem 3: It looks like break statement

Because function may have more than one return statement, we don’t accurately know where from is the result. As long as you use “early returns” to stop reaching an invalid state you shouldn’t care about this. Of course, are you writing tests?

Problem 4: Sometimes it’s ok to use a variable instead of many returns

There are some structures where “returning earlier” doesn’t change anything. Please look at these two examples of the same function. This is a part of code from the “game of life” algorithm where function calculates the next state of the cell:

function nextState($currentState, $neighbours)
{
    $nextState = 0;

    if ($currentState === 0 && $neighbours === 3) {
        $nextState = 1;
    } elseif ($currentState === 1 && $neighbours >= 2 && $neighbours <= 3) {
        $nextState = 1;
    }

    return $nextState;
}

The same function but using “return early” looks as below:

function nextState($currentState, $neighbours)
{
    if ($currentState === 0 && $neighbours === 3) {
        return 1;
    }

    if ($currentState === 1 && $neighbours >= 2 && $neighbours <= 3) {
        return 1;
    }

    return 0;
}

So, as you see there is no a big advantage in this case.

Are you wondering what is a “happy path” in the last code snippet? That’s the interesting thing. The name of this function is not so meaningful but the hidden goal of this function is to “kill” a cell. Everything that keeps our cell alive goes to the invalid state. So, maybe do you have a better proposal for the name for this function?

Conclusions

The way how we write a code simply comes from our customs. It’s hard to say that something is better than other when the final outcome is the same. It’s very subjective like many more things in the programming.

“Return early” concept introduces some rules. Rules that you shouldn’t blindly follow. As you can see there are a lot of cases where this approach helps and otherwise, where it doesn’t change anything. It’s very important to keep a discipline during writing a code because the consistent code style is much more important than anything else when it comes to the cognitive load.

I personally use “return early” as often as I can. It means that I use it where I see the advantage of returning early from functions, e.g. because of invalid state or when this operation improves readability. I’m used that success is always at the end of the function. The hidden mantra sounds:

Follow the success path (happy path) and react in case of errors.

Resources

Featured Photo by Franz Spitaler on Unsplash

2 Comments on "Why should you return early?"


  1. Hi,

    I’d love to mention that I have translated your article to Russian and posted it on the largest Russian-speaking tech community portal – Habrahabr.

    The discussion in over 150 comments, 15K views, 100 saved pins and dozens of likes reveal how interesting the topic you’ve covered is. Thanks! Maybe you’d like to use Google Translate and at least see what people think about it.

    I am a bit upset that some portals just copy-paste my translation, even though I’ve used personal linguistic techniques. Pity, but that’s the Internet. One of them wasn’t afraid to attach the link here in comments, Jesus.

    Wish you all the best and keep writing interesting posts!

    Link to the article: https://habrahabr.ru/post/348074/

    Reply

    1. Hi Tima,

      I’m glad that you’ve enjoyed this article. Thank you for sharing and spreading it further 🙂 . I really appreciate that!

      All the best for you!

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.