
Avoid Else, Return Early (2013) - signa11
http://blog.timoxley.com/post/47041269194/avoid-else-return-early
======
drawkbox
Programmers with lots of hours of maintaining code eventually evolve to return
early, sorting exit conditions at top and meat of the methods at the bottom.

Same way you evolve out of one liners.

Same way comments are extra weight that should only be in public or
algorithm/need to know areas.

Same way braces go on the end of the method/class name to reduce LOC.

Same way you move on from heavy OO to dicts/lists.

Same way you go more composition instead of inheritance.

Same way while/do/while usually fades away, and if needed exit conditions.

Same way you move on from single condition bracket-less ifs. (debatable but
more merge friendly and OP hasn't yet)

Same way you get joy deleting large swaths of code.

and many others on and on.

Usually these come from hours of writing/maintaining code and styles that lead
to bugs.

~~~
Merad
> Same way braces go on the end of the method/class name to reduce LOC.

Screw that. I've been writing code for 15 years, and Allman style braces make
it so much easier to mentally parse code into blocks that they're worth every
single LOC. I can't speak for anyone else but I'm not working on an 80x24
terminal anymore.

~~~
optimuspaul
Totally disagree with you.... it's funny though that I read LOC as "level of
complexity" not "lines of code". I've been writing code for 30 years and I
think it's jarring when the braces are on the next line, so much easier for me
to parse that when it's on the same line. But everyone is entitled to their
own opinion.

~~~
alunchbox
python fans are going like "what are braces?"

~~~
kabdib
That's fine -- the Python folks have their own share of religious wars
(starting with: tabs, or spaces?) :-)

~~~
tom_mellior
Do they? All the Python folks I've ever seen express an opinion on style have
said "follow PEP 8".

Unsurprisingly, PEP 8 does have a rule for tabs vs. spaces:
[https://www.python.org/dev/peps/pep-0008/#tabs-or-
spaces](https://www.python.org/dev/peps/pep-0008/#tabs-or-spaces) (spaces, of
course)

~~~
sireat
I love Python but as a long time C programmer I can't understand the
preference for spaces in PEP 8.

Tabs are semantic and only take one key press for movement back and forth and
to delete.

If you see 1 tab you know it meant one indentation level. With spaces you have
to think.

Plus with spaces you are stuck with 2/4/8 spacing(unless you reformat), with
tabs you can configure your editor to your preferences.

~~~
slaman
> with tabs you can configure your editor to your preferences.

Configure 1 tab to be 4 spaces.

Sometimes it's easier to go with the flow. I like tabs for the reasons you
mentioned, but fixed-width spaces are a bit better for some reasons too. IDE's
can do the heavy lifting of re-formatting indentation levels and converting
tabs to spaces for me, and it means if I cat a file on a remote server
regardless of the bash tab width settings or if I'm in your code or the stdlib
it will all make sense.

