I agree with much of the article (return early, errors management at the top, etc.). But won't returning a "wrong" value for the sake of one less line worsens the readability of the code?
When I saw that, I imagined myself spending 10 minutes trying to understand why he does that. But maybe I'm not familiar with a JS best practice here.
The `err, result` function signature implies the function is an asynchronously executed node-style callback, which can never return anything anyway. Well, it can return something, but nothing internal reads it and there's no way for user code to access the returned value so return becomes only useful for short-circuiting.
The goto fail snippet doesn't conform to the "one logical statement per line" guideline in the article though, so the author would advise against this style too.
Goto is heavily used to return early, by jumping to the cleanup section at the bottom of the function. Otherwise, you have to repeat the cleanup code at each exit point, or use the dreaded pyramid of doom where each failure point introduces another indent level, which is what the author here is trying to avoid in the first place.
"cleanup section at the bottom of the function" is pretty rare in JavaScript, which is what the original blog post is discussing. Same with Java, C#, Ruby etc.
No, it doesn't play well with early return, so avoid mixing them.
Not a C-programmer at all but I would separate out the resource creation/access from acting on the resource. So Open X, Do Y on X, Close X would have do Y on X in a separate function and possibly Open X and Close X as well. This way Close X would be executed regardless of the result of Do Y on X.
Only by doing multiple things in one function you really need to use goto's in C. (again, as far I can judge coding in C, not an expert!)
The "Avoid Else, Return Early" blog post above appears to be about JavasScript code, which (along with Java, C#, Python, Ruby etc) do not ready do "buffer frees (cleanups)" much. They have other language and runtime mechanisms for this such as GC and try...finally blocks, which work fine with early return.
Yes, in the presence of teardown code at the end of the method, as is common idiom in C, avoid early return. This appears to be the case for apple's "goto fail" code.
"Code should be written in a way it is more readable, not shorter."
Personally I think sometimes shorter code, that gets rid of unnecessary bureaucracy, is more readable. For example, C++ recently (in the past decade...) gained range based iteration.
Previously:
for(containe_type::iterator i = my_container.begin(); i != my_container.end(); i++){
do_stuff(i);
}
Now:
for(auto& i : my_container)
{
do_stuff(i);
}
This is really context sensitive. Some code benefits from verbosity while other don't.
This particular refactor (changing `if (err) { return ... }` to `if (err) return ...`) didn't actually reduce indentation levels though. It only reduced the number of lines.
In general terms, in C-like languages, mixing braces and semi-colons is horribly refactor unfriendly and bug prone. Meanwhile, sticking things on one line introduces a cognitive load that you might not want. (YMMV on the second point, but I’ve pretty strong views on the first.)
Ceteris paribus, shorter code always is more readable. It's the only guideline I've found true regardless of programming language or environment. Shorter is better.
It depends. Short, dense code can make the code more difficult to understand/change later, while overly bloated code can have the same effect.
I often find inelegant, yet straightforward solutions are generally better options than dense, mathematically pure solutions, simply because inelegant code usually relies on fewer assumptions. Noting that code rarely/never evolves the way we expect it to, apply Occam's razor and strike a balance.
Shorter code may or may not make things more readable. If brevity was the key to readability, we'd all be minifying all code we write to make it more readable.
Readability is about clarity of intent and function, as well as sensible structure. Sometimes that means writing slightly more code with more expressive naming so that the code is easier to follow. Sometimes that means using a given language's shortcuts to condense things down.
The functional style that you show certainly has multiple points where a value is returned, so you could call it "early return"; even if as a syntactic shorthand the keyword "return" is omitted. This is common across a lot of functional languages.
So value_in_cents can return a value of 1, 5, 10 or 25.
Saying that it "does not have a return statement" might be truish - but the fact that the "return" keyword is not needed to return values is just syntax.
Saying that there's no early return / multiple exit points is not so much. To my eye there are 4 places where that function can return a value. And the cases might not be simple numbers making the whole thing an expression, later examples on that page show how the cases can differ in side-effects.
I would agree. Putting "early out" logic at the beginning allows the one who reads the code to lower the cognitive load a bit. Basically "I know I can read the following code safely because the error handling and parameter sanitizing is done". But that only works with mundane operations (error handling, parameters sanitizing). If the operations convey some important semantics, then I prefer to see the guarded code inside an if block...
If something is truly exceptional, this is a particularly good use for Debug.Assert. You have it fail in debug mode to help catch edge-cases but pass but returning early production (which may be more performant or otherwise wanted instead of throwing an exception).
Big blocks of if/else logic impose a heavy cognitive load. I find that anytime your eyes have to jump more than a few lines there's higher mental overhead
I was more or less required to use guard clauses by my manager at my previous job.
I used to hate being forced to use them, just because as a recent computer science graduate I was happy with nested conditionals everywhere. But guard clauses make life so much easier, I eventually discovered.
They make testing your code a lot easier, and also make reasoning about the code and reading it so much easier.
Ruby makes writing methods with guard clauses a breeze, since you can just write something like `return if x > 7`.
A big gotcha with `&&` as a guard in JS is that it can return any falsey typed value if you don't coerce the guard to a boolean.
e.g.
const check(cond) => cond && otherValue
If `cond` is falsey, it returns `cond`.
This means the function could return any of: `false`, `undefined`, `null`, '', `0` or `NaN`.
This is a fairly common issue I see with React + JSX:
<div>{cond && <SomeComponent />}</div>
If `cond` is `false`, `null`, `undefined` or '', everything will be just fine, but if `cond` happens to be a zero or `NaN`, suddenly you have a weird `0` or `NaN` rendering in your page. Oops.
That's the problem with antipatterns. It's impossible to remain alert to the edge cases, and then they eventually bite you in production. Better to lint them away. But it's difficult to convince people not to do something really convenient if they haven't gotten bitten yet.
> which might promote misunderstanding of the behavior
This isn't a misunderstanding, binary logical operators in JS short-circuit like this by design. I believe && and || returned a boolean value in the past, but were explicitly changed to support this behaviour.
The production LogicalANDExpression : LogicalANDExpression && BitwiseORExpression is evaluated as follows:
1. Evaluate LogicalANDExpression.
2. Call GetValue(Result(1)).
3. Call ToBoolean(Result(2)).
4. If Result(3) is false, return Result(2).
5. Evaluate BitwiseORExpression.
6. Call GetValue((Result(5)).
7. Return Result(6).
That is, the behavior has always been "If the first expression is false-ish, return the first expression, otherwise return the second expression" ("BitwiseORExpression" is a class of expressions that include a lot of things, including equality operators).
JavaScript does not have any operators that is not explicitly listed in a version of ECMA-262. It would be correct to refer to the construct as a "guard", but incorrect to refer to it as a "guard operator". Calling it "guard operator" also does not promote an understanding of the underlying construct.
ECMA-262 defines ECMAScript. "JavaScript 1.3" refers to a Netscape-specific language implementation ("Mocha", "LiveScript", "JavaScript"), with version 1.3 being based partially based on ECMA-262 Second Edition. Some of it might still live on in Firefox, but it is certainly not what people refer to as "JavaScript".
I learned some Swift when it as at 1.2 and I'm happy to see it evolve so quickly and take common statements, like the optional unwrapping "very long `if let` statement" mentioned article, and simplify it. I really should get back into Swift.
I work in Python, and one thing I do if I end up needing to use the same guard clause in several functions is to turn it into a decorator. That way, if I need to update the logic, I can do it in one place and have it propagate everywhere and it makes the function's dependencies clear at first glance.
This was my introduction to aspect-oriented programming, which I've thoroughly fallen in love with.
As usual I found myself writing these more and more before finding out there was a word for them.
Are there any preprocessors or libraries to make statements like this easier in Javascript? Sometimes the guard statements can be longer than the function code and I go back and forth on whether to throw (forces the caller to anticipate) or return (causes lots of silent failures).
That's awful advice. Obviously _someone_ has to use their judgement to _create_ the rules. But if you have a large codebase that lots of people are interacting with, letting everyone "use their judgement" is going to create an unreadable mess of code, because you're going to have different patterns and approaches all over the place. It would be like writing a book and one paragraph is in English, the next in Spanish, the third in French, then the fourth goes back to Spanish, and on and on.
I worked in a shop that lived by "use your judgement", and it was really the cleanest body of code that I've ever worked in with more than 30 people, in 20 years of professional coding.
Why? Because people with bad judgement were mercilessly brow-beaten into developing better judgement.
We had lots of different styles of code hanging around, and you could tell if code had been written by our CTO, our leads, different guys in different teams, etc. The one rule was "write like a chameleon, and make your code look like the code it is in." (okay... to make that work, the other was "spaces, not tabs")
The code was generally very readable, and it was a very adult codebase. The problem with hard rules is that comprehensive codification of complete rulesets is fundamentally intractable. You're always going to run into areas that don't fit, or the ruleset will be laughably overwrought (and, thus, impossible to put into practice).
You could call it the code-zealot incompleteness theorem...
Of course, if I get to make all the rules and have no obligation to be complete, consistent, or considerate, I can get on board with this plan.
> The one rule was "write like a chameleon, and make your code look like the code it is in."
I bounce between many code bases in multiple languages, and this has always been my rule. I first figure out why/how are things done, and then do them the same way within reason. If the existing code has something done in a way that is no longer valid/correct, then we have discussion about changing all of the code.
"Unless he is certain of doing as well, he will probably do best to follow the rules." - Strunk, The Elements of Style
Your advice is unfortunately common but misguided. Without a firm and intuitive grasp of "rules" and why they exist, one cannot build a framework for making decisions with which one can use their judgment. On top of that, most programmers aren't advanced enough to have more reliable judgment than "the rules" and few working groups can possibly survive a bunch of programmers "using their judgment" all over a nontrivial codebase even when those programmers are that advanced.
"Unless he is certain of doing as well, he will probably do best to follow the rules." - Guy who wrote the rule book
A better way of saying it might be: If you don't understand why the rule exists and don't have time to learn, follow the rule. When you follow the rule you may discover why it exists, when you don't follow the rule you will almost certainly discover why it exists.
Judgment is subjective so this pattern fails when there are more than one person reading the code or the same person at different time.
You end up with code written in several different styles within the same codebase. Assumptions that don't hold, etc. Much harder to reason about and maintain.
I usually prefer explicit if...else blocks even when all or some of them return, because it's always good to see the big picture with blocks that are executed only on certain condition. In other words, see the code tree almost literally.
I don't think the latter is legal in most (non-JS) languages if the return value of the outer function is void.
Even if it is, I definitely agree that it should only be done if you want to communicate that handleError's return value is meaningful and should bubble up.
And in non-promise-based async JS, the return value is almost always lost/useless anyway so `return x` has no effect, might as well repurpose `return` for short-circuiting.
Because if you don't have resource cleanup to do in the method, the GOTO boilerplate is just making things unnecessarily more complicated than a simple return.
> If you have a complicated function, and several exit points where you want to bail out, they why not just put a "label: bail" and goto it?
Because go-to is so infrequently a good choice and do frequently used when wrong that most languages don't have it at all, so it's not an option. It may be a reasonable.
choice when available, especially if you aren't breaking structure and thereby making it harder to follow the logic.
I do this all the time in Ruby, Python, JavaScript because the code is so easier to read. Error management tends to stick to the beginning of functions/methods and the main algorithm doesn't have to be indented.
With Ruby it's even cleaner because of the postfix conditionals.
def something()
return value2 if error1
return value2 if error2
do_something
end
The return values in case of errors stand out, the conditions for the errors do not clobber the code because they are sidelined.
I switched to using this style in the last few years, but it only works well where the code is reasonably good quality otherwise, in particular with short methods and classes. If you're dealing with legacy code with massive procedural style methods having return statements scattered around makes things even more confusing.
Since it forces you to write short methods everything you add or change on the existing code will automatically improve the code quality. Also depends on your refactoring tools, C# with ReSharper can reliably refactor without side-effects.
I think there's a lot of value to returning early out of functions when you hit an error condition. There's not much reason to go further if you're just completely hosed. (e.g. malformed arguments).
However, I don't think that coders should shy away from built-up result values for return. It makes the code more friendly to things like copying (I can feel the boos and hisses now), block restructuring, #ifdef'ing (or some equivalent), short-circuiting, and debugging. If you have one easy breakpoint to set that still has a stack so you can peek at a result, it's pretty darned convenient.
So, yes, sure, tend to error out first (this is not news), but don't just litter returns all over the place in code that's going to have to be used and modified by lots of people. The goals for practical code should revolve around the useful lifecycle of that code. Make it readable, portable, mutable, ibleable, ableible, etc.
In many cases, carrying a return value on the stack is a decent way to do this. In some contexts, particularly where branching is expensive, it can have performance implications as well.
Guard clauses aren't only for errors. They do however usually involve certain kinds of asymmetry. Typically: The early return case does fewer things (or nothing) than the major case. Or the early return case is normal, but has a 'negative' sense (which again does less work) like not finding a key in a dictionary.
I think the statement (ish) “don’t use if/else, use return” deserves some qualification, and one natural place to use it is for errors.
When people start dropping returns all over massive multi-screen branching and looping structures, readability may suffer, and debugability quite often takes a big hit.
In general, I like to use guard clauses for any big at-entry branch asymmetries. If they’re farther down the logic tree and/or unwieldy, it may be time to call some more functions.
This refactoring is so simple and obvious I don't even understand why some people don't write like this by default. I've had colleagues comment my PR on my "failed validation, return early" code saying "we generally strive to maintain one single point of return". I mean, why would you want only one return even?
Are you saying that closing files is now a cargo-cult thing to do? There are plenty of resources that are more important to clean up than memory. This has nothing to do with C.
I’ve always been a fan of refactoring deeply nested if/else into a more linear less nested sequence of if/return statements.
If the function spec is a sequence of complex AND checks which must be satisfied in order to do work, an unnested sequence of “if (!x) return” statements are logically equivalent, have less indentation, and I think it’s much cleaner and easier to read comment blocks above each IF explaining the reason why each check is there.
I also really like seeing the main body of the function which is doing the real work at be bottom without any nesting/conditionals around it at all. It helps you find it if it’s always down there at the end, rather than in the middle indented way to the right.
This often becomes a non-issue if your functions are short anyway. Having this discussion can sometimes be a code smell in itself.
Obviously it's impossible to completely generalise over, but sometimes it's worth asking yourself if the function is doing too much. In saying that though, early returns are great for input validation.
Before I heard about the single exit point rule, I have coded during many years using multiple exit points. I am complying with the rule for many years and I am quite happy with it. The code is slightly more nested, very slightly longer to type. But OTOH, the style checker can spot mistakes in my code and there is less thinking about how I will structure my code to make it smarter.
IMHO, the gain is small but not smaller than the cost.
It's interesting when style becomes rules, the years goes by and no one challenge the "best practice", languages and frameworks might change, but the rules stay, for no good reason. So yeh, why only one return !?
> It's interesting when style becomes rules, the years goes by and no one challenge the "best practice",
FWIW, I would start making plans to leave a company where any significant amount of my colleagues followed rules like this without challenging them or being able to articulate their rationale.
It depends! But it depends on the actual meaning of the code, so examples which use meaningless identifiers like `doStuff()` miss the distinction.
If the conditions are semantically symmetrical, if/else is the right approach:
fun max(a, b) {
if a > b { a } else { b }
}
But is it a precondition which causes you to skip the primary logic of the function, then get it out of the way early:
fun max(a, b) {
// special case for null
if a==null or b==null { null }
if a > b { a } else { b }
}
Yeah we want to reduce indentation, but not at the cost of making the code overall harder to follow. The logic of "max" is much clearly expressed as a single expression with `else`.
The logic would be convoluted if you are going through extra hoops in order to write your logic like this, making the flow of the application unclear. For some things, an else branch ends up just being easier to deal with. This is especially true in languages like Rust, where if/else is an expression, leading to the following construct being used often (although usually with more complicated content):
let max = if (x > y) {
x
} else {
y
};
Note that "max" is immutable despite the conditional assignment due to the if being used as an expression.
I think it's only clear and concise looking because of the achilles heel of this entire thread: terse examples. As soon as you need to do more than a single line of work, the else'd version becomes far more readable (due to superior indentation and clearer guarantees of what happens when).
If it's a single line of return in both branches, then a ternary expression is usually going to be ideal instead.
We can definitely agree that the examples are too simple to present any readability issues with any method of writing them short of an IOCCC entry.
I do not agree with the conclusions you draw at all, but it is hard to continue the discussion without examples. There are definitely cases where an else clause is the natural choice, but I believe that these are in the minority.
If you’ve ever had to deal with various errors or to filter out something and return the appropriate output, it is much more concise. Read past the if’s and you have the main logic of that function.
But that max function should have an else statement since it's part of the logic. Less code doesn't always make it more concise. If there's a more complicated logic, then it'd actually be harder to understand at a glance.
The examples are only similar to the extent that they do the same. If you look at what intent they communicate - which is an important part of readability - the second example indicates that x>y is a "special case", which just obscures the simple logic of the function.
I agree that what they communicate is important, but I disagree that the early return communicates "x>y" as a special case.
Nothing about early return indicates a special case, but rather just indicate that a conclusion has been reached. A few examples:
1. A function that compares two arrays, and first checks if they are null or if their lengths differ before checking their individual elements, and potentially recursing. The early returns are likely to be the hottest section of the function, with the element checking being the special case.
2. A function that searches a list or tree for a node that matches a set of conditions. All but at most one run will use the early returns, making the corpus of the function the special case.
3. A function that does some processing, with fast paths that handle the vast majority of data, but a slow path for when the fast paths do not apply. The fast path is an early return, but the slow path is the special case.
In other words, I believe that it is incorrect to consider an early return to be a special-case, and interpreting code like so might result in misunderstandings. You should look at the condition to see if it is a special case. The only thing an early return indicate is that the return value has been decided, and no further processing is needed.
I still think that all my examples communicate the exact same to the reader.
I guess I would politely argue that creating new functions would make reading code harder in basic example such are this. You'll be jumping all round or possibly separate files. Obviously larger logic tress should be pruned. Side note: a lambda function may get around this specific instance.
The simple cases do come up though. I've written plenty of functions which amount to little more than a single if-statement and I have had people ask me to make changes like
Regardless of bracing style, I still interpret `return a` as a special case with respect to control flow due to the indentation. `return b`, on the other hand, is on the "happy path" — if this wasn't simply a max function, I'd assume from the indentation that it'd return `b` most of the time.
A max function is one of those rare moments where a ternary statement just seems right, but it's an admittedly simple example.
>I still interpret `return a` as a special case...
I think this is interesting. I wonder if our personal experiences with how we learned programming, maybe first languages or first teachers or jobs, etc... would effect our perceptions on logic flow.
I simple don't see anything unusual about the logic flow in the example. It's a simple if/else - return with less cruft to me.
Possible, and also shaped with recent language experiences. My first languages were JavaScript and C++ pretty much simultaneously. In both I'd write a max function with a ternary operator. It signals to me — in the same way as if/else — that no outcome is expected to be more usual or typical than the other, and that no path is more notable or interesting that the other.
The control flow isn't confusing either way, and some language linters will insist against if/else in this example, but I see the if/else approach with slightly more clarity. In general, though, I'm a big proponent of early return.
The earlier version is clearly better and more clear, but presumes a language where if is an expression (and the actual return keyword isn't required, though “return if ...” is nearly as good.)
But if you are using a more imperative language where “if” is a statement, I think the merits of early return vs. use of an else clause are more balanced in this case.
Functionally, yes, in terms of readability, not really. But, yes, for really simple cases like the example under discussion:
return (a>=b) ? a : b
is probably the best option in those languages. The level of complexity at which the C ternary operator ceases to be ideal, I think, is lower than that of expression-if.
The pre-conditions are called Guard Clauses [0]. It's really useful to think about guard clauses and explicitly look for them to ensure the pre-conditions are met rather than have them appear over time as if/else blocks.
edit: now that I've looked through the comments I see these are mentioned on numerous other threads.
If I have a choice of using if (err) or if (!err) I'm going to use if (err) to avoid the negation. Same conclusion ultimately, but for a different reason. Bit of a discussion about it here: https://softwareengineering.stackexchange.com/questions/3504...
I was made to have only one exit point for some projects and it drove me up the wall. It only really makes sense if you're coding in languages like C. I don't like functions written this way because they're longer, are harder to read, have complex nesting and most of all they introduce state (the variable that stores the result). In dynamic languages, you're not going to get warned if you forget to set the result variable and if you forget the variable can be returned as a null which is a horrible source of bugs.
It should be easier to reason about code when there is exactly one exit from a block. I am not sure this really applies when we are talking about guard clauses that are not deeply nested. So loop breaks and other shallow returns are OK, and can be good.
It was just cargo cult programming as far as I could see where what one senior developer said was taken as gospel. I tried arguing objectively against practices like this and several others but was ignored.
It was so bad, I remember one time some of the junior programmers literally shaking their heads when they saw some functional programming language examples for the first time because multiple returns are idiomatic there. They couldn't explain why multiple returns were bad, just that they were bad.
I don't find this uncommon though and understanding it's a time saving heuristic. People will accept a "best practice" from an authoritative source without question and only later start thinking about it objectively when it gets in their way somehow.
If your team is ignoring objective arguments though then you're in a cargo cult.
In Ruby these are idiomatic enough to have a name and are built into the linter, so it'll yell at you if it sees that you're not using guard clauses.
As a further refinement, I will often take tricky conditional logic and put them into a method that consists of nothing but guard clauses and a return true or false at the bottom of the method. In Ruby you can use ? and ! at the end of method names, so I'll give the method an intention-revealing question-mark name. "Order.used_credit_card?"
I use this in Javascript whenever I can too, though of course it's not as semantically nice as Ruby.
I would love Ruby-style guard clauses in JavaScript. Sometimes I wish I’d never written anything in Ruby just so I’d stop thinking about how great it would be if other languages shared some of its features.
1. This is common in Go;
2. It is not about saving lines or curly braces. It is about the flow of code, and branching away when expectations are violated. It is about helping readers of code establish a mental model of the intentions of code.
I have read somewhere else (https://softwareengineering.stackexchange.com/questions/1187...) that the "single exit" rule was not actually about having only one place where the function returns, but actually about having a single place where the function returns to.
That rule seems to have been so successful, that no modern language I know of allows (the original definition of) multiple entry or multiple return (except for exceptions).
Forth may not qualify as "modern" for some (though it still has a way of staying around in some contexts, like bootloaders), but it does allow code to store to and retrieve from the return stack, which is also used for loops.
If you read that and thought "does that mean I can change loop iteration order and returns programmatically?!?" and/or "WTF?!", the answer is, yes, to all of it. WTF indeed.
I've never actually seen a non-pathological use for this language feature.
600 comments
[ 3.1 ms ] story [ 294 ms ] threadWhen I saw that, I imagined myself spending 10 minutes trying to understand why he does that. But maybe I'm not familiar with a JS best practice here.
No, it doesn't play well with early return, so avoid mixing them.
All make the "goto manual cleanup at end" less necessary, and make early return easier to use.
Only by doing multiple things in one function you really need to use goto's in C. (again, as far I can judge coding in C, not an expert!)
Yes, in the presence of teardown code at the end of the method, as is common idiom in C, avoid early return. This appears to be the case for apple's "goto fail" code.
However, don't generalise this to all languages.
Personally I think sometimes shorter code, that gets rid of unnecessary bureaucracy, is more readable. For example, C++ recently (in the past decade...) gained range based iteration.
Previously:
for(containe_type::iterator i = my_container.begin(); i != my_container.end(); i++){ do_stuff(i); }
Now:
for(auto& i : my_container) { do_stuff(i); }
This is really context sensitive. Some code benefits from verbosity while other don't.
I don’t agree with him about removing the braces, though. That way madness lies.
I often find inelegant, yet straightforward solutions are generally better options than dense, mathematically pure solutions, simply because inelegant code usually relies on fewer assumptions. Noting that code rarely/never evolves the way we expect it to, apply Occam's razor and strike a balance.
Readability is about clarity of intent and function, as well as sensible structure. Sometimes that means writing slightly more code with more expressive naming so that the code is easier to follow. Sometimes that means using a given language's shortcuts to condense things down.
[1] http://wiki.c2.com/?GuardClause
It's not "early return" any more than:
e.g. https://doc.rust-lang.org/book/second-edition/ch06-02-match....
So value_in_cents can return a value of 1, 5, 10 or 25.
Saying that it "does not have a return statement" might be truish - but the fact that the "return" keyword is not needed to return values is just syntax.
Saying that there's no early return / multiple exit points is not so much. To my eye there are 4 places where that function can return a value. And the cases might not be simple numbers making the whole thing an expression, later examples on that page show how the cases can differ in side-effects.
https://gist.github.com/jwilk/6e7ecb94b623a82d01cdcd9765f05b...
I used to hate being forced to use them, just because as a recent computer science graduate I was happy with nested conditionals everywhere. But guard clauses make life so much easier, I eventually discovered.
They make testing your code a lot easier, and also make reasoning about the code and reading it so much easier.
Ruby makes writing methods with guard clauses a breeze, since you can just write something like `return if x > 7`.
e.g.
If `cond` is falsey, it returns `cond`.This means the function could return any of: `false`, `undefined`, `null`, '', `0` or `NaN`.
This is a fairly common issue I see with React + JSX:
If `cond` is `false`, `null`, `undefined` or '', everything will be just fine, but if `cond` happens to be a zero or `NaN`, suddenly you have a weird `0` or `NaN` rendering in your page. Oops.Low cost, much safer guard:
That's the problem with antipatterns. It's impossible to remain alert to the edge cases, and then they eventually bite you in production. Better to lint them away. But it's difficult to convince people not to do something really convenient if they haven't gotten bitten yet.
Same behavior on Ruby:
If you start thinking it is a "guard operator", I would think that going down the wrong path, which might promote misunderstanding of the behavior.
This isn't a misunderstanding, binary logical operators in JS short-circuit like this by design. I believe && and || returned a boolean value in the past, but were explicitly changed to support this behaviour.
JavaScript does not have any operators that is not explicitly listed in a version of ECMA-262. It would be correct to refer to the construct as a "guard", but incorrect to refer to it as a "guard operator". Calling it "guard operator" also does not promote an understanding of the underlying construct.
1: https://www.ecma-international.org/publications/files/ECMA-S...
The ECMA spec only defines Javascript 1.3 and above. See this description for logical operators for Javascript 1.1: https://web.archive.org/web/20060318153542/wp.netscape.com/e...
Doing some research, it appears that Netscape changed the logical operator behavior in version 1.2 (https://web.archive.org/web/19981202065738if_/http://develop...).
However, they did not highlight this change at all (https://web.archive.org/web/19970630092641fw_/http://develop...), so I suspect it was just a minor cleanup, potentially related to the equality operator change.
As a side-note: Netscape scripting was awful. This was how you casted an object to a Number:
https://hexdocs.pm/elixir/master/guards.html
I work in Python, and one thing I do if I end up needing to use the same guard clause in several functions is to turn it into a decorator. That way, if I need to update the logic, I can do it in one place and have it propagate everywhere and it makes the function's dependencies clear at first glance.
This was my introduction to aspect-oriented programming, which I've thoroughly fallen in love with.
Are there any preprocessors or libraries to make statements like this easier in Javascript? Sometimes the guard statements can be longer than the function code and I go back and forth on whether to throw (forces the caller to anticipate) or return (causes lots of silent failures).
It would be great to use a defined approach.
This article also discusses the solutions (there's a lot of overlap with OP's article).
http://wiki.c2.com/?ArrowAntiPattern
Why? Because people with bad judgement were mercilessly brow-beaten into developing better judgement.
We had lots of different styles of code hanging around, and you could tell if code had been written by our CTO, our leads, different guys in different teams, etc. The one rule was "write like a chameleon, and make your code look like the code it is in." (okay... to make that work, the other was "spaces, not tabs")
The code was generally very readable, and it was a very adult codebase. The problem with hard rules is that comprehensive codification of complete rulesets is fundamentally intractable. You're always going to run into areas that don't fit, or the ruleset will be laughably overwrought (and, thus, impossible to put into practice).
You could call it the code-zealot incompleteness theorem...
Of course, if I get to make all the rules and have no obligation to be complete, consistent, or considerate, I can get on board with this plan.
I bounce between many code bases in multiple languages, and this has always been my rule. I first figure out why/how are things done, and then do them the same way within reason. If the existing code has something done in a way that is no longer valid/correct, then we have discussion about changing all of the code.
Your advice is unfortunately common but misguided. Without a firm and intuitive grasp of "rules" and why they exist, one cannot build a framework for making decisions with which one can use their judgment. On top of that, most programmers aren't advanced enough to have more reliable judgment than "the rules" and few working groups can possibly survive a bunch of programmers "using their judgment" all over a nontrivial codebase even when those programmers are that advanced.
A better way of saying it might be: If you don't understand why the rule exists and don't have time to learn, follow the rule. When you follow the rule you may discover why it exists, when you don't follow the rule you will almost certainly discover why it exists.
You end up with code written in several different styles within the same codebase. Assumptions that don't hold, etc. Much harder to reason about and maintain.
Even if it is, I definitely agree that it should only be done if you want to communicate that handleError's return value is meaningful and should bubble up.
So people reading this code may have to stop and puzzle out this little pattern.
Here you can replace “ void “ with a new line and have code anyone with basic familiarity with a c-like language can read and understand intuitively.
GOTO has a bad rap IMO. There's a place for it, and the author should probably be using it.
Because go-to is so infrequently a good choice and do frequently used when wrong that most languages don't have it at all, so it's not an option. It may be a reasonable. choice when available, especially if you aren't breaking structure and thereby making it harder to follow the logic.
With Ruby it's even cleaner because of the postfix conditionals.
The return values in case of errors stand out, the conditions for the errors do not clobber the code because they are sidelined.However, I don't think that coders should shy away from built-up result values for return. It makes the code more friendly to things like copying (I can feel the boos and hisses now), block restructuring, #ifdef'ing (or some equivalent), short-circuiting, and debugging. If you have one easy breakpoint to set that still has a stack so you can peek at a result, it's pretty darned convenient.
So, yes, sure, tend to error out first (this is not news), but don't just litter returns all over the place in code that's going to have to be used and modified by lots of people. The goals for practical code should revolve around the useful lifecycle of that code. Make it readable, portable, mutable, ibleable, ableible, etc.
In many cases, carrying a return value on the stack is a decent way to do this. In some contexts, particularly where branching is expensive, it can have performance implications as well.
I think the statement (ish) “don’t use if/else, use return” deserves some qualification, and one natural place to use it is for errors.
When people start dropping returns all over massive multi-screen branching and looping structures, readability may suffer, and debugability quite often takes a big hit.
In general, I like to use guard clauses for any big at-entry branch asymmetries. If they’re farther down the logic tree and/or unwieldy, it may be time to call some more functions.
Some other comments here discuss resource cleanup issues that can creep into C code with early return.
If you're not coding in C, then more likely it's pure cargo-cult.
If the function spec is a sequence of complex AND checks which must be satisfied in order to do work, an unnested sequence of “if (!x) return” statements are logically equivalent, have less indentation, and I think it’s much cleaner and easier to read comment blocks above each IF explaining the reason why each check is there.
I also really like seeing the main body of the function which is doing the real work at be bottom without any nesting/conditionals around it at all. It helps you find it if it’s always down there at the end, rather than in the middle indented way to the right.
It's important to know both patterns though, so you are able to choose depending on your situation.
Obviously it's impossible to completely generalise over, but sometimes it's worth asking yourself if the function is doing too much. In saying that though, early returns are great for input validation.
IMHO, the gain is small but not smaller than the cost.
What makes it a "rule" or as sometimes said, a "law"?
It's just a style, and it has pros and cons, cases where it helps, and cases where it hinders.
Because it was a good idea in C.
it's pretty much irrelevant to Java, JavaScript, C#, Ruby, Python, Rust etc.
But the rules stay, for no good reason.
FWIW, I would start making plans to leave a company where any significant amount of my colleagues followed rules like this without challenging them or being able to articulate their rationale.
http://www.anthonysteele.co.uk/TheSingleReturnLaw
If the conditions are semantically symmetrical, if/else is the right approach:
But is it a precondition which causes you to skip the primary logic of the function, then get it out of the way early: Yeah we want to reduce indentation, but not at the cost of making the code overall harder to follow. The logic of "max" is much clearly expressed as a single expression with `else`.Early returns are not a panacea. If/Else-everything is not a silver bullet either.
If it's a single line of return in both branches, then a ternary expression is usually going to be ideal instead.
I do not agree with the conclusions you draw at all, but it is hard to continue the discussion without examples. There are definitely cases where an else clause is the natural choice, but I believe that these are in the minority.
But that max function should have an else statement since it's part of the logic. Less code doesn't always make it more concise. If there's a more complicated logic, then it'd actually be harder to understand at a glance.
Nothing about early return indicates a special case, but rather just indicate that a conclusion has been reached. A few examples:
In other words, I believe that it is incorrect to consider an early return to be a special-case, and interpreting code like so might result in misunderstandings. You should look at the condition to see if it is a special case. The only thing an early return indicate is that the return value has been decided, and no further processing is needed.I still think that all my examples communicate the exact same to the reader.
Ok, I really wonder if in this special case the logic just looks _odd_ because of bracing styles. (bear with me)
This is very easy to understand, where as the parent example, not as much.
A max function is one of those rare moments where a ternary statement just seems right, but it's an admittedly simple example.
I think this is interesting. I wonder if our personal experiences with how we learned programming, maybe first languages or first teachers or jobs, etc... would effect our perceptions on logic flow.
I simple don't see anything unusual about the logic flow in the example. It's a simple if/else - return with less cruft to me.
The control flow isn't confusing either way, and some language linters will insist against if/else in this example, but I see the if/else approach with slightly more clarity. In general, though, I'm a big proponent of early return.
But if you are using a more imperative language where “if” is a statement, I think the merits of early return vs. use of an else clause are more balanced in this case.
edit: now that I've looked through the comments I see these are mentioned on numerous other threads.
[0]: https://en.wikipedia.org/wiki/Guard_%28computer_science%29
Did they give any rationale for this?
It was so bad, I remember one time some of the junior programmers literally shaking their heads when they saw some functional programming language examples for the first time because multiple returns are idiomatic there. They couldn't explain why multiple returns were bad, just that they were bad.
I don't find this uncommon though and understanding it's a time saving heuristic. People will accept a "best practice" from an authoritative source without question and only later start thinking about it objectively when it gets in their way somehow.
If your team is ignoring objective arguments though then you're in a cargo cult.
As a further refinement, I will often take tricky conditional logic and put them into a method that consists of nothing but guard clauses and a return true or false at the bottom of the method. In Ruby you can use ? and ! at the end of method names, so I'll give the method an intention-revealing question-mark name. "Order.used_credit_card?"
I use this in Javascript whenever I can too, though of course it's not as semantically nice as Ruby.
http://ruby-hyperloop.org/
The guys in Gitter chat are quite helpful if you get stuck.
That rule seems to have been so successful, that no modern language I know of allows (the original definition of) multiple entry or multiple return (except for exceptions).
If you read that and thought "does that mean I can change loop iteration order and returns programmatically?!?" and/or "WTF?!", the answer is, yes, to all of it. WTF indeed.
I've never actually seen a non-pathological use for this language feature.
The functional equivalent would be (delimited) continuations and is extremely powerful (but potentially confusing in an untyped context.)