600 comments

[ 3.1 ms ] story [ 294 ms ] thread
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.

returning a wrong result is definitely wrong. It conveys some unexpected meaning and side effects...
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.
"removing a whole line and more braces" - this is really fighting the wrong enemy. Code should be written in a way it is more readable, not shorter.
Exactly. This led to the famous OSX double goto bug [0]

  if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
    goto fail;
    goto fail;
  ... other checks ...
  fail:
    ... buffer frees (cleanups) ...
    return err;
[0] https://www.dwheeler.com/essays/apple-goto-fail.html
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` wouldn't be used in a return early philosophy.
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.

Actually, in Ruby we have "ensure"

    def foo arg
      return true if arg == 42
      puts "got past the guard"
      raise "blah"
    ensure
      puts "ensure always"
    end
    
    foo(42)
    foo(3)
The "ensure" blocks gets executed whether or not you return early, throw exception or return at the end of the block.
There are several constructs for it, see also "try ... finally" on C# and Java, and "using" statements in C#.

All make the "goto manual cleanup at end" less necessary, and make early return easier to use.

I should have specified that I was talking about C. You usually don’t need this pattern when you have garbage collection or C++ RAII.
(comment deleted)
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.

However, don't generalise this to all languages.

I don't really understand why the use of goto is to blame there. If it was not a double goto but a double return, that'd still lead to this bug, no?
"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.

I think the point OP’s trying to make is that reducing indentation levels makes code more readable.

I don’t agree with him about removing the braces, though. That way madness lies.

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.
Yeah, I’ll file that under “maybe don’t do that”.
what are the downsides
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.

No it doesn't depends. That's what ceteris paribus means.
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.

Seems to me that there is quite a bit of commentary on this subject, and you can find it by searching for its typical solution: guard clauses[1].

[1] http://wiki.c2.com/?GuardClause

Yeah, Swift in particular really embraces that pattern.
Also functional languages with function guards e.g. while Erlang/Elixir does not have a return statement (and thus early return) you can write

    handle(Err, Results) when Err /= undefined ->
        handle_error(Err);
    handle(_, Results) ->
        % etc…
        .
though errors would generally be reified as the ad-hoc union of two tuples and would look more like this:

    handle({error, Info}) ->
        handle_error(Info);
    handle({ok, Results}) ->
        % handle results
        .
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.
> The functional style that you show certainly has multiple points where a value is returned, so you could call it "early return";

It's not "early return" any more than:

    function(err, results) {
      if (!err) {
         // handle results
      } else {
         return handleError(err)
      }
    }
Right, though I was thinking of other examples of functional match style and returning a value. As seen in in Haskell, F# Rust etc.

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.

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
Definitely. But in that case, abstraction is my friend...
A more formal name for this approach is / could be Guard Clauses: https://refactoring.com/catalog/replaceNestedConditionalWith.... This pattern has been elevated to a language construct in Swift: https://thatthinginswift.com/guard-statement-swift/
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`.

guard also lets you unwrap your optionals and have them in scope for the rest of the function.
JavaScript has the guard operator. It can really clean up your control flow when you know it's a guard operator and not just logical AND.
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.

Low cost, much safer guard:

    const check(cond) => !!cond && otherValue
Good point. Good example. I imagine it works well in my cases but I haven't come across cases like your example where it breaks down.
...until you do.

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.

Yes, back when CoffeeScript was in vogue the ? operator handled this case nicely:

  cond? && otherValue
Quite interesting. For a moment I've thought it is another JS issue but no. It's React who's strange https://github.com/facebook/react/blob/v0.11.2/src/browser/u... https://github.com/facebook/react/blob/v0.11.2/src/utils/tra...

Same behavior on Ruby:

    def content_markup(children)
      case children
      when String, Numeric
        children
      when NilClass, TrueClass, FalseClass
        return
      else
        # ...
      end
    end

    content_markup 'foo'
    #=> "foo"
    content_markup 0
    #=> 0
    content_markup true
    #=> nil
    content_markup false
    #=> nil
    content_markup nil
    #=> nil
