Avoid Else, Return Early
tldr;
- Return as soon as you know your method cannot do any more meaningful work
- Reduce indentation by using
if/returninstead of a top-levelif/else - Try keep the “meat” of your method at the lowest indentation level.
- Error handling is noise.
Programmers are often taught to have a ‘single exit point’ in their methods, i.e. only return from a single location.
function () {
var result;
if () {
result = x
} else {
if () {
result = y
} else {
result = z
}
}
return result // this return is single and lonely
}
This is a poor guideline in my opinion:
- “assign a result” doesn’t explain the intent: “this is the final value, processing stops here”
- Leaves question open “is the result object finished? can it be modified? by whom?”
- Allows accidental modification of the result
- Encourages “happy path” to be wrapped in one or more if/else statements
Example if/else refactoring
Consider this typical node callback code:
function(err, results) {
if (!err) {
doOtherStuff()
doMoreStuff()
// ... etc
// ... etc
} else {
handleError(err)
}
}
There’s a few problems here. First, our error handling is dangling off the end of the method. If the “happy path” is many lines long, it can easily become unclear what the else even refers to.
Let’s try keep the “meat” of the code at the bottom of the method, and keep any special cases together at the top:
function(err, results) {
if (err) {
handleError(err)
} else {
doOtherStuff()
doMoreStuff()
// ... etc
// ... etc
}
}
It’s very easy to get unweildy levels of indentation in JavaScript, so we should strive to reduce any unnecessary nesting.
In this case, we can remove the else indentation around
our “happy path” by replacing the else with a return:
function(err, results) {
if (err) {
handleError(err)
return
}
doOtherStuff()
doMoreStuff()
// ... etc
// ... etc
}
Not only does this unindent a bunch of code, it also moves the method’s main purpose/intention/meat to indentation level 0.
We often don’t care about return values in non-promise-based async JS, so we can
futher compact the method vertically by putting the return first,
removing a whole line and more braces:
function(err, results) {
if (err) return handleError(err)
doOtherStuff()
doMoreStuff()
// ... etc
// ... etc
}
2018 edit: To more clearly signal that the return value is unimportant you can use the void operator:
function(err, results) {
if (err) return void handleError(err)
// ...
}
It also obeys the “one logical statement per line” guideline, compacting the error detection and handling noise to a single line.
Another benefit is that the return keyword is generally syntax highlighted, so all exit points become very clear, as opposed to hidden inside result = something assignments.
This final form has:
- Method at lowest indentation level
- No unecessary indentation.
- Many fewer lines
Rebecca Murphey has also written about this
The end.
2018 edit: As with any programming practice, one shouldn’t see this as a hard rule that must be obeyed at all times. Early returns make little difference for small functions, and may even increase the cognitive load. However, I find the logic-flattening benefits of early returns become increasingly compelling as the size and complexity of a function increases.
Lots of discussion about this post:
Tweetclintfisher liked this
lala-valse liked this
darknets reblogged this from toxley-blog and added:
/me shall be writing way more code like this!! It always seems like I’m indenting the crap out of things this is a nice...
darknets liked this
vagorage-blog reblogged this from toxley-blog
toxley-blog posted this