# The benefits of early returns

> Should you use early returns, or avoid them? Summary: I recommend using early returns to reduce nested branching, and Guard clauses to state preconditions and separate the happy path. Before that, though, it is best to first think about whether there is a way to eliminate the branching itself.

Should you use <early returns>, or avoid them? Summary: I recommend using <early returns> to reduce nested branching, and [Guard clauses](https://wiki.g15e.com/pages/Guard%20clauses.txt) to state [preconditions](https://wiki.g15e.com/pages/Precondition.txt) and separate the happy path. Before that, though, it is best to first think about whether there is a way to eliminate the branching itself.

## Criteria for "good"

If discussion of what is "good" turns into a discussion of the aesthetics of code, it can become overly subjective or speculative, so let us think about it based on objective indicators. Two criteria come to mind.

- **Understandability**: which code is easier for humans to read and understand?
- **Testability**: which code is easier to test?

Half a century ago (the 1960s), performance was also an important issue in discussions like this (for example, when discussion was in full swing about [eliminating GOTO](https://wiki.g15e.com/pages/Go%20to%20statement%20considered%20harmful.txt) and introducing <structured programming>, there was an argument that in certain situations where one needed to break out of nested loops all at once, using GOTO was the only way to do so most efficiently), but things have changed a great deal since then, so I will not consider performance.

## Cyclomatic complexity

As a metric for quantifying testability and understandability, [cyclomatic complexity](https://wiki.g15e.com/pages/Cyclomatic%20complexity.txt) was proposed in 1976, and it is fairly widely used. If you use JavaScript, you can limit cyclomatic complexity with <ESLint>'s ["complexity" rule](https://eslint.org/docs/latest/rules/complexity). (However, the default is set to 20, which is far too loose.)

Cyclomatic complexity quantifies how many linearly independent paths there are through the code. The lowest possible score is 1 (if there is no branching at all, there is 1 path).

For example, the following code has a cyclomatic complexity of 3:

```javascript
function doSomething(x, y) { // + 1 (default)
	if(x) { // + 1
		if(y) { // + 1
			return 1
		} else {
			return 2
		}
	} else {
		return 3
	}
}
```

What if we use <early returns> to remove nesting?

```javascript
function doSomething(x, y) { // +1 (default)
	if(!x) return 3 // +1
	if(y) return 1 // +1
	return 2
}
```

It has become much easier to read, but it is still 3. **Contrary to a widespread misunderstanding, <early returns> or [Guard clauses](https://wiki.g15e.com/pages/Guard%20clauses.txt) do not affect cyclomatic complexity.**

As in this case, there are quite a few instances where the correlation between the cyclomatic complexity score and actual understandability is weak. This is one of the shortcomings of cyclomatic complexity.

## Cognitive complexity

One proposal intended to address this shortcoming is [cognitive complexity](https://wiki.g15e.com/en/pages/Cognitive%20complexity.txt). Since it was first proposed in 2016,[^1] it has been updated steadily.[^2] If you use ESLint, you can install [eslint-plugin-sonarjs](https://www.npmjs.com/package/eslint-plugin-sonarjs) and set the "sonarjs/cognitive-complexity" rule.

Unlike cyclomatic complexity, cognitive complexity gives extra weight to nested branches. As the nesting level increases, it adds 1 more point each time. For example, the code below had a cyclomatic complexity of 4, but its cognitive complexity is 6. (Cognitive complexity starts from a base score of 0, so it is 6, not 7.)

```javascript
function doSomething(x, y, z) {
	if(x) { // + 1
		if(y) { // + 2
			if(z) { // + 3
				return true
			}
		}
	}
	return false
}
```

If we rewrite the code above as follows, the cognitive complexity drops to 3. (Meanwhile, cyclomatic complexity is still 4.)

```javascript
function doSomething(x, y, z) {
	if(!x) return false; // +1
	if(!y) return false; // +1
	if(!z) return false; // +1
	return true
}
```

The code above can be reduced further as follows, and if we do that, the cognitive complexity becomes 1. (Cyclomatic complexity is still 4.)

```javascript
function doSomething(x, y, z) {
	if(!x || !y || !z) return false; // +1
	return true
}
```

The reason it is 1 is that no additional score is assigned when the same logical operator is used consecutively in the condition of a branch statement. In the expression above, the logical OR operator (`||`) is used repeatedly, so the entire conditional is counted as 1 point. This is because [cognitive complexity](https://wiki.g15e.com/en/pages/Cognitive%20complexity.txt) was designed with actual understandability in mind. By contrast, [cyclomatic complexity](https://wiki.g15e.com/pages/Cyclomatic%20complexity.txt) is still 4, because the x, y, and z contained in the condition are each counted as 1 point.

Meanwhile, the code above can be reduced even further as follows. (Assume `x`, `y`, and `z` are `boolean`.)

```javascript
function doSomething(x, y, z) {
	return x && y && z; // +1
}
```

Interim summary:

- <Early returns> can improve understandability because they flatten nested branches.
- However, there is no difference under the [cyclomatic complexity](https://wiki.g15e.com/pages/Cyclomatic%20complexity.txt) metric, and it can be quantified only if you use a metric that gives extra weight to nested branches, such as [cognitive complexity](https://wiki.g15e.com/en/pages/Cognitive%20complexity.txt).
- If the context is to quantify understandability rather than <branch coverage>, it is better to use [cognitive complexity](https://wiki.g15e.com/en/pages/Cognitive%20complexity.txt) rather than [cyclomatic complexity](https://wiki.g15e.com/pages/Cyclomatic%20complexity.txt).

## Testability

Now let us talk about testability. Testability indicates how easy a given module is to test, and "easy" can mean several things.

- If a function <has many parameters> or creating each argument is tricky, testing that function becomes harder. In such cases people use things like <Mock object>s, but that can often be a design problem. See also: <Mock object smell>
- If a function has many branches, more test cases are needed to raise coverage.
- If a function's happy path and its other paths are explicitly distinguished, writing test cases becomes easier.

Since we are talking in the context of <early returns>, what concerns us here is branching and paths.

First, let us consider <branch coverage>, the criterion for evaluating whether every branch has been covered by tests. From the perspective of branch coverage, the number of test cases required generally matches the [cyclomatic complexity](https://wiki.g15e.com/pages/Cyclomatic%20complexity.txt) score.

In the case of the `doSomething()` example above, the CC was 4, and the following four test cases are needed:

- when all are `true`, check whether it returns `true`
- when only `x` is `false`, check whether it returns `false`
- when only `y` is `false`, check whether it returns `false`
- when only `z` is `false`, check whether it returns `false`

But as we saw earlier, <early returns> do not affect the cyclomatic complexity score. Therefore, **using early returns does not reduce the number of test cases needed to increase branch coverage, either.**

If you want to improve the testability of individual functions while keeping the number of test cases the same, you have to split functions into smaller pieces (<Extract function>). Breaking functions up into smaller pieces helps lower <path coverage> more than <branch coverage>. Path coverage means coverage not of individual branches, but of combinations of branches (that is, the function's overall execution paths).

Eliminating the branches themselves by generalizing the logic is also a good method, but it is not directly related to <early returns>.

Next, let us think about the testability of functions whose happy path is clearly distinguished. Using [Guard clauses](https://wiki.g15e.com/pages/Guard%20clauses.txt) to check preconditions and clearly separate exceptional paths from the happy path has a definite advantage in that, although it does not affect [cyclomatic complexity](https://wiki.g15e.com/pages/Cyclomatic%20complexity.txt), it helps with writing test cases and can make the correspondence between the actual code and the test cases more explicit.

## Eliminating the branching itself

<Early returns> necessarily presuppose branching. It is meaningful to leave the branching in place and discuss how to handle it, but the best thing is to find a way to eliminate the branching altogether.

If you do <object-oriented programming>, you can refactor by <replacing conditionals with polymorphism>, and you may also consider the <Null object pattern> (in fact, the Null object pattern is a special case of the refactoring <replacing conditionals with polymorphism>).

Another method is to simplify the logic through mathematical or logical generalization. As a simple example, code like this...

```javascript
dir++;
if(dir >= 360) dir = 0;
```

...can be changed like this (expressing the concept of cycling with the remainder operator):

```javascript
dir++;
dir %= 360
```

In more cases than one might think, it is possible to improve code so that branching disappears entirely from a function, or so that a function can be expressed as a single formula.

## Only one return per function?

Meanwhile, there has long been a claim that it is better for a function to have only one `return`, but <Kent Beck> says this claim is no longer valid.[^3]

> The "rule" about having a single return for a routine came from the days of FORTRAN, where a single routine could have multiple entry and exit points. It was nearly impossible to <debug> such code. You couldn't tell what statements were executed.
>
> The "rule" about having a single return for a routine came from the days of FORTRAN, where a single routine could have multiple entry and exit points. It was nearly impossible to debug such code. You couldn't tell what statements were executed.

## Conclusion

From the standpoint of understandability:

- <Early returns> can improve understandability because they flatten nested branches.
- However, there is no difference under the [cyclomatic complexity](https://wiki.g15e.com/pages/Cyclomatic%20complexity.txt) metric, and it can be quantified only if you use a metric that gives extra weight to nested branches, such as [cognitive complexity](https://wiki.g15e.com/en/pages/Cognitive%20complexity.txt).
- If the context is to quantify understandability rather than <branch coverage>, it is better to use [cognitive complexity](https://wiki.g15e.com/en/pages/Cognitive%20complexity.txt) rather than [cyclomatic complexity](https://wiki.g15e.com/pages/Cyclomatic%20complexity.txt).

From the standpoint of testability:

- From the perspective of <branch coverage>, <early returns> do not improve testability.
- If you use [Guard clauses](https://wiki.g15e.com/pages/Guard%20clauses.txt) to separate the happy path and flatten nested branches, the code's execution paths are revealed more easily, so it can help with writing tests.

For these reasons, I recommend <early returns> for flattening nested branches, and [Guard clauses](https://wiki.g15e.com/pages/Guard%20clauses.txt) for clarifying [preconditions](https://wiki.g15e.com/pages/Precondition.txt) and neatly separating the happy path. Before that, though, it is best to first think about whether there is a way to eliminate the branching itself.

## Footnotes

[^1]: [Cognitive Complexity, Because Testability != Understandability | Sonar](https://www.sonarsource.com/blog/cognitive-complexity-because-testability-understandability/)
[^2]: [Cognitive complexity: a new way of measuring understandability](https://wiki.g15e.com/pages/Cognitive%20complexity%20-%20a%20new%20way%20of%20measuring%20understandability.txt)
[^3]: [Tidy first? A personal exercise in empirical software design](https://wiki.g15e.com/pages/Tidy%20First%20-%20A%20personal%20exercise%20in%20empirical%20software%20design.txt)