Logical AND in JavaScript can be used as a guard, by why in the world would you think of it as anything other than a logical AND?

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.

> 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.

Quoting ECMA-262 First Edition (June 1997), Chapter 11, Section 11 (Binary logical operators)[1]:

    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.

1: https://www.ecma-international.org/publications/files/ECMA-S...

> the behavior has always been

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...

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".

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:

    Number(x) = x;
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.
Swift also has the `defer` statement which makes this particular approach usable even if there is cleanup to do before the function can exit.
Yes, I love guard clauses!

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).

It would be great to use a defined approach.

Avoid rules, use your judgment.
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.
Sort of agree, but I don't think

    if (err) {
      handleError(err)
      return
    }
and

    if (err) return handleError(err)

are equally good. The second one doesn't really make it clear wether handleError returns a value and that value is intended to be returned.
I had to double check the function call occurred. It's not intuitive and removes braces in favor of possible side effects.
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.

If the return value is important:

    if (err) return void handleError(err)

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.
I don't think throwing void at people is generally going to make anything clearer.
why
I've never seen syntax like 'if (err) return void handleError(err)' before. If I saw that in JS I'd have to look up what it meant.
It’s an obscure operator. Many people are unaware of it and many of those that are have a vague notion of exactly what it does.

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.

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?

GOTO has a bad rap IMO. There's a place for it, and the author should probably be using it.

You're a brave person to say this in public. :D
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.

thanks for raising that up, it's always good to doubt anything obvious, however, it looks like it'll continue writing if-else complete code :)
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 agree with you.

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?
> I mean, why would you want only one return even?

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.

Right, I should've mentioned except arcane things (for us pleb web devs) happening in low-level languages.
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.

Depends on different factors, you can't just say one is better than the other. E.g. a function call itself might be quite expensive.

It's important to know both patterns though, so you are able to choose depending on your situation.

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.

> single exit point rule,

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.

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 !?
> So yeh, why only one return !?

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.

> 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`.
Exactly this. Anyone who put their shoe in functional programming can make such a distinction.

Early returns are not a panacea. If/Else-everything is not a silver bullet either.

How about "there are no silver bullets, there is no substitute for clear thinking."
You could still write it as

    fun max(a, b) {
        if a > b {
            return a
        }
        return b
    }
Not saying this is necessarily better or worse. The point I want to make is that your case isn’t special.
I am arguing this is worse, since it expresses the logic in a more convoluted way.
I would argue that it is by no means more convoluted. It is quite clear and concise. All of the following are equally readable:

    if (x > y) {
        return x;
    } else {
        return y;
    }

    if (x > y) {
        return x;
    }
    return y;

    return (x > y) ? x : y;
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

  foo(things) {
      stuff = do_work(things);

      if (stuff) {
          return a;
      } else {
          return b;
      }
  }
into

  foo(things) {
      stuff = do_work(things);

      if (stuff) {
          return a;
      }

      return b;
   }
in code reviews.
>I am arguing this is worse, since it expresses the logic in a more convoluted way.

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.

  fun max(a, b)
  {
    if a > b
    {
      return a
    }
    return b
  }
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.

In C style languages you have the ?: operator though, which is basically if as an expression.
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.
Yeah no hard rules, agreed on this point, especially when the if/else body is short.
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.

[0]: https://en.wikipedia.org/wiki/Guard_%28computer_science%29

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.
> I was made to have only one exit point for some projects

Did they give any rationale for this?

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.
I moved to Hyperloop on my personal projects. Give it a shot, I think you might like it.

http://ruby-hyperloop.org/

The guys in Gitter chat are quite helpful if you get stuck.

Not exactly the same but my JavaScript linter throws errors when I use "else" when it could be avoided.
That's called a predicate and is used in Scheme as well.
Guard clauses use predicates but not all predicates are guard clauses.
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.

Multiple entry/exit points is the defining characteristic of coroutines(as described by Knuth.)

The functional equivalent would be (delimited) continuations and is extremely powerful (but potentially confusing in an untyped context.)