------
Cthulhu_
A more formal name for this approach is / could be Guard Clauses:
[https://refactoring.com/catalog/replaceNestedConditionalWith...](https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html).
This pattern has been elevated to a language construct in Swift:
[https://thatthinginswift.com/guard-statement-
swift/](https://thatthinginswift.com/guard-statement-swift/)

~~~
toomanybeersies
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`.

~~~
rimliu
guard also lets you unwrap your optionals and have them in scope for the rest
of the function.

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

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

~~~
olavk
I am arguing this is worse, since it expresses the logic in a more convoluted
way.

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

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

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

------
simias
I always found this type of "general purpose" coding rules rather useless and
often counter-productive because some coders (especially beginners) will often
cargo-cult and apply them without rhyme or reason. I've known coders who had
the opposite rule: never have more than one return per function, use if/elses
for the control flow (TFA mentions that). I don't know where this rule came
from and neither did they, it's just how they were taught I suppose. Replacing
that with yet an other "rule of thumb" is a bad idea IMO.

Writing clean and maintainable code is as art and requires balancing many
variables. Sometimes an early return is warranted, sometimes an if/else might
make more sense. If you're writing C code and you need to release some
resource before returning (mutex, file handle etc...) then an early return
might be error-prone and an "else" or "goto" might result in cleaner and more
readable code. I use gotos a lot in error handling in C, I think it's a
superior pattern to the one proposed by TFA if you have a lot of cleanup to
do. In C++ and other object-oriented languages it's less of an issue thanks to
RAII and/or GC.

Beyond that if/else might be more readable if both clauses are on the same
"level" semantically. For instance if you write:

    
    
        if (some_cond) {
            do_a();
            return;
        }
    
        do_b();
    

It looks like `do_a` and `do_b` are not on the same "level", semantically
speaking. If instead they're two variations of the same concept then an else
might make more sense.

~~~
timoxley
I'm trying to imagine worst case scenarios here, and at least in an
environment like JavaScript, I'm struggling to see how a beginner being
overeager with early returns can possibly create anywhere near the same degree
of mess as a someone overusing nested if/else statements.

Agreed on early returns being questionable for "two variations of the same
concept" though, these days I generally use if/else for situations like that.

------
arximboldi
Mmm, I disagree.

Influenced by functional programming, I prefer to use the style of: "keep all
return statements at the same indentation level" in statement based languages.
This way, it is easier to parse as an expression. For example:

    
    
      if (...) {
        let a = ...;
        return x(a);
      } else {
        return y;
      }
    

Can be easily mentally factored into the pseudo-expression:

    
    
      return ... ? x(...) : y;
    

It is easier to see what the side-effects (or ideally lack of) are. It also
makes case analysis easier, and you don't hide the fact that you have
2^{indentation levels} number of possible states.

Early return while excusable for some very particular and idiosyncratic error
handling examples (e.g. fortified C APIs that accept null pointers as no-ops)
in general feels like "cheating", making the code look less complicated than
it actually is (it "silently" multiplies the size of your state space without
increasing indentation). But most importantly, it puts too much emphasis on
control flow: I'd rather emphasize the underlying declarative intent, the
state machines, and pre/post-conditions. This is is better achieved, in my
opinion, by trying to delay returns, and to try to minimize the amount of code
after a branching (this often means having unnecesary else branches, that on
the other hand help with readability.) As shown before, this helps mentally
factoring the code into reducible expressions and guessing state space and
overall complexity.

I agree though that having one single-return is not good, cuz most of the time
it forces one to use mutable variables that could otherwise be avoided.

~~~
brandmeyer
> ... makes case analysis easier ...

> ...making the code look less complicated than it actually is...

> I'd rather emphasize the underlying declarative intent, the state machines,
> and pre/post-conditions.

So much this. The goal of refactoring code for readability is not to make it
parse more like spoken language (ie, English). Its to aid in understanding and
analysis. Having code layed out on the page in a way that mirrors the true
control flow graph, makes understanding the CFG easier. Having a data flow
graph that mirrors the control flow graph makes understanding the DFG easier.

~~~
timoxley
> ... makes case analysis easier ...

> ...making the code look less complicated than it actually is...

> I'd rather emphasize the underlying declarative intent, the state machines,
> and pre/post-conditions.

These statements are strange to me as I personally see early returns as
attempts as achieving exactly these goals. I guess people unroll logic in
their head in different ways. "flat" to one may appear "nested" to another and
vice versa.

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

~~~
fen4o
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](https://www.dwheeler.com/essays/apple-goto-fail.html)

~~~
dep_b
`goto` wouldn't be used in a return early philosophy.

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

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

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

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

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

~~~
davewritescode
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

~~~
wiz21c
Definitely. But in that case, abstraction is my friend...

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

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

~~~
jmull
I don't think throwing void at people is generally going to make anything
clearer.

~~~
timoxley
why

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

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

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

~~~
Raphmedia
Not exactly the same but my JavaScript linter throws errors when I use "else"
when it could be avoided.

------
scraft
I think the real transcendence is when you try out many different programming
paradigms (all of which have loads of good reasons to use) and overtime find
out the shortcomings of each. Then just pick the right tool for each use case.

The general information here I think is good and I roughly follow. I have also
done almost the opposite and removed as much code outside of the hotpath as
possible from the top of the function for performance reasons (guided on
profiling). Also there can be times where you jump to a function to see what
it does and can't actually see as all of the edge cases come first rather than
the bread and butter.

Often you need to be careful when thinking a new paradigm is fixing a problem,
like handling the err variable being passed in, and question why there is an
err parameter at all. Where does it come from. What are you actually meant to
do when it happens? Does every case always just log it, or throw it? Is the
logic being performed at the right level? Etc. Sure the answer maybe yes
everything is correct but there will be lots of times that the complexity just
shouldn't be there.

Horses for courses.

------
citation_please
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](http://wiki.c2.com/?GuardClause)

~~~
kdazzle
Yeah, Swift in particular really embraces that pattern.

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

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

~~~
masklinn
> 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)
          }
        }

~~~
SideburnsOfDoom
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....](https://doc.rust-lang.org/book/second-
edition/ch06-02-match.html)

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.

------
dragontamer
This blogpost's analysis is incomplete. It needs to discuss the core issue
with WHY C programmers originally avoided multiple return statements. Its
pretty simple:

    
    
        void function(){
            lock_mutex();
            void* thingy = malloc();
    
            if(...)
            {
                return; // Bug!! You forgot to unlock the mutex
                // Bug!! You forgot to free(thingy);
            }
    
            // Imagine 100 lines of code here
            
            free(thingy); // Hard to remember with 100 lines of code in the way
            unlock_mutex();
        }
    

When functions grow to hundreds of lines of code, you need to ensure that all
cleanup functions are called. Modern programming languages have features to
handle this case "automatically" (Python "with", C++ RAII, Java finally).
Which is why its fine today.

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

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

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

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

------
cesarb
I have read somewhere else
([https://softwareengineering.stackexchange.com/questions/1187...](https://softwareengineering.stackexchange.com/questions/118703/where-
did-the-notion-of-one-return-only-come-from/118793#118793)) 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).

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

------
camgunz
I had a coworker who was fanatical about removing else blocks, and would
refactor entire files so that else wouldn't be necessary.

This blog post is about guard clauses and it's good advice. But if you find
yourself refactoring huge chunks of code to avoid a keyword, even making code
more complex as a result, stop and consider that you may be making things
worse, and maybe look at your other habits to see if you're doing the same
thing elsewhere.

------
ssharp
I've been slowly trying to get back into hobby/side development after not
being a professional developer for 11 years. Even when I was a full-time
developer, I was never really in a "junior" role or dropped into an existing,
quality codebase, so much of what I was doing was from scratch. Although my
coding did improve as our team grew and it forced me to be more disciplined, I
never learned a lot of little hints like this that make your code much
clearer.

In getting back into it, I've relied a lot of Laracast's tutorials in both
learning Laravel, along with how much PHP has evolved in the past 11 years,
but also some of his code quality videos, which included one on this very
topic! After seeing that, I immediately started refactoring some recent code I
had written with the goal of flattening it to as few indents as possible by
eliminating else's and elseif's. Sure enough, I was much happier with the
refactored code than I was with the original, even if the output of the code
was the same.

~~~
Azareus
I would strongly recommend Gary Bernhardt's _Destroy All Software[0]_
screencasts. They're usually done in Ruby but independent from language, and
more about concisely explaining general programming concepts.

Everything is crisp, and distilled down as far as possible. I've learned a lot
just from the thoughts it has given names to and elaborated on.

If you're skeptical, there's even free trial episodes that you could give a
shot first. (I would recommend "Functional core, imperative shell")

[0]:
[https://www.destroyallsoftware.com/screencasts](https://www.destroyallsoftware.com/screencasts)

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

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

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

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

------
louthy
There are major issues with not using _else_ \- in that there is no obvious
way to test whether it _should_ be there or not. Any programmer returning to
your code (or even you returning to your code a year later) can't know what
the intention was/should be, and neither can the compiler.

I try (in C#) to avoid if/else completely (not always possible, but it's a
general guide) and I try to work with ternary expressions instead. This forces
you to explicitly consider the else case and explicitly state what the
intention was. It also makes composition of code blocks simpler and often
removes the issues of statement ordering.

You can take it further by using Option\Either\Validation monads to reduce the
cyclomatic complexity [1]. This approach can allow for composable and reusable
validation computations making the job of validation trivial.

By the way, this is more of a general point about if/else rather than the very
simple case of argument validation that the blog talks about. I don't
particularly have a major issue with a series of _early outs_ in any function.
But if you want to do anything more complex, ignoring _else_ can be a source
of bugs.

[1] [https://github.com/louthy/language-
ext/blob/master/LanguageE...](https://github.com/louthy/language-
ext/blob/master/LanguageExt.Tests/ValidationTests.cs#L140)

~~~
seanmcdirmid
You aren’t really reducing complexity with the monadic approach, just
replacing explicit control flow for implicit control flow masked as data flow.
Debugging can become harder in the latter case.

~~~
louthy
Which is written once, rather than 1000s of times. That's a reduction in
cognitive load and the same 'missing else' bugs I mentioned.

I agree it's not always easier to debug - but mostly (I find) writing code as
pure expressions rather than a sequence of statements reduces the need for
debugging massively. So, I don't think it's quite as black and white as you
paint.

~~~
seanmcdirmid
No, you usually have to debug just as much, you just don't have any nice tools
to do it with. I agree the monadic approach looks cleaner, but just be ready
to undo it for any kind of hairy logic that requires debugging.

~~~
louthy
I have not found that and use it extensively. So, I respectfully disagree.

Debugging isn’t significantly harder, it’s _a bit_ harder. In VS you can break
on individual LINQ expression stages, read the unpacked values, read the let
values, etc. If I’m composing with Bind functions then I can always break
either inside the lambda or the static function being used for the bind
operation.

~~~
seanmcdirmid
You can eval the arguments, but you still have to pop a stack frame on if you
can at all to get at the underlying control flow. You then have to integrate
all those frames by yourself in your head. Even the most ideological Scala
programmer wouldn’t replace all their if statements with options.

------
shadowfiend
This makes sense only when you have to have an explicit return point. In
languages that return the last expression, I find it much more palatable to
represent your conditions as top-level if expression.

A conversion of the base example would be:

    
    
        function () {
          if () {
            x
          } else {
            if () {
              y
            } else {
              z
            }
          }
        }
    

Granted it's also worth noting in such a language I rarely find myself using
`if` and `else`. More likely I'm doing flow control based on `map` and some
version of `getOrElse` (Scala's term for Option that allows you to return an
existing value or alternate to a nonexistent value). These abstractions tend
to compose better, and lead to more clarity as to what particular data item is
leading to the value being one thing or another; e.g.:

    
    
        function() {
          requestValue orElse sessionValue getOrElse defaultValue
        }
    

If requestValue is defined, return it; otherwise, if sessionValue is defined,
return it; otherwise, return defaultValue. While not all conditions are
immediately expressed this way, many of them can be simplified to data flow
decisions that _can_ be expressed this way, and the exercise of reworking the
solution to use this strategy clarifies the surrounding program as well.

------
osteele
Two uses for tests are (1) detecting a corner case or other non-standard or
exceptional case, and (2) performing case analysis on a type that can have two
values.

(1) often raises an exception or returns an error, failure type, or zero type.
The clearest implementation is generally as a guard clause, for the reasons in
the neighboring comments and their links.

(2) might most explicitly be represented by changing the boolean to a two-
valued enum or (parameter-free) union type or algebraic type; or at least by
using a switch statement with cases for true and false. For example, an
`ascending` boolean variable could be replaced by a `sortOrder` variable,
whose type has values Ascending and Descending. These more _explicit_
alternatives are also more _verbose_ , though. Using a boolean to represent a
two-valued type is such a common and readable idiom that introducing an extra
type is often a readability and maintenance hit, even if it's more explicit.
Similar, using a case statement instead of an if statement might match the
underlying math or design, at the expense of verbosity. I generally go with an
if statement with two arms, instead of an early return, in to make clear that
the branches represent case analysis rather than one normal and one
exceptional path.

------
JulianMorrison
The whole "return in one place and it must be the last line" thing in our
coding standards here is just semantic noise - because you still have to set
the "gotta return" flag and repeatedly bypass the subsequent attempts to do
stuff. So it still returns up front, even if it doesn't "return" up front. It
just has to spend the remaining lines of the method tiptoeing around the fact.

------
d--b
Experienced programmer should not be giving these kinds of vague general
advice that really only apply to certain cases. Writing a sorting algorithm
and a node.js routing callback requires demands different styles. And if code
already exists, just follow the same principles as what's already there. It's
far more important to keep code formatting consistent than less indented...

I am not advocating for the opposite but having return statements in the
middle of the code does have its drawbacks. In many instances it makes the
code less easy to read. For instance when I write:

    
    
        If (x) {
          Return y;
        }
        Return z;
    

I don't see that return y and return z are on the same conceptual level cause
their indentation is different. The "else" is implicit, and so as a reader I
have to make the effort of thinking "oh that part has already exited, so I'm
in the case when 'not x'".

Anyways. All in all, just optimize your code for what makes sense and avoid
general advice on code formatting.

------
lamg
Certainly is hard to read nested code, but returning earlier makes it harder
because now our mind has to construct the execution path instead of reading
it. What I usually do is writing the body in a new procedure. Dijkstra
designed a language with no return statement with a very simple semantics made
for proving the program behaves exactly as its specification mandates. Niklaus
Wirth designed Oberon with no return statement. Currently I write Go code only
using return at the end of the procedure, my code is easy to follow and has
fewer errors than before
[https://en.wikipedia.org/wiki/Guarded_Command_Language](https://en.wikipedia.org/wiki/Guarded_Command_Language)
[https://www.inf.ethz.ch/personal/wirth/ProjectOberon/index.h...](https://www.inf.ethz.ch/personal/wirth/ProjectOberon/index.html)

~~~
cryptonector
Golang is not comparable, as you get to leave failures not handled explicitly
because its goto-fail scheme. I mean, that's a very nice feature of Go, but
not comparable.

The danger with early returns is failure to handle all cleanup. This is really
a failure of many programming languages. Golang gets this right. But even so,
I'd rather have early returns (or early goto-fails) than ever deeper if/else
ladders.

Another option that works very well is this:

    
    
        ret = thing1(...);
        if (ret == 0)
            ret = thing2(...);
        if (ret == 0)
            ret = thing3(...);
        if (ret)
            <handle failure>;
        return ret;

------
reidrac
Writing Go it is good practice dealing with the errors first and, when
possible, using if/return; and I agree. Less indentation is good IMHO, and
conditional nesting is usually best if avoided.

Working on someone's else code I've found this:

    
    
        if a, err := func(); err != nil {
            return err
        } else {
            // a exists here
            ...
        }
        // a is gone
    

I would have wrote it differently:

    
    
        a, err := func()
        if err != nil {
            return err
        }
        // work with a
    

I guess it depends on the amount of code to put in the "else" branch, but I
thought this was interesting because it looks like someone was avoiding to
write the error checking in a different line.

------
cryptonector
Another option that works very well is this:

    
    
            ret = thing1(...);
            if (ret == 0)
                ret = thing2(...);
            if (ret == 0)
                ret = thing3(...);
            if (ret)
                <handle failure>;
            <common cleanup>;
            return ret;
    

No ever-deepening if/else ladders, and no goto-fails.

Alternatively:

    
    
            if ((ret = thing1(...))
                goto out;
            if ((ret = thing2(...))
                goto out;
            if ((ret = thing3(...))
                goto out;
        out:
            if (ret)
                <handle failure>;
            <common cleanup>;
            return ret;
    

This has a goto-fail, but only one, so it's Go-like.

------
mercer
This is one of the reasons why I grew to really love function overloading,
especially in combination with pattern matching (I learned both first with
Clojure, perhaps, but have only become comfortable with them now with Elixir).
I can often avoid if/then statements altogether! It's also interesting how
error handling becomes much less of an issue.

I do a lot of javascript programming, and it's often quite frustrating to not
have these and other features available. While I by no means hate Javascript,
I have to admit I feel a bit silly about being defensive about it in the past.
I just didn't know what I was missing, or didn't see what the big deal was.

~~~
wutbrodo
> While I by no means hate Javascript, I have to admit I feel a bit silly
> about being defensive about it in the past. I just didn't know what I was
> missing, or didn't see what the big deal was.

I'm glad to hear a perspective like this. I'm fortunate enough to not come
across too many people like this, but I'm always utterly baffled by people who
get defensive about the smallest complaints about Javascript (of which there
are many valid ones that have nothing to do with irrational hatred).

~~~
mercer
Ha, well, not to get too defensive about my past defensiveness, this was at a
time where the irrational hatred was constant and unrelenting. Some time after
the 'Javascript: The Good Parts' book, but before all the ES6 goodies.

Just a few weeks ago I had some badstalgia when I mentioned I did front-end
development to someone. He responded with a rant about how real programmers
don't use javascript, and how it was written in ten days, and so on.

But yes, I'm happy to have largely stepped away from the language wars. I
occasionally join the #javascript channel on IRC and it's truly astounding how
much of the conversation is just people attack or defending javascript
passionately, and for very silly reasons.

~~~
wutbrodo
> Ha, well, not to get too defensive about my past defensiveness, this was at
> a time where the irrational hatred was constant and unrelenting. Some time
> after the 'Javascript: The Good Parts' book, but before all the ES6 goodies.

Yea, it's a crappy feedback loop, where unfairness on either side leads to
people being even more strident in their position[1].

> He responded with a rant about how real programmers don't use javascript,
> and how it was written in ten days, and so on.

Ugh yea, that's exactly the kind of person I'm talking about. I may be
dismissive about JS as a language, but the idea that "real" devs don't use it
is ridiculous.

[1] though I admit I can't really relate to people taking their choice of
programming language personally. To quote pg, "keep your identity small"

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

------
coldsauce
I guess this makes sense when programming in an imperative paradigm. Scala,
for example, has a completely opposite convention. Most of the scenarios he
describes are usually things that would never occur in a functional paradigm.

~~~
dudul
Agreed. I've been writing functional code for almost 10 years now and I fail
to see any situation in which I could apply this.

Functions are rarely longer than 10 lines, there is no explicit return
statement, just a short sequence of data processing and that's it.

------
ralusek
Error handling early on with a throw or return is so much better because it
just allows you to stop thinking the very moment you need to stop caring about
the remainder of the function.

It's very much like any logic or debate, where you simply want to prevent
getting into any of the details that occur further down the logical chain if
you can already dismiss what is to come by detecting the exit condition early.
There have been countless occasions where I've heard people having
conversations for hours that should failed and exited early due to an exit
condition at the very beginning of the conversation.

------
partycoder
The one liner is the wrong conclusion.

Why?

1) When using a debugger, it takes more effort to place a breakpoint on the
inner statement.

2) If you want to insert statements inside the if block you will be changing
version control history for statements other than what you are modifying.

About 2, you may argue that this will be the case anyways because braces are
not used... the solution to that is to always use braces.

Then there's another important aspect: handling error conditions is important
part of your logic. Do not sweep them under the rug. Proper error handling is
a big part of what makes applications robust, they need to be as readable as
everything else.

------
wereHamster
> Error handling is noise.

Use a language which has syntax to automatically handle the error handling
noise for you (dare I so do notation in Haskell). Go is egregious in that
regard, the constant repetition of `if err != nil {…}`.

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

~~~
Hendrikto
> I was made to have only one exit point for some projects

Did they give any rationale for this?

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

------
drakonka
I used to prefer keeping a single return point in my methods, but over time
this changed. Now I find myself returning as early as possible when there's an
error or some other failed condition from which the method can't/shouldn't
continue. For my hobby project I'm using Go and have found that it especially
promotes this practice - it encourages me to handle errors early and often; if
I nested all of the error-related conditions my methods would get very messy
very fast.

------
vortico
This also works well with a C++ `defer` helper.

    
    
        #define CONCAT_LITERAL(x, y) x ## y
        #define CONCAT(x, y) CONCAT_LITERAL(x, y)
    
        template <typename F>
            struct DeferWrapper {
                F f;
                DeferWrapper(F f) : f(f) {}
                ~DeferWrapper() { f(); }
            };
    
        template <typename F>
            DeferWrapper<F> deferWrapper(F f) {
                return DeferWrapper<F>(f);
            }
    
        #define defer(code) auto CONCAT(_defer_, __COUNTER__) = deferWrapper([&]() code)
    

Example of usage:

    
    
        {
            Foo *foo;
            if (initializeFoo()) {
                warn("Foo could not be initialized");
                return;
            }
            defer({
                destroyFoo(foo);
            });
    
            Bar *bar;
            if (initializeBar()) {
                warn("Bar could not be initialized");
                return;
            }
            defer({
                destroyBar(bar);
            });
        }
    

This way, all initialization and destruction happens in one block of the code
on the same level of indentation as everything else. Useful for socket code,
file handling, encoding/decoding states, etc.

~~~
tzahola
You're turning RAII upside down... Learn to use proper RAII and you won't need
this defer hack.

~~~
vortico
Tell that to the APIs I work with, like POSIX sockets/Winsock, etc. You're
suggesting that I wrap them all with RAII? That's ridiculous when you have
hundreds of C types which are all used just once or twice. Using defer makes
elegant/simple C-like code because it uses the C APIs directly and is nearly
the same number of lines of code as a C programmer would write without using
defer.

I fail to see how "learning proper RAII":

    
    
        struct FooWrapper {
            Foo *foo;
            FooWrapper(...) {
                foo = initializeFoo(...);
                if (!foo)
                    throw FooException(...);
            }
            ~FooWrapper() {
                destroyFoo(foo);
            }
        }
    
        try {
            FooWrapper fooWrapper(...);
        }
        catch (FooException &e) {
            ...
        }
    
    

is easier than

    
    
        Foo *foo;
        if (initializeFoo(foo, ...)) {
            ...
        }
        defer({
            destroyFoo(foo);
        })
    
    

If this isn't what you meant, could you demonstrate?

~~~
yorwba
You could use a std::unique_ptr with a custom deleter:
[http://en.cppreference.com/w/cpp/memory/unique_ptr](http://en.cppreference.com/w/cpp/memory/unique_ptr)

~~~
vortico
This is a reasonable suggestion since the code isn't _too_ bad.

    
    
        std::unique_ptr<Foo, std::function<void(Foo*)>> p(new Foo, [](Foo* p) {
            if (p) destroyFoo(p);
        });
        // initialize Foo and set to NULL if failed
    

It increases complexity a bit because you no longer have a simple pointer, and
you can't allocate on the stack anymore (my example should have declared `Foo
foo;` with `destroyFoo(&foo)`, sorry for typo.)

~~~
contravariant
If you didn't want a pointer you could have just used something like:

    
    
        template<typename TFoo, typename TDestr>
        struct FooWrapper {
            TFoo foo;
            const TDestr destroyFoo;
    
            template<typename TInit>
            FooWrapper(Tinit initializeFoo, TDestr destroyFoo) : destroyFoo(destroyFoo)
            {
                foo = initializeFoo();
                if (!foo)
                    throw FooException(...);
            }
    
            ~FooWrapper() { destroyFoo(&foo); }
        }
    

Although really that should have been part of the "Foo" class itself, but if
you need to deal with external code I suppose that might not be possible.

~~~
vortico
Oh yes, I forgot to make it clear that `Foo` is a C struct from a C API. There
are tens of thousands of such libraries.

An issue with your wrapper is that it is not generic enough and thus must be
written for each Foo-like object. That could likely be fixed with more
template arguments, but of course there are multiple ways to initialize C
objects like

\- `fooInitialize(Foo _foo); // expects that foo is pre-allocated`

\- `fooInitialize(Foo __foo); // allocates and sets the Foo pointer. Annoying,
but some libraries do this.`

\- `Foo _fooInitialize(...); // taking certain arguments`

A single `defer` wrapper allows you implement custom destruction behavior for
each instance, which is useful if you want to set flags or log errors in the
destruction process.

~~~
contravariant
Well you probably have your reasons for needing an absurdly general method of
handling things. That said I think it would be a lot cleaner to use locally
define a class that takes care of both initialisation _and_ destruction when
possible, rather than one that only takes care of the destruction. I wouldn't
mind too much if you used macros to simplify the boiler plate somewhat.

What worries me is that using 'defer' leaves absolutely no possibility for
reusing the code, other than by literally copy pasting it, which just rubs me
the wrong way. It goes completely against the DRY principle.

------
ryandrake
One drawback of returning early is it can complicate debugging. If I want to
stop on a breakpoint when a function exits, it's nice to be able to set a
single breakpoint on the final return statement. When you spew returns all
over the function, your life gets more complicated. Some debuggers let you put
the breakpoint on the final brace and will do the right thing, others make you
find and put a breakpoint on each return.

Another thing I avoid doing because it complicates debugging is putting a
final calculation on the same line as the return:

    
    
        return combine(foo.calculate(), bar.calculate());
    

Some debuggers make it difficult for me to examine the result of the combine()
function and/or the results of the .calculate() methods. Better to be
explicit:

    
    
        auto foo_result(foo.calculate());
        auto bar_result(bar.calculate());
        auto combine_result(combine(foo_result, bar_result);
        return combine_result;
    

There, much better, arguably more readable, and I can stop at whatever step I
need to.

I've had to debug far more code in my career than I've had to write, which has
encouraged practices that make debugging as straightforward as possible.

------
Pxtl
I find a compromise is best.

First, do the simple checks "Oh, this function returns an empty set if one of
its parameters is null? Do that".

Then go into the main method body. In the main method body, I want to see
elses on ifs. I'm okay with putting a return in each branch of the if/else,
but what I find bad is when the main body is

```

    
    
      if(foo)
      {
         doLotsOfStufff();
         return myResult;
      }
      return bar;

```

That pattern I can't stand. That's as confusing as switch-block fallthrough.

For those cases, I prefer to see an else. If you _must_ fall through to a
final return while you've had a zillion other returns in your branching logic,
then that deserves a comment at least explaining that this is the final
fallthrough return.

Like

```

    
    
      if(foo)
      {
         doLotsOfStufff();
         if(somePositiveCase)
         {
            return myResult;
         }
         if (someOtherNeutralCase)
         {
            doSomePreparatoryThing();
            if (heyMoreChecks)
            {
               return positiveResult();
            }
         }
      }
      // this comment here is super important where we 
      // explain that we couldn't do any positive thing 
      // so this is a failure state.
      
      return barFail;

```

~~~
guitarbill
So how does the final example even look with else's? I'm not sure what
argument you're trying to make with such an awful control flow in the first
place?

I also disagree with needing a comment, which is why I think not having an
else in the first example is great. Having a return statement at the end of a
function is obvious, and then all you have to do is scan upwards to find other
return tokens. Especially because the final return need not be an error state.

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

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

~~~
z3t4
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 !?

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

------
lowbloodsugar
How is it that articles like this not only get written, but get voted onto the
front page of HN? It's not that the content is bad but that the content has
been presented elsewhere, and numerous times, e.g. c2 wiki.

Is it because we're not really engineers? We're just hackers? I feel like I've
seen this discussion every five or ten years or so. Is that just my
perception, or do we have waves of interest and knowledge building and then
loose it all in some exodus from the industry every ten years that I'm not
aware of? One of the threads here ended up exploding into yet another coding
style "discussion", the kind I first remember back in 1995, and my attitude
hasn't changed since then (just pick one and move forward).

I can only imagine that as soon as B[1] was created, programmers started
arguing about whether or not the curly brace should go on a new line.

And that's the real problem here. We are still attempting massive engineering
works using the tools of 1969: text files. We have graphical tools with
automated assistants for finance, medicine, space flight, car design, but when
it comes to the software that we programmers use to create that other
software, it's 1969 text files with curly braces. No fucking wonder I keep
seeing the same old shit. We should be in outer space right now, but we're
still teaching the kids how to hold the sticks to make fucking fire.

[0]
[http://wiki.c2.com/?SingleFunctionExitPoint](http://wiki.c2.com/?SingleFunctionExitPoint)

[1]
[https://en.wikipedia.org/wiki/B_(programming_language)](https://en.wikipedia.org/wiki/B_\(programming_language\))

------
pmlnr
I worked in MISRA C for a while. You are not allowed to exit early there;
every run needs to be deterministic.

But when it's normal programming; YES. Definitely yes.

~~~
pwm
Deterministic in what context?

------
ztjio
Haven't looked into it for a while, but, it used to undermine the JIT compiler
to use multiple return statements in a single method in Java.

That said, I entirely disagree with the premise. I find it vastly easier to
reason about a function/method that does not return early. To me, this is on
par with writing switch statements that don't cover all the possible values: a
sin.

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

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

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

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

------
asveikau
If you are working in C (not C++ with RAII), return early is a huge headache
and maintenance hassle. Basically you want to free any loose buffers on any
exit path out of the function, and if you only have one place out of the
function, you only have one place to put it.

Or, as I think of it sometimes, if you have N buffers and M early exits, you
need O(NM) free statements. When the code changes over time with more exit
paths, you need to change that many places. So best to make M=1 for greatest
sanity.

A language that does this for you more automatically (either RAII, or GC) then
sure, early exit away. Although, one further point I do like about the more
explicit C style is you end up writing code that looks very similar in the
failure path and success path, which makes you prepared for when things
inevitably fail...

------
LoSboccacc
really not a fan of "return early" style, because of logging: I like to have
the ability of look at the business logic flow as/if needed and monitor its
states but multiple returns per method force having complex "ifs" logged one
after another, while in a single return pattern you can concatenate the
reasoning into a single log line.

Having a single log line is extremely useful in general as you don't have to
filter all the relevant information within other log lines and makes easier to
extract a single thread pathway within a multi-threaded application.

however I do like this snippet: > Try keep the “meat” of your method at the
lowest indentation level.

but I prefer doing that isolating atomic logics within their own method,
instead of having multi-step pathway with early returns

------
bad_user
Early returns only work well in untyped, statement oriented languages, like
JavaScript.

I'd rather change the language.

~~~
SideburnsOfDoom
Early returns work fine in Java and C# and Rust too, so I don't think that
it's anything to do with dynamic typing, rather it's about how removing the
burden of resource tracking from the programmer makes early return more
viable.

------
ljm
While I think the advice in this post is pretty sound (guard clauses are a
great alternative to bailing out of a function in various places, because you
can see the conditions up front and not as what is essentially an
afterthought), I do take issue with the language we tend to use when sharing
advice.

The advice we give is often prescriptive or absolute, in that you have to
avoid doing something, or you should never do something, or there is a _best_
practice. More often than not it strips out the nuance, as if the new position
has somehow managed to resolve all of the issues that cause the year--even
decade--long debates in the first place, and it is far more often the case
that the choice is more than preferential.

And what makes it bizarre is evidenced in the article itself: the original,
prescriptive position is neutered in a follow-up edit that claims to take the
position in balance because it won't make sense in every situation.

The top comment in this thread talks about the evolution of a programmer;
surely one of the greater evolutions of your career in the field is to
understand that all of this advice is purely dependent on context and an all-
or-nothing approach to rule making isn't going to cut it; in fact it will
likely make things worse when aspiring programmers take it as gospel and start
trying to shoehorn these best practices into whatever code they can, adding
all kinds of linters and such to enforce the rules in all cases and
prioritizing style over implementation in code reviews. Code _must_ fit in 80
characters in a line; it _must_ omit semi-colons if it's JS; it _must_ use 2
spaces for indentation; it _must_ be extracted into a method if it has more
than 5 lines...no matter what.

As an alternative, I instead propose that you think of _good_ or _better_
practices, also known as reasonable guidelines, and if you're going to build a
style guide around them at least try to provide _some_ alternatives should the
preferred solution not fit the problem the programmer is currently facing.
Treat it as an opportunity for mentoring and not just an instruction manual.

~~~
TheJoYo
Most of those rules suggested are too ridged can be accomplished by formatters
hooked into an editor's save function or version control commit. I don't want
my developers spending any more time on whitespace. Anything not solved by an
automated process should certainly be on a case by case basis. If half a file
has semi colon endings it's reasonable to request it be uniform for that file.

The benefit to automated formatting is the style configuration can be swapped.
if I HAVE to see altman brackets my editor can cook that format while my
version control can hook whatever format the project requires.

------
wheelie_boy
Reminds me of Tom Duff's Essay 'Reading code from top to bottom':
[http://www.iq0.com/notes/deep.nesting.html](http://www.iq0.com/notes/deep.nesting.html)

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

------
dcuthbertson
One nice thing about a single exit point is it's easier to add logging,
particularly for logging a return value. Only one call is needed, instead of
tracking down every exit point in the function.

------
makecheck
This breaks down immediately because no matter how much is “handled” at the
top, _any one_ of the neatly-packed doSomething() and doMore() calls at the
end _can still fail_ and need handling. You can’t conveniently omit the mess
that would be created for checking each of those.

“Errors in top if” is good advice but every “else” may need its own (indented)
top “if”.

Indentation isn’t that nice but it is a very loud indicator of code becoming
too complex, and a lack of indentation is a hint that an error might have gone
unchecked.

~~~
timoxley
> calls at the end can still fail and need handling… You can’t conveniently
> omit the mess that would be created for checking each of those.

For the purposes of this post, it is assumed that any errors produced in those
calls will automatically bubble up the stack to some caller or to the top
level. In my experience, most JS code is not written with fine-grained error
checking around every expression, if there's any explicit error handling at
all. The example code isn't intentionally omitting any mess, rather it appears
to me to be fairly typical.

The "handing" at the top is mainly for checking function preconditions. e.g.
"does it even make sense to proceed?". Early returns help to decouple
precondition checking from the important logic of the function, which IMO
makes these types of checks easier to write and maintain and thus more likely
to exist.

------
SideburnsOfDoom
A longer essay on similar issues:

[http://www.anthonysteele.co.uk/TheSingleReturnLaw](http://www.anthonysteele.co.uk/TheSingleReturnLaw)

~~~
billysielu
Oh gosh yes. If I had a dollar for every time someone forgot a break statement
in a "Found item in loop" scenario...

~~~
SideburnsOfDoom
Well, LINQ ended that issue for me ;)

------
lozenge
Avoid rules, use your judgment.

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

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

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

------
billysielu
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...](https://softwareengineering.stackexchange.com/questions/350472/)

------
spdionis
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?

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

~~~
spdionis
Right, I should've mentioned except arcane things (for us pleb web devs)
happening in low-level languages.

------
rockostrich
As someone who has been working on a Scala codebase for the past year and who
now forgets to use the `return` keyword in other languages, it still pains me
when I see stuff like that first example in coding interviews. It doesn't help
that the solution for my go-to problem has a bunch of places where you can
return early...

------
ehosca
[https://stackoverflow.com/questions/7264145/if-less-
programm...](https://stackoverflow.com/questions/7264145/if-less-programming-
basically-without-
conditionals?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa)

------
davidmoffatt
Nothing is new. When I was a NOOP programmer I knew the crusty old build
engineer called Diane. She did not like my shell script and rewrote it with a
terse comment "Exit early and often". The same idea as the article and this
was in the late 1990s. It was a lot shorter and easier to understand.

------
CodeCleanly
Usually we call this fail fast in the java world. There's a similar item in
Java by Comparison on that: [https://pragprog.com/book/javacomp/java-by-
comparison#conten...](https://pragprog.com/book/javacomp/java-by-
comparison#contents)

------
barell
I agree with everything apart having if and return statement in the same line.
It makes code really difficult to read.

In addition, I also like is to avoid if/else when returning bool values:

    
    
      if (a > b) {
         return true;
      } else {
         return false;
      }
    

Could be simply written as

    
    
      return (a > b);

------
Grue3
In Common Lisp the syntax for early return is so ugly (```(return-from
function-name result)```) that I use it very rarely. In general, it seems that
in the languages that use implicit return (Lisps, Haskell, Rust?) having an
early return is discouraged by design.

~~~
kazinator
In Lisp other structures come into play that effectively give you early return
in various situations, like for example _cond_.

    
    
      (cond
        (early-return-condition early-value)
        (second-early-condition early-value2)
        ...
        (t final-else-value))
    

A _cond_ as the last (or only) form of a body is basically an N-way switch
toward multiple exit points, the ones appearing first being earlier.

In TXR Lisp I made _block_ have dynamic scope, not only dynamic binding. You
can do this:

    
    
       (defun helper ()
         (return-from master 42))
    
       (defun master ()
         (helper))
    

I.e. early return from nested helper functions, without a lot of added
ceremony.

Speaking of this dialect, here is a function from its C internals, exhibiting
a preference for an early _return_ over _else_ :

    
    
      val flatten(val list)
      {
        if (list == nil)
          return nil;
    
        if (atom(list))
          return cons(list, nil);
                  
        return mappend(func_n1(flatten), list);                              
      }                                                                                                   
    

If this were in the Lisp dialect instead of C, the same author would write it
like this:

    
    
      (defun flatten (list)
        (cond
          ((null list) nil)
          ((atom list) (list list))
          (t [mappend flatten list])))
    

and certainly not like this:

    
    
      (defun flatten (list)
        (when (null list)
          (return-from flatten nil))
        (when (atom list)
          (return-from flatten (list list))
        [mappend flatten list])
    

Even if the implicit block around a function body were the anonymous block, so
that _(return (list list))_ could be used, this would still be eschewed as
unidiomatic Lisp.

------
mark-r
Small bit of trivia: the 8080/Z-80 microprocessors had conditional return
instructions that were only a single byte long. I utilized these instructions
heavily when trying to cram my code into a single EPROM, because the jump
instructions were 2 or 3 bytes.

------
menacingly
At some point in the last few years, I shifted into doing this, but I've just
recently been trying to decide if it's a good idea. Every once in a while I
lean into some cute little expedience that later annoys me when I'm digging in
old code.

------
michaeljbishop
How to do this in a for-loop:

    
    
      for (int 1 = 0; i < length; i++) {
        if (checkError(i))
          continue;
      
        ... // happy path
      }
    

You can also use a return statement inside the for-loop if you want to exit
the entire function.

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

------
S_A_P
I believe this strategy is called "get to no asap". If you need to validate
the data coming into a method, do it first and throw the error/return as soon
as you can so the rest of the method is doing the work intended.

------
wiradikusuma
I guess I'm the minority here, I prefer "if else" with the happy path inside
the if.

Returning early gives me mental overhead of keeping the "elses" on my head,
and doing it in Scala makes the code look more verbose.

~~~
jayvanguard
I don't think you're in the minority. Whenever possible the happy path should
be inside the if. The article seems to imply otherwise but on re-read I think
he is just building the case for putting the simple, quick pre-condition type
handling up front which generally removes the need for the if/then (which I
agree with).

For complex methods with branching logic you want prominently displayed, you'd
end up with:

def myfunction(args) {

    
    
      // check preconditions, return early
    
      if (x) {
        // happy path
      } else {
        // less happy path
      }

}

But if the else just contains a bunch of error handling you always have the
option of wrapping it in a handler method and putting it near the top in a on-
liner.

~~~
redwolf2
Problem is, you may build the if pyramid of death, if you branch too deep.

So break early.

[https://www.youtube.com/watch?v=GIl_BNIJcFg](https://www.youtube.com/watch?v=GIl_BNIJcFg)

------
mikmoila
In case guard-clause is used for input-validation, how about extracting the
arguments to a class of which constructor prevents instantiation of a class
with invalid input, and pass that to the function?

------
xumingmingv
I totally agree with this one. Especially:

1\. It reduce the indention level, makes the code cleaner. 2\. Keep the error
handling cases at the top of the method, the rest of the method can be written
with more ease.

------
pasta
I would also argue that you should throw an exception early when the function
cannot return what it promised to return.

When you break your application fast you generate less bugs.

------
hnaccy
Removing the braces and returning the unneeded non-result of handleError to
save a couple LOC is penny wise pound foolish.

Is this a common JS idiom?

~~~
timoxley
The `err, result` signature of the function indicates that it will be executed
asynchronously, and thus the return value can't be captured by anything
anyway.

------
ams6110
> We also generally don’t care about return values in JS

I don't find that to be the case, though JS isn't my main language.

~~~
timoxley
Post is referring to async programming in node's callback style (non-promise-
based functions executed asynchronously) where return values cannot be
captured even if you want to.

------
xhan0o
I can recall this, this was taught to me in Computer Architecture class, while
designing the processor.

------
LarryMade2
Reminds me of using GOTO back in my early days... certainly could lead to more
confusing code.

------
z3t4
I use both if else and return, so in case I forget any the code still works as
intended.

------
skrebbel
This is why I'm so sad that Elixir doesn't have a return statement.

~~~
OvermindDL1
But Elixir 'does' have early returns, they are known as `Guards` as a language
construct.

~~~
skrebbel
Yeah but that's only half the argument, right? Early returns make control flow
a lot easier to read in general, not only when checking preconditions.

------
ngvrnd
There are no silver bullets. There is no substitute for clear thinking.

------
soruly
I just noticed that the blog's title is "Probably Wrong"

------
CodeWriter23
And keep your functions / methods down to ~60-70 LOC each.

------
tomerbd
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 :)

------
gwbas1c
I'm down-voting all arguing about brace style. Whenever I see discussions
about brace style, I just roll my eyes.

The sign of someone who doesn't value their time is someone who argues about
brace style.

------
davvolun
Seriously.

TLDR: use guard clauses (when appropriate for readability and cognitive load)

------
ForgorPsxrd123x
This article was noise

------
ebbv
Good advice except for the last example which is brittle and unnecessary. If
handleError() is ever changed this method could start returning values
unexpectedly. Totally unnecessary and not a worthwhile "optimization" to save
lines of code.

~~~
timoxley
The `err, results` params form the signature of a continuation-passing style
"errback":

    
    
        function(err, results) {
          if (err) // …
        }
    

It is assumed that this function will be executed asynchronously, and thus
it's not possible for anything to consume the return value anyway.

If you want to control the return value, simply return on a new line or use
the void operator:

    
    
       return void handleError(err)

------
augustk
This goes against best practice as proposed by Steve McConnel in his "Code
Complete."

------
amelius
Premature return considered harmful.

~~~
lamg
Certainly is hard to read nested code, but returning earlier makes it harder
because now our mind has to construct the execution path instead of reading
it. What I usually do is writing the body in a new procedure. Dijkstra
designed a language with no return statement with a very simple semantics made
for proving the program behaves exactly as its specification mandates. Niklaus
Wirth designed Oberon with no return statement. Currently I write Go code only
using return at the end of the procedure, my code is easy to follow and I have
fewer errors than before
[https://en.wikipedia.org/wiki/Guarded_Command_Language](https://en.wikipedia.org/wiki/Guarded_Command_Language)
[https://www.inf.ethz.ch/personal/wirth/ProjectOberon/index.h...](https://www.inf.ethz.ch/personal/wirth/ProjectOberon/index.html)

