Today in a code review we had a statement like this:
Statement A
if (typeof val === 'string') {
return 'A'
} else if (val === nu...
For further actions, you may consider blocking this person and/or reporting abuse
I prefer the second syntax because it acts like a "guard" against further execution, so to speak. The first syntax just encourages more nesting and levels of indentations. To me, it is more readable to have a "guard" condition than to have
if-else
blocks.I hadn't encountered the term "guard" for this but I like it a lot ๐
I can't take the credit for it. ๐
Article No Longer Available
I call it the Short-circuit or (Fast-fail)[en.m.wikipedia.org/wiki/Fail-fast]. It will help you just worry about less things if it's not followed by
else
I have the same train of thought with it! A senior developer at a previous job introduced it to me
The first time I heard the term "guard" was in haskell. Is it any related?
I'm quite surprised actually. Given the imperative nature of
if
statements, I wouldn't expect Haskell (of all languages) to be the origin of that term.Haskell (among other functional languages, I guess) has guards for pattern matching
I always go with Statement B. This is known as "early returns" and helps with confusing success/error cases. Take for example:
It's nice to know for sure that the end of the function where we're saving the post to the database will not error out due to bad data, since we already verified those cases earlier.
I like this style more - it looks clearer to me, and from the point of working through the logic, I like that the returns just end the function.
If a block just set a value and returned it later, there's a chance it could be reassigned in another buggy block, so I think there's less risk in this style too.
This looks great to me. I'd be happy even without the comments as it feels pretty self-documenting.
I added the comments just for the sake of the example, but they should definitely be removed for a real app.
I prefer the first approach, it's ensures all cases are covered, and is more extensible IMO.
I also prefer a single
return
per function, excluding sometimes null / wrong validations, as this code tends to be easier to read and debug. In your example, I would use the first method, but assign the return value to a variable, and return it after the statements.This made me thing of this post on hackernews: Please do not attempt to simplify this code
Can you elaborate on the "cases are covered" point? In my mind both snippets covered all cases, but I'm open to hearing otherwise.
In this case, both examples are the exact same of course.
This is somewhat related to a single return statement in a function, but the syntax of an
if/else if/else
is more explicit than the various conditions are related to each other.With few
if
statements each statement is independent from the others (unless you return in the previous cases, like in your example), therefore more than one of them can be triggered.Overall, while there aren't many arguments for any of these approach in your specific scenario, I feel like it's a better practice to have a single style of conditions for your whole codebase. As the first approach is more flexible, I strongly prefer using it everywhere!
Consistency is definitely most important. I just did a search through an API that I built recently, and I only have 3
else
statements which I can probably refactor out. So, I think it's definitely possible to go all-in on early returns.TIL about "space shuttle style", very interesting.
I'm not sure if the Kubernetes controller source is meant to be used as an example though. Otherwise there would be no comment to warn people from simplifying the code.
In other languages such as Scala and Rust there is a "match" statement and a compiler that looks at the types can tell you if all cases are matched. In Scala you can compose/chain together sets of partial methods that match a set of related cases and the compiler will tell you if the combination will do an exhaustive match. So what the Kubernetes example you post is pointing out is that the devs are having to work around a missing feature in the language they are using. Once you have used a language that has an exhaustive match you never want to go back. This is because it is very much easier to write code that you know won't just fall through to some wrong default case when someone adds a new type of input.
I prefer B, if only because it is a one to one mapping of cases to output - when reading the function it says:
This is a much more human way of thinking about the possibilities. I don't tend to think in terms of
if/else
conditions in the real world and I don't feel like much business logic works that way either. Option B is also easier to extend - if later you need to add an "if val is a number, returnval.toString
" or the like, then that is easy and you can add it wherever it makes sense in your code.Slightly unrelated, but I also really perfer this style to switch statements. They have a place, but most times they should probably be multiple if statements.
@wuz Well said, and I totally agree on the switch statements piece. I'm not sure I've ever been satisfied with the look/readability of a switch statement in JS!
I don't prefer one over the other, but what I do prefer is a single
return
statement, so you can make more assumptions about the code (eg: should get to the end of the function if there's no error)IMO, the single return comes from
C
coding guidelines where it may help developers easier to trace the complex code and prevent them from forgetting release any resources which need finalizing (like freeing heap). For this point, C++ projects usually don't adopt this guideline since it has its own stack-unwinding thing.For Javascript, I prefer the statement B for more readable code. Save everyone's effort.
I find it more confusing to return a variable in a return unless it is really basic (like changing the message of a JSON response depending the data given).
Early returns make it sure you don't even up with mutating the response somewhere later in the code. It also make more readable (imo) because not everything is nested.
A lot of people would recommend using const by default. A variable that can be many things is cognitive overhead and results in bugs down the line.
I prefer the "return early" syntax. It puts a line in the code where if you pass it, you know some assertion holds. The code above doesn't really illustrate that, but for example:
As opposed to:
In the second example, imagine some ugly nesting. For each block of code you need to think "am I in a level where I've tested flag1?"
@farble1670 so do you prefer a hybrid of first and second? Something like:
Another option (for those who favour expressions over statements) can be built out of JavaScript's ternary operator:
Quite a popular pattern.
Ternary operators are really nice for simple and small statements, but I definitely wouldn't recommend them for anything complex. Makes things to hard to read for me. The same could be said for other developers.
That's not easy to read at all in my opinion...
If we know the branches will have
return
, then B, plainif
, aka early return is better.We don't need to keep the context of whose
else
we are in anymore most of the time.Even refactoring it is easier, both in terms of just editing the code and the resulting git diff.
However, if we don't have
return
......then there are two options for me.
1. Extract into function
2. Use a conditional expression
It just depends on the complexity, relevance, and size
What's particularly good about both?
They end up only assigning
val
once, so after some further refactoring we may end up with the superiorconst
binding!F.A.Q:
Is early return at odds with functional programming?
Should we strive to use conditional expressions at every size?
No. Used correctly, return is just guards in a match expression. You could see it as advanced pattern matching that is missing from JS syntax.
(Early throw, on the other hand, is usually a hack we have to use for lack of static types.)
I prefer Statement A since it is more familiar, more explicit, and groups the possible outcomes. Almost every programmer has seen and used option A. It doesn't require changing the structure to make an update, if something needs to be added before the return statements, it is easy and predictable.
The second statement seems like brevity for the sake of brevity to me. It is shorter, but I'd argue that it is not concise because it does not present the information clearly (at least to me). On Statement B, the formatting would trigger a red flag for me. I'd likely have to take a second or third look just to make sure I know what the intention of the code was. I don't like single line if-statements because all it takes is accidental reformatting without adding braces to cause issues that are easy to miss. Some will argue that their editor does this all for them so maintainability is not an issue, but when working on a team I don't think that is a safe assumption. I'm sure someone could also argue efficiency gains of option B (smaller JS file), but that seems like overkill for minimal gain.
I think the second example is muddying the water by combining the pattern (failing early) with a style choice (single-line, braceless
if
s).I prefer Statement B, or would if it was written like this:
I like this because it reduces nesting and acts as a barrier to people over-complicating a function. I mean that if the function ends up having to re-use a negated form of one of the earlier conditions or have a separate set of nested
if
s after the guard conditions, then it's a big smelly smell and should be moved to its own function instead.Also, if you think about it in terms of human experience, it's how we work. If you're tending a patch of garden, you don't think about what category of flower you're looking at and cross-reference it in a book if it's obviously a weed.
The second one. Every time. For the early returns reason mentioned earlier. Since you're doing a return, the
else
is an anti-pattern.Also this thread terrifies me for all the code that exists out there and solidifies everything about xkcd.com/2030/
Totally! "else" at the end of an if/else chain is a trap awaiting someone changing the input then making a mistake in the if/else such that the new input falls into the old else that does the wrong thing. Scala has "match" and the compiler gives an error if you don't match everything that can come in. Putting in a wildcard match at the end, which is equivalent of a last 'else' in JS, reads as "okay I don't recognise this input so I am going to ignore it". That screams anti-pattern as being both inefficient and inexact.
Which ever route is the easiest to read(usually it's the second one) is the one I pick. Less code is more.
I've worked with both a lot, and I find the second one is often better for my cognitive load. If you need
if/else/elseif
perhaps to many things are going on at once.It depends on what kind of logic is needed, and which one reduces the amount of code and improves the readability.
I wouldn't do sample B as written because of the lack of braces for the if, which I'd always add (as well as spreading it out over more lines).
With that change, though, it's something that I do wonder about myself from time to time. With the exact sample given (where the else is just another
return
statement), I would usually go with sample A because the if-else-chain (which has a known mental model for me) nicely describes what happens. However, when the "else" portion is much larger (i.e. it's the "real implementation" after the corner cases have been taken care of) I'd probably go with something like sample B because it limits the levels of indentation that don't add information.Ultimately, though, I'm not entirely consistent in this.
First one. Brackets are consistent I feel and easier to assess. I've heard the argument about one return per function although then you're potentially creating a local variable to manage state. What's truly important is that your developers can effectively read it, after all the computer obviously doesn't care.
Wouldn't you put the null and undefined check first to have a quick return?
Of course, you could go back to the more explicit null and undefined check if you think you might get other falsy values that don't fall into that condition like
0
, empty string,false
, andNaN
.But to answer the question, I prefer just 'if' whenever possible.
As long as every branch returns then the 2nd version is fine with me.
(Although I'd still use brackets instead of guard clauses because of personal preference regarding readability)
It becomes a problem when the
if
does not return and gets more complex than a simple one liner.I.e.
would obviously be okay, but
would not be okay, because it makes it really hard to reason about the output of the function and the logical flow of your data can't be parsed at a glance.
I use both in two different situations. I use the first form at the top of a function when I'm doing assertions about parameters. Think of it as poor man's type checking or contracts in languages that don't have them, e.g.,
I use the second form in the primary body of code unless I am forced to do otherwise. The lazy reason is that I write in both imperative and functional languages, and in functional languages all ifs must have an else statement, so by always having an else, I have one less thing to keep track of between languages.
The more philosophical reason is that using the else block makes it very clear what the branch is in the execution path. The precondition and postcondition for both branches should be the same and the effects required to bridge that gap are all confined to those blocks. I have come to value this kind of locality more and more over the years.
If you find yourself doing this often, check this out: pypi.org/project/icontract/
I reckon that it all depends on what is your target.
Return statements, of course, you can easily use approach 2 or ternary operation. However, if you do any operations on a particular block, rather than return,
if-else if -else construction guarantees that if an expression is evaluated to true, other blocks would not be executed
, hence -> save your precious runtime. Thus, it would be only visible on a high-demanding calculation blocks.definitely the just "if" boat here as much as possible, although I avoid single line and practice the bracket method.
@dechamp something like:
?
yup! that is the good stuff right there!
And based off your example I would do this...
check for no value first, save the code from type checking if it's not set.
What if val is
0
orfalse
? ๐You have a good point, but are you expecting your system to act like that? It's very rare you need to actually be checking for that, but if you do suspect it then definitely go for the finite checking.
But then again, I love using the "joi" package for my type checking.
Normally no, but in this case yes. It's a bare-bones project and the code is constructing a DB query from a set of known inputs, so
null
andundefined
get treated asnull
, strings get quotes around them, and everything else is passed through.This is a classic versus modern debate.
Classics are classics because they stood the test of time, in which case statement A is preferred.
If any calculation were needed after the if and before the return, you would need Statement A in any ways, so isn't it more consistent?
I'm totally over trying to get code into super compact pieces after seeing a 140 line program shoved into a one liner in a coding competition :D Took me 140 hours to figure out what it does!
Hello, I prefer the second one, because in the past when I was reviewing my peer's code I was faced with the first style a lot and it put toll in my mind to process the meaning.
But I used to like the first one because it puts my mind at ease when writing it, knowing the else part is covered explicitly. But some time after when I need to read it again, to be honest, its not a nice experience.
It depends on the case, but I would probably pick a mixed case like this:
if (typeof val === 'string') {
return 'A';
}
if (!val) {
return 'B';
}
return val;
The main reason is because blocks are in my opinion better to see and less confusing. I also try to avoid the tenary operator, but it really depends on the case. Sometimes it's just perfect. There is no need to write an else in this case. The else if wouldn't improve any performance, and for me it's better to have no else case at all.
In favor of not having a
if/else
block if I can avoid it.or to be a little more descriptive
I'd definitely go for early returns.
less indentation, the logical steps are easier to follow, less code to read when you navigate through breakpoints while debugging.
also easier to further refactor if the single if statements get too long or complicated -> just move them to another function, which by the way would be unit-testable too.
There isn't much value in the
else
since the first conditional results in a return, however having it aids in readability in this case by grouping these two similar conditions. but having a final return outside of a condition is a best practice to ensure you don't mistakenly return undefined. The trailingelse
should be removed if it is just a return statement.Though in many cases, a map works better than a bunch of
if
/else
's.Using the mapped approach eliminates any need for switch statements.
Also, don't abbreviate your code, there is no benefit gained from shaving off a few characters. Let the uglifier worry about that. Your goal should always be readability above all else until something is noticeably slow.
For easier readable, I prefer Statement B.
You know? This remind me the
break
statement comes after areturn
inswitch...case
. For such the case, I supportbreak
should come after finish every case branch. Just for safe ๐At the beginning of my career I used to use the if-else syntax. Now I see just if syntax more readable
I talked about that in one of my articles, using Python instead.
What I found out is that there's not just one good answer for these types of questions.
Feel free to read the comments there to, I found all of them pretty interesting, and they can apply to JavaScript to :)
If else of course!
Statement B in general, because of indentation and context, even though you need to be careful about conditional cases. It's generally easier to follow through. IT REQUIRES MUCH LESS COGNITIVE LOAD because you don't need to think about all the cases that might have happened before or might happen afterwards if the specific condition is true or false. It's mostly used when you want to quickly check for a very bad usage of a function. Parameters that might break your logic. In that case you'd have 1-2-3 if clauses that check for bad params and immediately return/throw error. Alternative to that is to let the bad param checking leak to ALL of you NESTED conditional cases and make your code very hard to read. In that case you need to check for valid params inside of nested code and it's quite possible that similar code will occur a couple of times in the same function, which is crazy.
If you at any point end up thinking that it's not enough, that you need to take care of a bunch of complex cases, your function needs to be broken down to a smaller and composable functions that each do 1 thing and then revise all of the surrounding logic.
I vote for a mixed style: return early if there is an obvious default case (a guard) then use an if/else for some set of related cases. My reasoning is below.
I first ran into this waaaay too long ago in a code review with someone who said "a method should have one exit point" whereas i was liking the "exit early" or "guard" approach. I argued "but if you use early returns you don't have the cognitive overhead of needing to reading the whole method". Yet to them "single return" was The One True Way so we agreed to differ. My guideline for what to do when two devs disagree on a purely stylist matter is that the code remains as-is. I actually cannot remember whether it was my code or their code that was under review.
Unfortunately early returns is often a rotten code smell of "couldn't be bothered to refactor to add a new feature". In that anti-pattern you have a God Object / God Function and to add new features you add a fresh block at the top of the enormous "doAnything" method to detect your new scenario, call your new feature logic in another file, then return early. Then "ta da!" you never have to refactor the God code or change its unit tests (which are incomplete) nor manually test anything other than your new feature case. This leads to an immediate Broken Glass model where the next dev copies that approach. Pretty soon you have a thousand line method that keeps on growing with every new feature added. Regrettably large OO code bases that have seen any team turnover tend to default to this sort of thing remarkably fast.
If you tempted to return early in more than a single obvious default case then perhaps you should break up the class or method into different units that can be composed where each unit has an if/else set for some closely related cases. You then end up with smaller functions or classes that handle a tight set of cases. Then you can take a mixed approach. Try to only return early if there is a really common default case. Why? Because during debugging you don't want someone to have to read the whole code if there is a very common case. The more early returns you put in the more the code is asking to be broken up. The more complex the if/else starts the more you should break it up into different units that handle a more narrow set of cases.
I do like the if/else case. Where sometimes when you are actually reading the codes it gives a better insight on the app and just like piece of orchestration or a music it makes sense (it rythms). It does sound and it is a good coding practice.
I see a ton of comments on this thread and i read through about half.
I don't see anyone making a case for method A.
I prefer method A because conditionals rarely do just one thing inside them like these comments would have you think.
eg.
if(something){
//one line here
}else if(anotherthing){
//one line here
}
If this were always the case, then having just if statements after each other is simple and easy to read and maintain.
More often than not it's "//50 lines here" in between the conditions, and if you're not using squigglies (brackets) then your code quickly becomes unreadable. You won't know what conditional you're inside of even with indentation.
And yea, someone's gonna go "You should never have 50 lines inside a conditional!"
Ok, so you'd have a conditional that calls a method that calls another 5 extracted methods in other files right? because THAT solves the problem, when it really just makes your code more convoluted and harder to follow.
Also, does it really hurt so much to just write "else" in front of an if statement? So you always KNOW that this is A. not the only condition, and B. not the last condition either.
In the long term it's never good to minimize your code like that. You always end up with someone going in to add something to the conditional and because they added a second line it now breaks because of a lack of squigglies. The code executes the second line always because it's not "inside" the if statement.
It's just better practice to always wrap things so they're maintainable in the long term.
My 2 cents.
I think the original way is clear and more readable.
I always try to save as much space as I can, so I guess it's the choice B for me.
If present then Stick to codebase convention
Else prefer readbility of the method
๐ฌ
Well which one is more "readable"? ๐ค๐
As our phylosophy teacher used to say: we are debating about angels gender... Anyway I think I stick more for the first one. I work in Java and there is a plenty of other situation to use one-liners.
I prefer just if with early returns as it reduces nesting conditions.
I prefer the second approach, but with curly braces.
Generally speaking, I return ASAP like you did to avoid nested code and big
if/else
statements.The second approach is nice if and only if all of your branches result in a return; however, it's not great if you're trying to do something else with the result of evaluating the conditions.
Hey checkout my new post, inspired by your post. dev.to/dechamp/clean-up-your-code-...
I'd use the second for simple conditions and as 'guards', mentioned below. The more verbose if/else if/else would be used for more verbose conditions.
Statement B
don't forget the switch! :) quite often it's a lot cleaner solution than multiple if/else statements
My vote goes for option B.
Since I learned the Swift guards I prefer that style.
I'd never code A, always B, no doubt about it. Since the 'return' statement ends execution there and then, IMO the 'else' clauses don't make sense at all.
Easy one I would say.
In Golang if the if statement is returning, the linter will complain about the else statement. In short,
I find this version even more difficult to read...
interesting that modern languages are putting in generators and "yield" that have multiple exits that save the stack and then reenter into a new position with the saved stack.
Why is
val
not already typed? It could be anything?!? I'd rather look into avoiding either of these situations and make sure the type ofval
is already known.I'd keep the curly braces but remove the last "else" because it's useless.
I either user Ternary operator or the statement A type of syntax. But after reading this thread definitely will stick to statement B or ternary operator depending on the situation.
I usually go for the if + else if and no else at the end
I prefer the second, It's clean and readable :D
Is the if and elseif statement about same scenario?
Yes.
Is the if and elseif statements not about same scenario OR is taking up more than 10 lines?
Switch case.
Great points!
Ternary operator
I tend to use a small map, or a ternary in these situations.
Ifโs without brackets breaks the bracket consistency for me, and โif elseโ can be hard to read.