You may have already seen Nick Chapsas's video on this same topic but if not you should check it out. th-cam.com/video/_ougvb8mT7k/w-d-xo.html His video was the inspiration for me creating this video as I loved what he had to say and wanted to add my own thoughts to the conversation.
It's funny. TH-cam recommended me one of your CSS battle videos, which led to being suggested more of your channel, which led to being recommended Nick's channel. Saw his vid on the else keyword too, feels like such a small world haha
Take it from someone with close to 15 years of experience in multiple programming languages and styles: Always take advice like this with a grain of salt. Yes, "else" hurts readability *when used ineffectively* (mostly with long statement bocks, which obviously is bad), and sometimes early exit is preferable, or even more modern idioms like switch expressions if your language supports it (JS sadly doesn't). BUT in many cases it's much clearer and simpler to just use traditional if-else. If the statement blocks are both just a single line each, with nothing else going on in the function other than a single pair of if-else statements, then there's absolutely nothing wrong with it. If, on the other hand, the else keyword is hidden after pages of scrolling down, you have a complete mess, and it's high time to refactor your function and at the very least split it up into smaller functions - and early return won't save you in that case, it may in fact make things even worse. Obviously, be sure to have decent test coverage before you refactor. Just use the idiom that conveys the semantics you intend to convey in whatever idiom feels most natural for the job at hand. More often than not, if-else does just that. It's always a matter of context. The important part is to take a moment or two to think about it rather than mindlessly applying whatever idiom someone on the internet told you to be the one and only true path. And if in doubt, ask a collegue for a review. If the statement were phrased like "Don't use else SO MUCH", now that would be something I could get behind.
@@minerscale By the way, I *do* hope you know the difference between expressions and statements, at least if you call yourself a professional programmer
@@cod3r1337 I thankfully do not call myself a professional programmer. I'm just a hobbyist doing a music degree. As for the difference between expressions and statements, having just looked it up I think I have a decent understanding of the underlying concept that expressions evaluate to a value whist statements do side effects, and that expressions are inherently safer because the flow of data is more clear, but I don't think I ever put proper words to it, as I lack a comp sci degree or anything of the sort. Is it incorrect to call a switch a statement?
@@minerscale First off, if your are a hobbyist it's fine if you don't know all the theoretical basics. I happen to be a hobby musician and don't know much about musical theory either. Just try not to wave your lack of knowledge around like a badge of honour. You are just going to piss people off for no good reason. Your understanding of expressions is halfway correct, but the actual definition is more limited. Expressions can have side effects just like statements, the difference is just that they evaluate to a value. Most mainstream languages have no way of guaranteeing an expression to be "pure" (side-effect free); some FP languages do, but those are hardly mainstream (sadly, one might argue). Still, it's generally considered good practice even when coding in a non-FP style to avoid side effects in expressions, or at least make them as explicit as possible. As for your question: It's absolutely correct to call switch a statement, but in most modern languages it can also be an expression. This allows for some constructs that are much more concise than the traditional statement style. C# has had this feature for quite some time now, Java has finally added it recently, most newer (and even a few older) languages have had it from day one. Some of the major examples that (to my knowledge) still *don't+ have this feature are JS (including TypeScript), C and C++.
I would say: "I very rarely use else". But there are some cases where an else statement is the only thing that makes sense, and it doesn't hurt readability.
So what about this: x = random(1,3) if x == 1 then print("E") else print("notE") ...instead of this x = random(1,3) if x == 1 then print("E1") elseif x == 2 then print("E2") elseif x == 3 then *_spontaneous combustion_*
@@smt4090 i dont know coding etiquette as i have none, but the first one can return in the if so you would need the else assuming you can write it in a function. The second one can be made into a switch case.
One thing that throws me off sometimes are functions that suggest a boolean return but don't actually return boolean. E.g canDrink suggests true or false return, but it returns undefined or string.
True, can't really defend against that but in a way you could consider that function as a boolean with edited returns since it is giving a Yes/No answer, except for Not in the US part in this example. I like your name btw, can't wait to do that when my own laptop
In such a case I think the best thing to make the code cleaner is to change the function name so that it doesn't imply a boolean return. Function names starting with "is" and "has" should probably return a boolean.
I agree with the general point I just don't think it matters all that much in this context. It's just a small example and even without the explanation it's pretty clear what the code is actually doing in its entirety. The code won't be published and the function won't ever be used for the result it produces or doesn't produce.
That's why I can't live without strongly typed languages. Not only do you have the ability to check the type that is returned by all the functions because it's checked and enforced at compile time, but it's also possible to have dynamic returns that aren't bools by, for example in C#, creating a special enumeration (enum, does JavaScript have it?) type just for that functions return. That way even if the return isn't true or false you can easily tell what all the options are.
I can understand replacing the initial if (not null) that covers everything (early returns are great), but what you've ended up with here is instead of one function which is pretty easy to follow, you've got two functions and the logic is now bouncing all over the place, it's spaghetti code. As the complexity of your functions increase, and you have more variables to keep track of, you'll find yourself building functions with longer signatures and passing stuff all round the place, and it's going to end up very unmaintainable. Also elses help you to understand the flow, and signify when you have a number of mutually exclusive cases, whereas a bunch of ifs don't really give you that same information. I can definitely see the value of considering whether an else is necessary, but I think here you've half improved the code, and half made it worse.
That's what I thought. Bail out early to showcase which cases will not be handled, but a function needing a function simply to avoid an else statement makes no sense. You could do the same thing inline and save making the reader scroll around and the associated stack activity.
See the thing is.... console.log(validateAge()) function validateAge(expr) { switch (expr) { case 1: return "can drink" case 2: return "can not drink" case 3: return "egg" default: return "Huh???" } }
@Jan Krynicky This shit iis how you end up with a main file that does nothing but load like 1000 lines of internal files that all are just helper functions What could go wrong
Yeah, the example code is pretty much nice as is, and readable. However, in today's development flow I like the canDrinkResponse pattern better, as that function is reusable, the caller deals with the result, not hardcoded in function. With one exception: IF it's operation depends on the age method, then the not null check should be in that function, and it should return null on null input. Then it is self contained and not fail on faulty input. By the way these return statements are not bad. May not help readability either, but sometimes an if/else is more descriptive, sometimes returns. Usually for guard conditions, error checking, returning early is the most wanted, while determining some state from variables the nested if/else make much more sense. Transforming either one to another is a hack in my opinion. Back in the days in assembly jumping out of a loop were a norm. No other way. Then when high level languages came, everyone yelled that NO JUMPING OUT of loops, and all sorts of these design antipatterns which doesn't followed the block nesting logic of the language. It is pretty much a pain in the ass to exit from the middle of block in a nested loop, which is iside of a something when an error occurs. Return or goto is the only nice choice (which considered evil), otherwise you have to make an error flag and propagate out the error, which is ugly as hell, hard to understand, but not antipattern. Today, few decades later, it is acceptable again, even considered more readable to return from a loop. Mindblowing! My final words, agreeing with Luke: if a code is not a deliberate hack, but written in a logical way, nested blocks where feels appropriate, returns from loops where feels the easiest way, then it is a good code. Most likely, if it is easy to write, then it should be easy to read. If someone can't understand it, then either get better someone, or have him spend more time on understanding. We should not dumb down our thoughts for everyone to unrderstand.
@@sehbanomer8151 Why would that be a problem? Nesting is a power tool of a language, any decent developer should understand few levels of nesting. If the conditional flow describes pretty much the same as a human would be doing then is it a most understandable and readable way. One can hack it more clever, it might run faster, but surely won't be readable. Sometimes it is necessary, for example there are embedded devices, but usually not needed even there.
@@gabiold It isn't about the case where your nesting lasts for 6 lines and is totally isolated that is the problem, but cases where the nesting is 3 levels deep, 50 lines long, and is included in / includes looping, etc. In cases like this, a single level of nesting can cause the code to have exponentially more branches, which makes debugging harder, comprehension harder, and maintainability goes down. I would consider this "no else" idea a tool. Like any tool, there are times when it is useful and times when it is not. A good programmer will know when it will make the code more maintainable to reduce nesting to reduce branch complexity and when trying to do so would just make the code less readable. I think it is also instructive in that, here we see that the naive approach is to have a single function with a lot of conditional behaviour, and by following this "no else" mantra we arrive at code which is significantly smaller and has resulted in two functions, each of which achieve only one task. This is a typical ideal of programming, that each function should only do one thing. So I think that even if this "no else" approach is not always the right tool, at least in this instance it has led us to more idealistic code in a meaningful sense by trying to adhere to it.
@@Hexolero I partially agreee and partially not. I disagree in that regard, that the main problem in an 50 line function is the excessive elses. Probably not, rather than organizational issue, which should be sorted first, and maybe eventually not the elses have to be blamed. And in the original case probably one can't even return at every else as probably other independent functionality follows, which might independent from the preceding condition. I agreee in that this is a tool, and a good programmer can (should) choose which approach is good in which case. The problem is that good programmers are not born from nothing, among other education materials, these sort of videos that SHOULD educate programmers to be good, thoughtful and open minded. This kind of teaching like "don't ever use this, always this", without options, without exaples of when it is apropriate and when not, which leads to short-minded thinking and mood-like aproaches of otherwise scientific things. The video is not bad, just biased. While I like the responsibility separation in the video's example, the canDrinkResponse function is still not independent in the video (I consider this as a bug), because the if null check is removed from it. It is to excessively shorten the code, but logically belongs there, and nobody going to have problem interpreting an 5 line function. While it is separated to two functions, it really the canDrinkResponse IS the business function, the other is just a console.log wrapper in this case. On a side note, in my opinion, separating an evil 20 line code to 5 function calls not necessarily makes it more readable, but most likely will make it run slower. Function calls have some cost over inline code, and excessive use is aa wrong as writing the whole code without functions. It is annoying that you have to open 10 files to understand what's going on, just to find out that all of them contains five 3 line functions which some way interleaved in each other. This is anything but readable. It is like buying books in 5 page packs. A good programmer should have enough brain to comprehend a healty sized file and a screen long function, if that's what required to solve the task.
After spending the better part of an hour composing this comment I'm hesitant to press the button. On the one hand, I don't want to come off as overly critical of a young developer. On the other hand here is a video with over 200k views, on a channel with over half a million subscribers, who gives some, what I argue is, bad advice. While parts of the have sound advice I think you are shooting yourself in the foot long term, in particular when it comes to returning early. I completely agree with how you should restructure the code with a blocker so that you don't nest your blocks, but the rest is basically not good advice. The first problem, as mentioned, is returning early. By returning early you have code with multiple places to change when you only want to change one thing, e.g. you may want to change what you return. It may look harmless when shown in an example as small as this but can cause havoc in a large code-base. It is a particularly big problem with untyped languages like JS because you don't have a compiler to help you find a, for example, type problem. The second problem is listing if statements without else if. You already showed that
@@godDIEmanLIVE Worry about having maintainable code before worrying about optimization. Outside of old school graphics coding or HPC very little of your code needs optimization more than it needs to be usable (and re-usable) for the long term. Once you find a bottleneck then you can work on optimizing it. But as my algorithms prof. said, the coding needing optimization probably wont be the section of code you thought it would be. And these days the compiler/runtime is better at optimizing your code than you are, so ensure your logic if formatted the same way the optimizer is expecting *cough*OraclePL/SQL*cough* Edit: Now I'm not saying "do not optimize", apply standard optimizations, don't walk a sorted array looking for a value when a binary sort approach would work better. Don't implement what should be a batch processing job as an OLTP process. Follow common sense. But that is part of writing code that makes sense.
@@danlandia4399 My problem with this attitude as such is that this is the reason we have shitty and slow software. You just get used to premature pessimization for little reason and think that's normal and okay to do, because it makes your code "more readable" by 2% or what. Fundamentally, our job is not to write software that pleases us or is easy to read or follows some ridiculous paradigm for no technical reason. Those are secondary concerns. First order of business is to write fast and efficient software. The default thinking should be "how do I write fast and efficient software", not "what can I get away with, because it doesn't matter". Performance ALWAYS matters. Then and if there is a substantial reason to write some a bit less efficient code, it might be okay to write something more visually pleasing or lazy etc. Otherwise we're just not doing our jobs and we're making a joke out of software engineering. What other profession is as careless with their craft? Imagine designing a combustion engine and trying to convince your boss, that the pessimization of 10% in terms of power efficiency is okay, because it was too hard to design something more efficient or building it like that makes it look pretty. And there is plenty of software out there that is 100s of times slower than it should technically be. At that point, we truly are a joke profession. Long rant and I don't really disagree with some of your sentiments, but I am of the conviction, that what I wrote above should be the default way of thinking about what we do, so we can be proud of our craftsmanship. If then we say, okay here it literally doesn't matter what we write, because it doesn't affect anything really or just pessimizes performance by a tiny bit and it has other significant advantages to do it this or that way. Okay, nothing against that. P.S the thing about the compiler is very dangerous and most of the time it's wrong imo. Often bad performance is a problem of data locality and memory and cache access. You can easily pessimize your performance by fragmenting your data by a factor of 100s and your compiler can do zero to help you there. Same with choosing correct algorithms for the task at hand etc. etc. That is 100% your job.
Over the years I've found "clean code" is very subjective because a lot of people have differing opinions about what they consider clean. However a sensible rule of thumb is to avoid a function having too many branches. At some point it stops being readable if you have to scroll to see what the function does, but having too deep of a stack trace isn't a good thing either if you can't even name them apart.
You should definitely avoid a function having too many branches. You should also avoid having too many functions whose only reason for existing is implementation detail. You should also avoid having a piece of self-contained logic being split into too many different locations (e.g. functions, classes, files, or whatever). Too many of anything is to be avoided. That is what "too many" means.
I "clean code" perl… and trust me, if you don't speak Perl, you don't read my work. "clean code" does not exist. Python was made for "clean code" and $°°° but it's a bloody nightmare to read… And If you work with binary cond else is half the work.
@@pierretonnelier9994 I was a perl enthusiast too, one of my favorite algorithm involved a recursive function. It was compact and very clean in my opinion, but the thing is people around me just had a hard time wrapping their head around it, anytime I would write code in a functional way it was hard for them too and you could see by the way they wrote code that they just had an easier time with imperative procedural style. The lesson I learned from that was that you don't write code for yourself and sometimes something less compact with just simple logic is better. Language is just syntax, that's not really the most important thing, even though sometimes they canake things rather unpredictable (looking at you, JavaScript...)
The version shown at 6:35 is the most readable IMO and the one I would write. I find the final solution way less easy to read because of the unnecessary second function I have to jump to and from. In this particulier case, the if/else if/else statement represents the exact logic, that's what makes this form so effective and straight forward, while the additional function method requires a bit of mental gymnastic to sort out
And function calls are SLOW. You are very correct that second function is unnecessary. In fact, copying its contents back to the prior function is simple, adjust for person.age. That is the simplest and most readable IMO. Coding is all about readability to the developer and the maintainers to come. I use the rule "keep it very simple, the same constructs used repeatedly. Definitely NOT every construct the language offers using a different one each time." Find a simple one you can reliably work with and use the hell out of it. That gives the greatest reliability and best maintainability, as new developers on the project (or you in 6 months when you've fully forgotten what you did there) don't have to learn and make allowances many and apply different thinking to each. I use the rule "keep it so simple I can understand and work with it when I have a full blown head cold or flu and should be home in bed". Because that's the day you'll have to fix it, expand it, rewrite it. Or someone else will.
There is a difference between readability and something just being what you personally are used to seeing. Something familiar isn't objectively more readable, even though you can easily read it. When someone says, "This is more readable IMO", they're using the word "readable" wrong. Readability that is opinioned is just familiarity.
Not exactly. Sore, in this context it seems unnecessary: all four cases log a string, why would we have three of them in one code piece and others in the other? There is a reason, although in this case I think Kyle chose a poor example. It does make sense to have a separate validation function for a separate valid business logic concept. Is age valid for drinking is a matter of domain logic. Is the person object, if valid, of drinking age, may be a subject of some, say, individual controller which is not sure which input it got. The problem with this idea though is that the function returns a string which does not seem to make sense within it. But that could be just a poor example on Kyle part. I think the idea he presents is quite valid, just the example he uses is not the best, and he may've mixed up different ideas here thinking it was one: - using guard clauses improves readability (mostly, yes) - single return per function decreases readability (often yes with a notable exception of non-interactive debugging like through logging) - separating domain logic from data validation is a good idea (yes for more complex cases)
yeah, like the first function IS the simplified function. He could've had the sole function use the exact same return functionality he used later, and just use `console.log(CanDrinkBetter(p))` for the initialisation.
Exactly - this is the same, just written differently. You still have to read all previous conditions when adding new one later in the code. Doesn't really solve anything. One good advice from this video is to use additional function to handle some cases. But still... this naming... it suggests boolean not THAT...
Meh it doesn't make sense, what if you cant exit early because you need to execute some parts outside the if? Idk about js, it is very necessary in c# at least
I don't think avoiding else completely is a good idea. It can help your function if you use early returns to get rid of edge cases, but if you have a more complex check, or you are doing calculations instead of just returning on each leaf of the binary tree then this method will probably not work. Also you shouldn't really turn one working function into several just for the aesthetics, it's not going to make it more readable if you constantly have to jump between 3-4 functions that call each other. In some cases it can work, but you shouldn't take either of these as general rules, more like neat tricks you can do in specific cases
if you use an else for a return for edge case you can just flip the if check to be inverted and achieve the same thing. if it's a specific else if, that's different, but still there are ways around it that use less code but maybe is less straightforward to read.
Just wanna add that if/else (branching) can be converted to binary logic and caching the results into booleans (howto-details on request). Timely I prefer this, aware it's merely another way of writing if/else. I use it for readability, and for reducing code path complexity in larger projects, which can be beneficial for testing and debugging (referring to code quality metrics). Else-If cascades I completely avoid. For conditional value assignment, I favor the ternary operator where possible (sign = value < 0 ? -1 : 1). Sometimes I use if/else for code flow control, while preferring the alternative: if (condition == true) call A, if (condition == false) call B. I know it looks a bit odd).
Not using else on usual day-to-day tasks: a good exercise to help you understand and actually improve your flow control skills as a developer. Not using else when actually needed just because someone said it's better: stupidity. The else keyword do not exist to endorse lazy development though. It has a purpose and it can and should be used as long as it fits that purpose.
Personally I think replacing 'if/else' with 'if return/if return', hurts readability more. You still have to check earlier in the code if every previous 'if' block has a 'return', in case the next block will not be executed. With 'if/else' you know straightaway that it's one or the other, not maybe this one if the previous one(s) didn't exit. In some cases it might help (it's more about the logic), but it is definitely not a 'good rule'.
the main problem with his initial code was the nested If/Else, not use of Else. The nested and deeply nested If/Else in a single method is what hurt readability and maintainability than everything else
I think that with the push to using functional programming that this style works better than if-else blocks. As Kyle proves in this video, there's less code to work with, and therefore less possibility of mistakes. If we can truly get away from relying on variables to control inter-code responses than whatever we write will be more reliable. Avoiding the 'else' statement takes us a little closer to that goal.
Agreed.. Early exit/fast fail is a no brainer, and factoring in general is great. But I'm really skeptical that converting the range mapping into a series of guard clauses creates any meaningful improvement in readability or reliability. If/else chaining is a well established pattern for this sort of thing and our brains are wired to quickly comprehend things we've seen before. As another TH-camr said - what's better than ideal? Standardized.
@@emissarygw2264 yea, if i had to work with kyle and he did that, id get out immediately, because whilst it might work for him, using the traditional style that literally everyone else uses is better, since everyone understands it immediately, and doesnt have to worry about the code being exited too early. Not only that, he is spreading small amounts of code into functions, which means to read his code, youre gonna have to start at the top, then jump to the next function and so on and so forth, instead of just reading it as it is. When trying to get a job, it doesnt matter how much better you think your method is, if everyone else doesnt like it, you aint getting that job. I made some examples using a condensed version of his thing (just using guard clauses without the bulky functions), and then how you could do the same thing using the if / else method, with the code being shorter, and still on one line. // using guard clauses const a = { age: 17 }; function canDrink(person) { if (person?.age == null) return console.log('You are not a person'); if (person.age >= 21) return console.log('Yes 🍻'); if (person.age >= 18) return console.log('Not in the US 😦'); return console.log('Nope 👶'); } canDrink(a); // What i would use const b = { age: 17 }; function canDrinkBetter(person) { if (person?.age == null) console.log('You are not a person'); else if (person.age >= 21) console.log('Yes 🍻'); else if (person.age >= 18) console.log('Not in the US 😦'); else console.log('Nope 👶'); } canDrinkBetter(b);
I'd say it does improve things. You're converting the negative conditions to positive ones, which is innately easier to think about. You're also pulling nested conditionals into descriptively named functions to help compartmentalize the thought process.
With one exception: do not turn the code into a maze of returns, keep them as close to the beginning of the function as possible, or at least make them visible inside your code.
it become a maze if it only have one return, just like a maze in general it only have one exit. if you have early returns you don't have to read all of the code just to understand one path of the logic. one early return in line 3 will reduce the mental burden of reading line 4 to line 10, because you know that one path of logic already ends in line 3 and is not going to be touched in line 4 to line whatever because of return statement in line 3.
@@RoastLambShanks 1, you shouldn't make a function expecting the function calling it to have already done a null check. A switch statement would been the best way to do this.
I never had a problem reading code due to usage of "else". On the other hand I did encounter difficulties due to early returns and unneeded nested function calls which simply break the locality of logic. I'm not in favor of spaghetti code. Just use commonsense when writing code and remember that there is nothing inherently wrong with "else". Don't waste your efforts as an anti "else" fanatic.
This. Different situations, and thus different requirements, require different implementations and different solutions. This video is superfluous and completely unnecessary. I would argue it would lead to more messy code following this videos advice. The concept he's loosely touching on is actually short-circuiting, which is a beneficial technique for improving algorithm efficiency. "Guard clause", in my opinion, is a terrible name for a short circuiting if
I've had issues due to overuse of else, but only in programming languages that offered no viable alternative. When if/then/else is your only flow control, anything slightly complex becomes a nightmare. Normally I'd just advise avoiding such languages, but sadly it's not unusual when dealing with industrial control software (looking at you, Wonderware) that BASIC dialects are still the norm. Supposedly, they're easier for someone unfamiliar with programming to debug, but honestly if you have someone unfamiliar with programming tinkering around with heavy machinery controls, you should be sued into oblivion. This stuff can kill people.
The main issue with this video is that ... JS supports switches which in this case wouldve been the correct thing to use to simplify it not just visually but also in code so it only has to be evaluated once.
@@unknownalien3837 yeah, "guard clause" is a fine name for a clause that guards against invalid input. But this guy also uses the term incorrectly for simple short circuit returns that are not really guarding against any unexpected/invalid input.
Seems to me, the 'else's were the least of the problems in your code. It was the functional decomposition and the switch from '!= null' to '== null' that caused the real improvement.
I was sitting here sweating when all of those console.log calls still existed halfway through the video. Thankfully he dediced to create a string function rather than a void function for the actual logic towards the end
@@holonaut tbf the usage there was mostly just for demonstrative purposes. yeah, could easily have been heavily simplified with only 1 console log, but, generally, when you do need to print that much stuff to console, it's probably mostly for debugging and you're going to remove them anyway :D
I would be vary about this “no else” approach. You are introducing implicit conditional structure into your code and while it might make sense if like in this case it is one liners, then I have seen plenty of cases where if statements are long and not knowing the if statements must come in that order is just bugs waiting to happen. Guard cases on the other hand are great!
This! I would go one step further and adjust the if statements so the order doesn't matter at all. Yes this adds overhead but it will make sure that somebody who messes with your code will not run into issues without fully understanding what that code does.
well, with "else" ordering is also critical. The only way to ensure ordering does not matter is to expand every single condition, like "if (person.age < 21)" into "if (person.age < 21 && person.age >= 18)". But that's bad to maintainability too, do you agree?
@@yevhenkozlov286 Absolutely! The ordering matters in an "if", "elseif", "else" block too - But on refactoring I would tend to keep the order there more likely compared to a bunch of single if-statements. By design in such a block only one branch will be executed - I dont need to worry about the content of the branch itself. As perlohmann mentioned above its quite easy to read if its one liners.
Exactly my thoughts. Besides, it applies pretty good on JS Web Programming. On System Porgramming or other areas, I dont think it's a good idea, beyond the Guard cases.
Whenever you're working with a single variable, try and use a switch statement instead. Something like this: ``` switch (true) { case age < 18: return "Nope"; case age < 21: return "Not in the US"; default: return "Yes"; } ``` The beautiful thing about a switch statement is that, you can add one more condition, pretty easily without changing much.
Default still works like an else this not solves the problem. The point is that whatever you wanna use for if a decision has to be taken or not you dont wanna be in the condition that else or default accepts unexpected results like null or undefined so you need a guard as explained in the video! Have a good day ;)
@@Nunzio.o I get your point. But you can always have an if statement for checking whether the variable is `undefined || null` before getting into the switch. OR, better yet, you can literally have an `if` statement inside the `default` case. What I showed was just an example of how to use a `switch-case` statement for single variable conditions, rather than an `if-else` ladder. Hence, that's the beauty of a `switch-case` 🙂
Multiple returns in code are a really bad idea. It is why they are excluded for safety critical system in MISRA and IEC 61508 (Functional Safety Standards). These standards have grown as essentially a list of lessons learned from a variety of safety critical development projects over decades.
You could still do this : let result = "" switch (true) { case age < 18: result = "Nope"; case age < 21: result = "Not in the US"; default: result "Yes"; } return result; This solves the non-multiple return issue
switch statements are UGLY imo. You introduce more nested brackets which doesn't improve the readability. In that is the case, make a static list or hash with a function pointer! That's more clean.
Using console.log is in my opinion a special case that’s not really useful outside of demonstrations. I would have changed the function so that instead of canDrink (which btw sounds like it returns a Boolean), I would’ve named the function something like getDrinkingMessage and have it return the string. This way, you would not have to have an additional function and in your main program flow, you would console.log the result from that function instead. And because you return the string instead of using it inside the function, you also improve the reusability of your code.
isn't it what he did in the last exemple ?, btw i don't know javascript, but i guess Function mean you can return what you want? for the name, i totally agree he should either find a different name or add something like CanDrinkStrg
It should be common knowledge that functions beginning with "can" or "is" *should* return a boolean. Such rules make code easier to read, especially in untyped languages like JavaScript.
@@gizel4376 no. He just split function in two for nothing, he could just replace all ifs with function, that he extracted and console.log(canDrink()) outside of function
The downside of early returning / continuing / breaking is that you can't tell by the IF's condition itself whether your code below it is going to trigger or not. You have to inspect the entire IF block for return / continue /break statements to find out if your code below is in a "virtual" else block or not.
The switch statement only works if you're checking specific values from only one variable. One situation where you could easily have several chained if else statements is when doing validation with clear error messaging
Agree. Even in this case, i find it harder to see where the group of checking the age starts and ends with all the returns. With the if and a few else statements, it is way clearer to see what belongs together.
i used to think just like you... but i'm reading "clean code", by R. Martin, and it seems that using nasty if/else statements, or either switch/case, you would be violating the open-close principle. After using this technique i could significantly increase the quality of my code, also making it more readable... check this video from Kyle and then tell me your impressions: th-cam.com/video/-ptMtJAdj40/w-d-xo.html
it's a matter of the paradigm you are working with. The way you programmed is strongly align with functional programming, because you are centering yourself in within conditions (functional programming). And avoiding the need to keep track of a variable (state). That's why the code looks cleaner and more abstract. It's more cohesive. In the other hand using if/else and creating a variable (state) to then modify it. Makes more allusion to Object oriented programming. It's a matter of how you are centering yourself. I strongly prefer to encourage the way you code because functional programming makes allusion to recursion and it encourages people to create better abstractions. The answer to whatever you need to use If or else is mostly redundant. Both work, it's a matter of preference.
It works only when writer itself understands multiple paradigms and still be able to make choices depending on project standards. Unfortunately most programmers are unaware of it till significant amount of time until they realize this with other codebase, best practices and from their own experience. In my experience it's always about sticking to common tongue that other developer's speak when it's a teamwork and always a personal choice when going solo. At the end of the day all loops, conditions and switches are converted to jumps and jump with conditions in either real cpu or virtual machine, those who are fimilar with assembly can easily relate. This essentially abstracts english mnemonics that we use to write executable code.
I don't think guard clauses is a "functional vs OOP" situation. Guard clauses are just a generic way you can safe guard your arguments or safeguard the value of some variable/state according to some conditions. Which is equally applicable to both OOP and functional paradigms. But yeah, it's a matter of preference. Guard clauses are great at cutting down needless nesting which is vastly superior in terms of readability/eligibility. Can't really say there's anything superior about nesting ifs and elses together. I would go as far to say it isn't really a matter of preference, as one is just sloppy. At least if you're using the example given in the video.
@sdsdsds dsdsdsdsds not necessarily. Depends on if in your recursion you are calculating the same thing multiple times, in which case you should use something like memoization to prevent this. A good example of this is Fibonacci with recursion. Fib(n) = Fib(n-1) + Fib(n-2). You are essentially calculating the same (n) multiple times so you should store the result in some kind of shared cache.
if else statements have nothing to do with object oriented programming. And neither does using a variable to keep track of stuff. Unless you see an object in what you just mentioned?
The problem with programming teachers is often they teach hard rules without explaining the intent behind the rules. So it just becomes blind adherence rather than something followed conditionally. A good example is the "one return per function". There is a good reason for this, sometimes. Other times, what he does with multiple returns is fine. When people understand the intent behind the "rules", then they know when they should be followed, and when they can be broken.
I’ve had some programming teachers show rules without explanation and some with explanation. What I’ve noticed that is the more advanced the class, the more in depth the explanation is. With your one return analogy, an introductory class might skip over the explanation to cover more content while a more advanced class will go into detail about unreachable code.
It can be a problem with bad teachers. But it's also a problem with bad students. If they don't know the reason for the rule, they just assume that there isn't any reason for the rule.
Agreed - the MISRA rules explain all reasons why things should be adopted. These are not theoretical - they are effectively a case study of lesseons learnty over decades of issues that caught people out in real world sceenarios.
Here's another perception. The conditional is already done in the ( age < 21), the less than, equal to, greater than operators are already conditional checks, adding in the "if" statement is an extra conditional check. The boolean operator will already evaulate to a 0 or 1, which can be used to multiply with the string. If you multiply the string by 0, it will be "" blank, if you multiply by 1, it will be the string once. Console.Log("No" * (age < 18) + "Maybe" * (age >= 18 && age < 21) + "Yes" * (age >= 21)); Now you completely removed the extra comparisons. That's branchless programming.
But when the logic gets more complicated you could end up with tons and tons of small functions which don't really improve code readability. I think the best option is not to avoid the else keyword but rather use common sense - if you feel like your code is getting harder to maintain, then refactor it. It's the same case as code optimization: think about it only when it starts to matter.
Yeah, this is only useful for use cases like the example shown when a function has a single primary purpose, not a function that is meant to tie a variety of other functions together into a workable program.
@@Martin-pb7ts I would disagree. There are cases when breaking down a function into smaller functions provides no benefit, and putting the majority of the code in one function would be better. Functions are useful and necessary to help reduce duplication of code, but they are not necessary to group it into parts that can easily be distinguished. And then there’s the performance overhead of function calls, minimal, but it exists, and should not be neglected.
@@noahsmith2555 I would disagree based purely on what is the intention of many small functions. If it allows you to have a more descriptive set of tasks based purely on the function names and allows better unit testing then its worthwhile. Functions aren't just useful for reducing duplication but should be used if it helps with communicating your ideas to others using that codebase, I would personally always go for readability if that is the intention of many small functions that a developer has chosen
@@dennisgatere7821 I think youve missed the point of the argument, which was that using a lot of small functions wasnt outright bad, but in certain situations it is. Most of the time using small functions is good, however, there are some specific situations where you wouldnt want to for various reasons. Maybe theres also confusion around everyones definition of a "primary function". What is classified as a primary function? Thats a rather subjective term. There is a point at which increased abstraction comes at a cost in terms of readability, and of course, performance. Also, regarding your view on readability, while using many small functions can certainly help make your code more readable to others, theres no reason that the same cannot be done if few functions are used (Comments exist for a reason).
I literally can't believe I watched this through. No wonder there are so many comments on this video, i'm not going to re-invent the wheel for a single value checker when we have the switch statement... it will in fact make it even more complicated to read. At least when you see a switch, you can immediately see the intent.
@@ryanswatson nah, switch can check for exact vaues but it cannot check if something is greater or smaller than that value (well, it might depends on actual language used)
Corry, I agree if the language is not JavaScript. In JavaScript, I’ve seen many people avoid switches because they are implemented to automatically continue from one case clause to the next and must be manually stopped with the “break” keyword. In general, not a huge problem but you always have to remember to break, which is just one more thing to worry about. As such most people would only use switch in JavaScript when returning a value because that automatically breaks out of the switch without running the results of the following case clause. That would not work in his example because JS switches only check equality, not greater than or less than. Now if we were working in something like Elixir, we could use “cond do” and call it a day :D
It looks like my code before I learned about else :D It was mentioned already, but this approach may cause bugs and loose of logic. You also need to follow exact order of your if statements and may need to repeat same criteria for more complicated code. Also, it may be small difference, but in my understanding else allows to make code quicker. Every time you call IF, program need to check whether it is true or false. Else does not do so, because it already know the answer. Because you use more IFs and more likely repeat same IF conditions, your code slightly less efficient.
Yeah, it kind of bothers me because there is more to code than being clever. The compiler will optimize things based on best practice and often times clever code is actually significantly worse. I used to do this kind of thing all the time before I got into compiler architecture. Turns out best practices are set by the people who designed the language (and compiler 99/100 times) and not usually by other clever people. Who’d-a thunk? xP That being said, creating unnecessary branches is not great and something to avoid. I would prefer this video be “You use else too much” rather than “You shouldn’t use else ever.” Guard clauses can be better for many cases.
Yes because else is unconditional, it does not redirect the flow of the program. I don't know if you've ever tried writing simple code in assembler but you can see how these conditional jumps translate into several operations. On a small scale program it often is not noticeable, segments of code that aren't optimal won't be observable but when dealing with a large set of data all these checks and jumps can drastically affect the speed of execution. I don't see how there's any advantage to getting rid of else condition unless you have such a problem that literally requires you to specify all possible outcomes. But more than that, you should worry more about nested loops
I think ur point about else being efficient isnt applicable to this example. It's the if that comes with cost. What you do following it, whether that be standard else or not, has the same cost. So for example "else" is coupled in the cost of the original if. But if you do "else if" your adding another if So if ur if will return or run the next block its the same computation whether u follow that with else or not Tagent but that'd what at low level ur aiming to remove every "if" in gles I often had to figure out something that would remove the if by way of maths for example you might try and replace ur if with something that would nullify that part of the equation (e.g multiple the expression by your boolean) There is also an interesting quirk/trick I've seen in c++ with empty if blocks and boolean functions that I'll have to dig up lol but I wouldn't recommend it lol
For maintainable code, I would recommend throwing in the guard clause for things like invalid objects. Instead of separating into two functions just return the string and do the printing in the main. Other than debug messages, I prefer only the main loop to do any IO. function canDrink(person) { if (person?.age == NULL) throw 'Age is required member of person.' if (person.age < 18) return 'Nope!' if (person.age < 21) return 'Not in the US!' return 'Yes!' } const patron = { age: 29, } const drinkingToday = canDrink(patron) console.log(drinkingToday)
Hey hey, same! And then when two functions in two separate files need to use the same smaller function, you can move it to a global 'utility' file. I discovered that you can return not just strings, but also the whole fetch, so you can append to your own functions. That just blew my mind 🤯
The problem with multiple return statements is that it's easy to forget some that are in the middle of a function and not realise some part of the code doesn't get executed when you modify the code later. it's a source of bugs that can be easily avoided, that's why they teach not to do it. Personally I do use early returns a lot in guard clauses exactly like you, defensive programming where you check your assumptions first is really good form. But past that I do avoid multiple return statement, caching the "result" in a variable is way more sensible, as you can add stuff later on to the end of the function, and you're sure it's gonna be reached in all cases.
Personally, I think that's just as prone to bugs, since you have to make sure you're not affecting the result between your "stop" point and the return. I'm not saying it will never be useful, but I do like the multiple return because it means I can safely ignore the rest of the function from the point of exit onwards without worrying about how I handle the variable later on.
I hear you. But if you stick to Robert Martin's rules for function length, multiple return statements are easy to spot. "The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that. Functions should not be X lines long." Different developers use different values of X. I find that keeping my functions under 20 lines makes them easier to understand and test.
@@MartinOmander I dislike these kind of rules, some functions are long because they do a linear process that logically belong together. cutting them in pieces just for an arbitrary length rule adds a lot of overhead and is extremely annoying to read through. plus it's slow to code, especially when you have to change parameters. that being said, I obviously avoid multi pages functions, but they end up being cut up according to my refactoring needs, not according to some arbitrary layout rule.
@@feandil666 Agreed, how we organize our code should be driven by our refactoring needs, not some rule. Whenever I've written a function, I check its length. If it's over 20 lines, I take it as a signal to think about whether it should be split up. About 80% of the time, splitting it up makes the function easier to understand and test. Maybe this "rule" should instead be called a "reminder to think".
@@MartinOmander I've kinda been thinking about this. Say you've got a struct that holds some data as well as two functions, one for generating a string out of that data (for saving to disk, sending over a network, printing to console, etc.) and another one that parses such a string to get the original data. In my experience, the parsing function can become fairly large, especially if the structs holds large amounts of data. For every field you need to get the corresponding string token, return early if it is null, then optionally convert it into an integer or float, then perhaps return early if the data doesn't make sense (input sanitization can be kinda important), and then apply it to the struct. You can hardly shorten it, and you also cannot easily break that up into multiple functions either. Any thoughts about this?
Developers should always be cautious of other developers telling them NOT to use aspects of a programming language. There are developers out there who say OOP is bad and that you should only use functional programming (OOP is good). if else is probably the first keywords in programming, macros are literally if else statements. All if else does is to determine what you give it, is true or false, if you have bugs that means you haven’t properly tested your code (and that’s ok. Don’t blame it on if else). And if your having trouble reading if else statements then your going to have problems reading code period. Like one commentor stated previously this is more of a style thing NOT a proper way of coding.
@@SrIgort but you are bot checking for one thing. You're checking for multiple. There might be a If(x) then resultx = a Else resultx = A If(y) then resulty = b Else resulty = B Return (resultx & resulty) That is checking for two seperate thins. But still using the else keyword. And that is not what is happening here.
Develpers should be cautious of developers who categorically say ”X is good”, as in meaning ”X is always good”. OOP has its uses, but there are many instances when you are much better of figuring out a functional solution to a problem. I can recommend this video which shows what is sometimes within reach if you consider the functional approach: th-cam.com/video/vK1DazRK_a0/w-d-xo.html
We're talking about JavaScript, a language originally designed for people who don't know what they're doing by people who didn't know what they were doing, some of whom even intended for it to fail and be replaced by VBScript.
Years ago in C, if there is a lot of if/else then I would use the SWITCH statement, I found that helped make it clearer. Thanks for the reminder and advice to avoid using a lot of if/else in code.
Early returns can be good (as long as you've taken care of any co-concerns like events or logging and tracing). However I think the gymastics you need to do to get rid of "else" are usually not worth it. Most basic language constructs are avoidable if you're willing to put in the time and effort in doing something weird. Is it worth it? Sometimes but not usually.
Depending on the type of project you're working on, but usually not, it's more about flexing it seems. I've written code that could be described as clever but often times it's not necessarily better, it just looks out of the box Early returns are always good, why execute code needlessly?
@@joschmo4497 Eh, the CPU is actually executing exactly the same number of instructions in both the first example and the early return example. Either way after setting up the registers it does a compare, a conditional jump, sets a string pointer, calls another function, then returns unconditionally. Just in the first case the conditional jump fails so it falls through to the instruction in the next byte which is the start of the else block, in the second the conditional jump passes the instruction pointer gets overwritten and it executes inside the if block instead. The CPU really doesn't care one way or the other between these two it only starts getting extra workload once you start making parts of it into a new function call, a function call results in at least one instruction to put the current value of the instruction pointer to the stack, one jump to the functions address in memory, one instruction to pop the return address from the stack, one final jump to return. Obviously any additional parameters will require further instructions since the pointers to those values must each be put on to and then pop'ed from the stack also. So unnecessarily breaking down simple functions into very small parts is not particularly efficient.
@@seraphina985 if you want to optimize code, you need to take valid benchmarks before and after. As you say, the "better" version takes more steps to execute, and is otherwise just "different" not "better" as the same readability drawbacks from chained if/else statements exist in switch statements and in his consecutive short circuit returns.
they're very useful in C, especially in embedded programming. As C doesn't have exceptions, you often return an error type that you define as an enum - something like OK and ERROR. If you were to save this to a variable, you're wasting cycles that can be very scarce in MCU - say, you're recording values every 10us on a system with 150MHz (or you're doing something in hard real time). Then you only have 1500 cycles to do your thing. Declaring then saving to a variable and recalling from a variable is already a few cycles - let's say around 10 for a low estimate. That's 'only' ~0.7% of your available cycles, but hey, those cycles could be used for other things. I still think that's a big problem with modern programming - most non-embedded things are so inefficiently coded that it doesn't even make sense to efficiently code. Write python? Unless you're using a C++ library such as numpy, just fucking do something - it doesn't matter anyways. Need something time critical implemented? implement it in C++. Javascript? Lol, don't even get me started. Most 'software developers' are just slinging libraries around to quickly accomplish their task (inefficiently), because we have the processing power necessary for it.
@@iFireender now you're arbitrarily defining efficient. The company pays the programmer to do work. Does it make sense to pay more for something that produces the same results a few milliseconds faster for a normal human workflow? That all depends on how the program will be used. Will it run billions of times per second, or only a few times per minute? Will it run on a constrained IoT device or on a server? Will it need to run on a user's click and respond immediately, or on a schedule where it can take 5 minutes and nobody will notice? JavaScript has its place as well. If you need a web app, or you are in a startup mentality of get it out now and fix it later, then it might make sense. If you want a backend service that's solid, executes fast, and will be maintained for a few years, then it probably doesn't make sense.
Generally speaking: yes. extracting complicated code in its own function is a VERY goot idea. But: MOVING RETURN ON THE SAME LINE AS THE IF DOES NOT REMOVE NESTING. this merely HIDES the nesting. Also you had 2 levels of nesting, now you have 1 level of nesting (because of the initial guard clause) so, concerning nesting, you did not really change that much.
It also didn't improve refactorability at all. As someone who actually teaches people to code, I am... rather concerned that his threshold for a function being "too big and complicated" is "it contains an else".
> *extracting complicated code in its own function is a VERY goot idea* Functions are created to prevent duplicate code (a code block, algorithm, etc) and not for cleaning up code in one place. If the created function is only called/used in one place, then it shouldn't have been created.
@@DejitaruJin it's an example function that is made simple for viewer comprehension. I think the meaning of the video is to build cleaner coding practices and to think about possible solutions with a different perspective.
Dropping else-statements in favor of return statements, is pretty bad from the POV of computer science since it removes the ability of code checkers to prove that your code is valid. Mathematically, you can no longer prove the code always does what the requirements tell it to do. And this is my main issue with this approach. Readability can be solved by refactoring into more functions. If you don't understand nesting and the correspondence between provable code and correct code, the solution is not to throw out the else, but to get better at programming.
2 ปีที่แล้ว +1
Huh? The transformation between the two styles is pretty mechanical.. Why would a checker choke on one style, but not the other?
This is a fairly common misunderstanding. Yes, static analysis and code provability uses a single return point to make certain kinds of analysis tractable. However, regardless of how many return statements you use, every function only has a single return point. This guarantee comes from structured programming and isn't negated by code style choices. Consider that the control flow from any function call returns _to_ a single point, i.e. it returns _to_ the point from which the function was originally called. This is the single return point, not any of the zero or more return statements within the function. Even in the case of exception unwinding, control flow still passes as-if through that same single return point. Given that you can rearrange any code that uses early return statements to not use them, it follows that for any early-returning code with a flow graph G that there exists a homeomorphic / equivalent non-early-returning form of the code with a flow graph G'. Modern static analysis tools already handle such code without issue.
"Readability can be solved by refactoring into more functions." This, seeing the code in the video I was just thinking "wouldn't you just do function canDrink(person, country) { return person.age >= getLegalDrinkingAge(country); }" so that your code actually describes "to drink you need to be above the legal drinking age" instead of putting too much of the solution into one function?
I do prefer guard clauses. When used to eliminate nesting levels on large if statements I think they increases readability in most cases. But I also think every situation should be evaluated on its own, rather than just arbitrarily restructuring every program to remove else. There are many usage cases where using else is cleaner and more efficient, particularly when there is more code that follows and you can't just return.
I would advise *against* falling into JS-only development patterns (even when writing JS), as it makes it harder for multi-language developers to deal with your code.
@Brandon Busby If you don’t want to read; Tl;dr, this is the only language that contains early returns and is super unstandardized which will fuck everyone over programming in this style let’s take this for example in c/c++, the current language I specialize in ```c if(d == "a") { printf("you printed the letter "); } printf("you didnt print the letter "); ``` Both will execute, this concept does not work in c/c++ Not to mention in a python style language where curly brackets are not used in, this is just painful to deal with. You should never stray from the standardized language systems that everyone is accustomed to. Everything in this video is bullshitting you and is just plainly lacking of basic common sense. It does not affect performance either so why bother with this? It’ll make your code horrible and not even readable.
@@snesmocha Have you actually tried, or did you just assume you're correct cause you specialize? C and pretty much every C-like language allows early returns. This literally works in C: void someFunc (int a) { if (a < 0){ printf("a < 0 "); return; } printf("a >= 0 "); }
I suppose you might want to avoid early returns when dealing with malloc when it comes to C specifically, and so maybe you would want to make that a standard your C projects. But that's an issue specific to C, and so in many languages this is totally ok.
Yeah, "return console.log()" is a fucking aberration. It hides the fact that console.log doesn't actually return anything, and that your intention is not even to return anything, just to save one line of code. Trying to turn everything into one liners by taking stupid short cuts like this will make your code a fucking nightmare to deal with
As a person who's been doing this for 4-5 years at this point, I've learned that if something is better, but inconsistent with the existing code, then it's simply not better. Imnsho guard clauses are objectively better for most cases. But gl&hf teaching that to people who have been coding with nested stuff for years. And no single convention is worse than mixing 10 different ones. Exact same thing can be said for immutability.
true uniformity and consistency with the current code base is far more important. experience developers emphasize upon design patterns, but really, what they should give importance is not doing anti-patterns.
this is the worst part of javascript, you can do even the very simple logic in many ways, too many syntactic sugar, too many freedom, make it painful to read. same shit diferent code. imagine a programing language so bad that people make another language (typescript) just to fix it
"the system sucks so I'm going to write worse code instead of trying to fix it". I really suggest you try to push code cleanup on your projects, it is an integral part of development IMO
Good luck convincing business people (who are the ones distributing the money at the end of the day) that you're going to spend a day/week/month to rewrite code that has already been tested and known to work, just so your if-s are better or you distribute it better. Then have it retested... Very unlikely.
@@nov366 it's not impossible, but I personally don't fully agree with the "avoid else AT ALL COST" statement. It can be readable with "else" statements imo, but when coming to such deep nesting, if you really go deep down a rabbit hole then the idea is a good practice to avoid so much heavy indentation. (weird how rabbits have nests in this scenario)
@@giladshmueli5831 imo avoiding anything at all cost is a bad practice because in most cases you're usually dealing with symptoms and not the problem. I mean overcomplicated code frequently is a result of a) not reusing your code properly b) not splitting functionality properly c) 'type as I go' attitude without any planning whatsoever. 'Else' is just a cherry on top of it.
Using guard statements is a valid way to make you code less cumbersome to read because you removed one depth of nesting, that's a really good takeaway. Removing anything *else* form that code doesn't really give anything, in this case it's better to just stick to whatever convention you have in your codebase already and stick to that.
Agreed if you are working in an existing code base. Making new form changes can really become a burden later. In this case where an else clause can be reduced to a single line, these are improvements, so long as the second function's code is put back into the first--the second function is unnecessary and SLOW. I've 40 years programming experience, really learned the single return point from Pascal programming in 1980. I didn't like multiple returns from functions especially long ones or complex ones. Before syntax colored editors finding where the code actually exited was a pain. The trouble with multiple subs breaking down the functioning is that they become scattered in large files, chewing up time finding them, and across multiple modules, such as when refactoring or expanding the calling parameters and adding new associated logic. There is also a large performance hit to calling a subroutine. So the rule there is "keep your code simple, use functions to encompass a reasonably strong amount of code, generally not just a few lines, and so minimize the number of functions needed. And keep them close together as best you can. Seeing them all together is usually necessary when extending their functionality or refactoring."
I think there's definitely something to minimizing nesting (at least when it improves the control flow) but I'm not sure that creating a bunch of tiny functions for every little thing is the way to do it. It cleans up the main function body, sure, but the way I see it if you're reading the function body there's a decent chance that you want to look at the contents of all those helper functions you defined to keep the code "clean". It's agony when you're trying to find a bug etc. and every function just calls a bunch of other tiny 3< line functions. Honestly I prefer a chunky function here and there to a fractured codebase that is impossible to read serially (not that the example you showed fits in this category but I think that's what you eventually end up with when you apply the principle of defining helper functions gratuitously anytime you do anything). It's a balancing act of course but I just thought I'd make an argument for the opposite POV since you didn't really touch on that.
100% agree. Big functions can be unreadable, sure, but so can big call stacks. Also, functions that only have one caller can give the false impression that the two parts are logically independent when in reality they are coupled.
Definitely agree in taking caution with over-breaking-down functions into other functions. I've seen this in extreme cases in the wild where every single part of every function was broken into more files and functions, including the helper functions themselves! It took hours to days to do small refactors and bug fixes. This is in an API which I took the time to refactor one of the endpoints. Went from over 18 files and hundreds of lines of code and fragmented logic, to about 4 files that were maybe 100-150 lines of code total. Turns out the helper functions just weren't really helpful.
Doesn't every IDE have a 'go to declaration/usage' hotkey? I find it super easy to navigate the abstraction layers as long as all the helper functions have good names. If it really becomes a problem then just inline the function to work on it and re-extract the helpers when you get it working again.
To me, everything that has a much higher/different level in detail gets it's own function or code that repeats. I use methods to keep the specific subfunctions grouped to the main aim. works ok-ish.
@@graffhyrum The point is that it is off course situational. A big function is not automatically hard to read as long as the control flow is nice and obvious. It is also perfectly possible to make a function much harder to read if it is broken up into poorly though out helper functions, to the point of creating a labyrinth of function calls that only serve to obscure the actual flow of the program rather than just abstract away the gritty details. When I was a noob I was overly zealous about never letting any function get too long, and ended up with a mess of hard to explain helper functions scattered all over the place. The solution was to just accept that sometimes long blocks of code are perfectly fine as they are.
I guess it is just what you prefer. For me, the "better" function is actually more complicated and less readable. *shrugs* Also, lots of little functions are less speed (and often, space) efficient. Control transfers (jumps, branches, calls, etc) are often more costly to execute than a simple stream of instructions.
> Also, lots of little functions are less speed (and often, space) efficient. Control transfers (jumps, branches, calls, etc) are often more costly to execute than a simple stream of instructions. That's... Debatable.
@@timnonik2736 Maybe if you are writing extremely optimized and high performance code for something specific, but for a web developer it definitely doesn't matter
I can't tell you the number of hours I've wasted before trying to make guard clauses work for hypercomplex situations when a nested branch would have been the simplest and most efficient use of my time. Guard clauses can save you a lot of headache, but they can also create confusion in your code and waste a lot of time and just as importantly, space and duplicated code, under certain circumstances. This is a god technique to employ when possible, but never let your preferences get in the way of actually completing the task. Code that runs is always better than code that doesn't. Everything in moderation. PS: Also any time your code gets complex enough that it _could_ be non-obvious... just leave some comments in. Some people comment too much, others too little, and have strong feelings about either extreme... but frankly, comments can be a godsend when working with a base you haven't used before or in a long time. Just make sure your comments actually mean something. Sometimes its better to comment your code and use uglier branches than to write an excellently beautiful function that has the next guy jumping back and forth over a billion different functions and methods trying to understand where the single piece of behavior they're trying to alter is. Having everything in one place, but properly documented, can be easier to work with at times.
That "next guy jumping back and forth over a billion different functions and methods trying to understand where the single piece of behavior they are trying to alter is".. hits too hard.. omg the times ive opened a project and everything looks clean and neat but then this function leads to this file which in turn imports this file that imports that file isnt worth it. I think that's why even microservices are being rolled back to/into monoliths, or at least larger more manageable microservices
@@Kiev-in-3-daysI get where you are coming from, but I imagine this is exactly what leads to: "next guy jumping back and forth over a billion different functions and methods trying to understand where the single piece of behavior they are trying to alter is"
@@fryphillipj560 Well, that is just not what I am experiencing with my code. I know exactly what functions do since the name of a function is self explanatory. And each function is located in the feature or category object they belong to. If you organize your code properly I guaranty what you describe just doesn't happen. Sure you jump a lot but a jump is just a fraction of a second in visual studio code.
@@Kiev-in-3-days it happens 😁 Coding is not in small steps for me .. I always keep a bigger picture of what's happening and the code in front of me helps keep that picture .. so even if the function is a click away in some code editor .. it's still distracting ..
returns in higher level programming, if they even get compiled to assembly before execution anyway, will get abstracted into comparisons that point branches at a single return. (same with intermediate languages that have a return atom like MSIL, write C# code with multiple returns and the MSIL it is built into will probably still only have one return instruction) so if this even still matters for assembly, you aren't writing assembly, there's no reason why the system that produces assembly from your code has to trust you to perform a literal return at the exact position that you told it to (because these languages are able to use jumps and breaks to get around, and that isn't an anti-pattern because of how low level they are)
@@MagicGonads I have to confirm what you say, but I agree with you. At least in C, when I look at disassembled code, I see returns always branch to the epilogue of the function (the code you don't see when writing a function) where things get tidied up for return. So I don't see that having multiples return is a problem for any modern compiler, nor I see a problem with using else, or any other keyword or statement. Some programmers jump like crazy if you just mention that C has "goto" they always tell you "you should never used" because branching like that is a really bad idea. I always think "here is another programmer who has never looked at disassembled code or has no idea of any ISA". I remember attending a lecture from Donald Knuth where someone asked about this particular issue, in the end he not only said that he used "goto" multiple times in the past but also stated that any statement or logic expression can be dangerous. He just mentioned some study he conducted with his students back in the 70's, so no programming technique is good or bad, just depends on skills and scenario.
Else has a place, just like guard conditions have a place. If your entire function is basically a guard condition what is it guarding? It reminds me of how when someone learns something new, they try to find creative ways to use that new thing and end up using it in a way it isn't intended. To quote Abraham Maslow, “I suppose it is tempting, if the only tool you have is a hammer, to treat everything as if it were a nail.”
Very true. Use various scripts and classes where it makes sense. Use functions where it makes sense to use them. Use else conditions where it makes sense to use them. To just eliminate else altogether is pretty stupid, imo. There is no right answer on when and when not to use either, but use common sense when approaching both.
And this is something I think about when I program. How can I make my code in such a way where I can make quick changes in the future while understanding its impact on the overall outcome? Should I create a separate class so I can readily import it to various other scripts? Should I break up my code into chunks so I can reasonably call upon functions multiple times, instead of needlessly creating one that will only be called upon once. Should I use an else if statement or a nested if statement when handling various variables? These are all questions I ask myself, and I know the answer isn’t always the same across the board.
Using early returns in place of else only improves readability in very few cases. Most of the time, it just makes it more difficult to tell that code is conditional. I cannot tell you the number of hours I have spent trying to figure out why some code wasn't running, only to find a "return" hidden deeply in an earlier if block.
Yeah, agree. This is a real pain especially when maintaining legacy code. You find what seems like the most appropriate place to make your change, only to find that the new code doesn't run sometimes because of the early return buried somewhere. I favor an approach where you use guard clauses to sanity check the parameters to.a function early on, and return early if you can't proceed. But once your function gets into doing "real work", it should run through to the end with a single return point.
One could argue that adding an extra function to replace if statements also unnecessarily complicates code. I find reading the code without the else requires understanding the run-time order jumping between functions versus being able to parse the logic manually.
Code is for humans. So I write to be readable even for another person, a year or more later. Most times condensed code is harder to read and some times might lead to problems. I want to make my life easier, I don't want my colleagues to always ask me "what do you mean by this?"
If you want to do that at the cost of writing inefficient code then you really need to think it all over again. It is basic knowledge that nesting increases the complexity of a code (yes Ik it is still done in tons of cases). If there is a way that you are able to avoid it then going for it is the best thing you can do. Code is supposed to advance with time and it will keep getting complex with time as well. It is really a bad mindset to not wanting to improve and stay in your comfort zone just so that it doesn't become too much of a task to keep a tab on what's going on. Humans adapt to change as we always have. Not just humans but every organism that are thriving currently were able to adapt to changes and that's why they are alive. Constant improvement and wanting to be more efficient is a mindset that is extremely important if you don't want to be a typical C++ developer who just wants to stay at their comfort zone and keep writing the same shite every day for the rest of their lives cuz they are too afraid to learn something new as they are way too comfortable. But then, that's just my mindset. I understand if you don't get along with it and no offence from my side. Just said what I thought was important. In no way I want to start any sort of argument or what not. Peace.
In the example in the video, as long as the functions are named properly and according to convention, the code is more readable than with nested if/else clauses IMHO.
What happens if later on there's a bug with your conditional that would typically show up inside the else clause? Since you don't have one you'll be dealing with a silent error and if you don't remember what you did before you could be in a world of hurt. There's a reason the ternary operator enforces it: You should always have an else statement.
For straightforward programmatic code like this, it is not really needed since age can be well defined, and returned alongside the other outputs either for debugging or added to the console output "you are xx years old, you may..." so that it is clearly visible. In a case like this, one should also add a clause that demands that p be an integer, and below some reasonable number, and then raise an exception on failure. It gets trickier with multiple variables, especially when they are mixed values and mixed formats. I work with big data, and most of my code does nothing more than validate the inputs as valid. The other important aspect of exiting early is that it is much more efficient. Performing a bunch of Booleans on 10's of millions of data points runs slowly even on the Super where I work. Put the most common exit condition as close to the top as possible, and move on to the next datum.
@Erich If your if statement has faulty logic it could evaluate to false and continue until it reaches the else statement. If you don't have an else statement you won't be able to quickly identify which condition is failing. I've seen it take hours before so all my conditions have an else clause.
@Raymond Andrews No, not exactly. Think about it, if condition is false, and the ternary operator can only output when its true, then what happens? Not all programming languages have the concept of JS undefined, maybe the output shouldn't be undefined either. Your argument on the operator's name is unnecessary and it could be called something else if you wish.
I've often omit "else" after "return" myself, for example when I write recursion and start with the base case. Mainly because I'm lazy. But I disagree that it's generally more readable, and especiallly that it produces less mental overhead. To wholly understand what the function does, you pretty much have to add the missing "else"s to reconstruct the control flow. It's easy for experienced programmers, but it rather adds a bit more to the mental overhead than makes it less. And I don't think "canDrinkResponse" is a good example for extracting a function. I don't really see any advantage doing it except omitting that one "else". So it comes down to one level of else-nesting vs. an extra function. I'd say that one level of else-nesting produces less mental overhead.
If you're building larger systems that will require long term maintnance switch is probably worse than using a string of else if due to how rigid it gets when multiple systems need to interact with it. Think about it, you need to explicitly state what to expect in a case, then if you need to change one of the cases, then you'll also need to change it for all other systems.
I had a prof in college that wrote a subroutine that "simplified" the logic for us. The "simplified" logic had multiple nested switch statements up to 3 level nesting.... He taught primarily engineering and not much comp sci.
I like the guard clauses, especially right at the top of the function where it's easy to spot. I saw a lot of beginners get into trouble by NOT using the else. They would write code that only checked the true condition with no thought about what happened when it was false.
All programming techniques have their place. Sticking to or avoiding certain techniques are limiting. Instead we should try to use the best technique for a particular use case. In the case of the code shown I would have rather used a switch statement as I think this is a more clean approach. Multiple If statements, or If Else statements also have their place though.
Wont the extra function calls add overhead (however minimal) instead of just using else within the same function? It really depends on the language and compiler but unless the compiler optimizes code in a very specific way extra functions will just add unnecessary time to execute. But i also agree you need to gain readability at the cost of performance sometimes so its definitely a fine line.
I've only ever messed around with Arduino stuff, but on microcontrollers (from what I've read) the 'if/else' way of doing things is generally preferred to switch/case statements or separate functions because of speed. For my feeble brain, I just stick to if/else because I can come back to project code many months or a year later and still understand my own writing. Also, the early exiting/returns make me nervous, since I'd like to test all available variables in a function first procedurally; I've been caught out doing the 'early exit' thing before.
Javascript compressors like Terser will eliminate those inner functions if they are simple enough. Generally, compilers offer "function inlining" optimization since decades.
@@grantorino2025 function canDrink(person) { switch (true) { case person?.age == null: console.log("You are not a person") break; case person.age < 18: console.log("Nope") break; case person.age < 21: console.log("Not in the US") break; default: console.log("Yes") break; } } const p = { age: 22, } canDrink(p)
Having nested Ifs and returns inside them is a Code Smell. Inputs / parameters verification with direct returns should be placed and at the top of the functions (also, split your logic in functions / methods). Also if you're evaluating a variable just use switch case.
@@thefreeze6023 switch case is more readable in cases where you can have a different number of actions depending on the value of a parameter. It's more readable than trying to use Ifs. This is also very use-case dependant.
Guard clauses are useful for stuff like the initial "not null" check, but I'm not sure I agree with the other changes as much. Moving stuff that's only used in one place into a separate function IMO makes code harder to follow because you need to move around to multiple different functions just to follow what's happening. With more complex functionality you might end up bouncing between like 4 or 5 different functions just to follow the logic of one operation, and in all that bouncing around it's easy to lose track of what exactly is happening, when, and why. As for the early return clauses with every "if" statement, I don't really see how it's any easier to follow than just using "else". In fact it increases the chance that someone new to the code misunderstands it because they miss the return line and don't realize that only one "if" is going to be used. If you really hate "else" then it makes more sense to just use a switch statement; in fact all you're really doing by returning after each condition is jury-rigging your own switch statement out of ifs.
Yeah, breaking the code into several function just to avoid a straightforward If/else statement is what leads to "enterprise code" such as splitting FizzBuzz into a FizzResponseStringFactory and a BuzzResponseStringFactory, both being children of a StringFactoryFactory of course. (I know its one hell of a slippery slope and mostly for comedy)
@@hil449 does it really? Your entire decision tree is on the same level as the rest of your code, regardless of how many logic levels deep you are. That's the antithesis of readability
The least restrictive constraint is actually the inverse: !(age < 21). That is, everyone 21 or over (the majority of people). If you put < 21 first, then < 18 would return ""Not in the US" instead of "Nope", which fundamentally alters the logic of the original function.
I appreciate that this is 2 years old and you may have already covered it in a future video. My favourite technique for dealing with your problem is using the try/catch coding style. It works similar that you switch your checks around.
@@Kevin-lh6xu seems like he is simply rearranging and splitting his original nested if-else block. What i mean by style is that one could easily elect to say use a Switch block, ternaries, null coalescing etc to accomplish the same thing. Sort of at the end of the day comes back to ones coding style vs a wrong and right way of doing things (similar to how if multiple people sat down and wrote an essay... we would all for the most part structure our essays by paragraphs differently/logic to accomplish the same thing)
@@harshilpatel387 I have worked in some big projects (hundreds of classes) and else and switch case statements are the critical places where bugs are introduced. It is good and common practice to avoid them and use little processing blocks. Switch cases can always be replaced by a better pattern and are reserved for the factory pattern.
@@gabrielyea "Switch cases can always be replaced by a better pattern and are reserved for the factory pattern." Please implement Duff's Device using the factory pattern.
Nested ifs and loops can get real nasty, so the functions can really help clean things up. But past that point you're probably right, unless there's some branchless path you can take which would definitely speed up your program.
The deeply nested if else code is known as the arrowhead anti-pattern. The name comes from the visual appearance of the code like an ... arrowhead because of the indentation.
Totally questionnable : what if, for example, one developer calls your "canDrinkResponse" function with an undefined age ? If this function is only to be used inside your other function, it shouldn't have visibility anywhere else but in your "canDrinkResponse" function. "Else" statement is often used for code robustness matter, in embedded softwares development for example, and it can ease analysis when tests are concerned.
What he says about single return per function does make sense but for explicitly typed languages such as C++ or Java its better to only have 1 return only (although it would work) Aside that: your point makes sense, why not make a class with private attributes and […]. It just gets cluttered. If and else exist for a reason
I absolutely agree with you when it comes to cases where you may have to many nested if/else-clauses that it gets unreadable. But readability is not all! If you use an ELSE, you exclude everything that is not covered by IF. This is means ultimatively, that you have to cover ANY case, that is not covered by your IF. It's just logically easier to use an ELSE instead of "IF'fing everything out"... you little hacker. ;) Very nice video.
In my opinion, "else" does not hurt a code's readability (unless, of course, long nested cases are being used). I think it makes the reading way more natural, especially for someone who is pretty new at coding. Also, removing it doesn't really improve it, it changes the way it appears better to you in terms of simplification, even though numerous functions for just one specific action do make the reading a little more complicated; because after all it's just 1 small process that makes the executor skip to the else's code block, so even if you removed it it wouldn't really change that many CPU processes and the code would be more clear to who's new at coding, as the "else" keyword is a fundamental ground to step on when coming to a code's reading naturality.
Having spent most of my career (35+ years) writing c based embedded code for automotive and aerospace, the biggest part of coding has always been on reliability and robustness. Most of that revolves around code that has zero ambiguity and decisions that have a coding answer to every permutation. This ensures that you have considered all these and single return points from functions are usually better from code size as the local stack handling for the function call is done in one place. Also, creating more function requires bigger stacks and has a performance hit, so understand the end target before just removing keywords! For this reason, MISRA C/C++ was created for highly robust and deterministic code. Will be different for web based coding, but it's principles are extremely valuable and well worth a read.
I've been at it about the same time... I've worked for various major aerospace and DoD contractors over the years... I seriously doubt that this kid has ever worked on a large project which required large numbers of developers in multiple groups at multiple companies...
It’s nice to understand the affect of branching statements on your code’s performance. Using / not using if-else isn’t usually a performance optimization. It’s a valid tool, that has its benefits. Replacing if-else with any other type of statement will also have its affects on performance. I low-key hate these videos that tell people to adopt a new style into their code. Just write regular code.. To elaborate: If-else statements perform similar to switch statements - after about 6 comparisons, it’s recommended to switch (assuming your working with switch-valid values). It’s not uncommon to write a method that requires a conditional, which might return a result. You can use the syntax: return (condition)? if_value : else_value; Which can serve as a single-line statement, that could be used in lambdas, loops, or maybe a call to “this(…)”. Else blocks might be necessary for a specific flow you might want. If you have a method: int f (x) { if( condition ) return 0; // constant value else return f(x+1); // recursive call return x-1; // not constant value } When you say you don’t use else. I just think you’re coding to the fullness of your capability. Some people try to avoid branching statements with arithmetic expressions : iff it is possible to do so - which can help the compiler. Assuming your running some pretty expensive code. That’s such a trivial situation too. Usually, we might look at fork-join pools to help optimize performance like that.
I was taught to use else in my first couple introductory programming classes. But when I took data structures and algorithms, my professor advised us to try not to use else. It was hard at first, having to get creative with different methods. But after using it for a while (just like recursion), I totally saw the value in using it and it totally made my code way better.
This is called "early out", and has been used for decades. It's interesting to see people rediscover it. However, I've leaning toward functional style lately, and that may require `else` in my language of choice: Rust. In Rust `if` is like a ternary operator, and `match` is like `switch` in Javascript and other C-like languages, but on steroids, and unlike `switch`, it's an expression, not a statement. Like watching a recursive algorithm execute, there's a kind of elegance in data flowing through a single expression that happens to be the only expression at the root scope of the function. Also, you can leave off the `return` keyword and the final semicolon of the final expression in a function to imply that it's returned.
Cutting through YT 10-min Padding: * Use guard-clauses instead of else's * Reject Single-Return, Embrace Multi-Return * If nesting, consider making a function for it instead
"Single-Return" Single-Return is a necessity for many areas. When intensive logging (entry, exit...) is your only friend, multi-return is the last thing you want to use.
Multi-Return is good for things like guards at the beginning. Other than that, it should be used carefully and logically so as to keep the flow of a function readable.
@@JohnSmith-xf1zu So tell me then, why even guards are not allowed to be used for control systems for nuclear power stations and aircraft? They are explicitly forbidden. Perhaps all these people extolling the use of them have more experience of stopping things going horribly wrong in disasterous scenarios - I doubt it though :)
@@JohnSmith-xf1zu The real thing is this is in safety critical software it is not even written like this. Most problems are first modelled as a finite state machine - there are no if statements really. The problem is modelled as an FSM using a UML, or even Yourdon. You do not encounter guard statements. All routes are caterred for, and fall out of invalid event and state combinations. The whole issue with software development and naval gazing on issues such as this, is they have not actually designed the code properly in the first place, or used best practices from software that needs to be "safe", and also actually testable. It is easy to start programming and look clever. It is way harder to think harder and design. It is what annoys people who develop software when functional safety is required hen too much emphasis is given to algorithms. For example, examples given for Google / Facebook interviews all rely on recursion. If you rely on recursion in say a fly by wire aircrafy system - it is not determinstic, and you could be flying along and you blow the stack - end net result - plane falls out of the sky!!! So SC engineers have to follow rules - which are lessons learnt from people making assumptions. Sooo design first, do not use multiple returns or guard statements, and do not use recursion - if you are clever enough you can linearise recursion algorithms in a fixed memory space. And so on and so on - the MISRA rules are always worth a read.
else statements have their place. Its just if things start getting hairy with them all over the place, then there's probably a better way. Sometimes a new function is the solution, sometimes a case block is. Just depends on the program requirements. But most of all - less is more as long as its human readable
That second if-else looked totally fine to me. I've seen so many articles like "switch is bad" or "don't use else"... The reality is: DO use early outs and polymorphism whenever it improves the readability of your code but DON'T restrict your coding to a certain style because you believe it's the only viable practice. Switch and else are clean and relevant in many situations.
A professional software is developed being restricted to well defined certain specific single style. That separates "professinal software development" from "coding". We had this world class coder in the company, he was winning coding competitions all around world. He could code insaly complex functionalities inline to single line. He was free to not restrict himself to certain style, specially once he was fired. His code was unmaintainable as everyone else struggled to read and understand it. Software development is a team sport, if you do not work together in same way as the rest of the team, you are of no value to any team.
@@michaelholopainen2822 Does unconditionally banning else and switch look like "professional software development" to you? Of course we need a strict coding style and stay consistent with it (no matter if we're working in team or not!) But we should never forget the end goal: making the code look clean! And banning else and/or switch, as appealing as it sounds, goes completely against the idea of clean code.
I disagree with you. Although in your examples it is actually better to try to avoid else statement, in some more complicated programs avoiding else statements will make them less readable, especially when you have a long code and have to jump between functions every time you want an if statement (and you’ll also need to pass it all the variables required, which can be a pretty long and nasty function deceleration). I do agree that in some use cases avoiding else might be better, but it many cases it will just make it more complicated. Therefore it shouldn’t there shouldn’t be a rule “always try to avoid else statement”, it is different for every case, and the programmer should think what suits it the most.
It’s more intuitive to do error checking without else. However, by using extra function calls, you are creating more overhead. If/else has hardware support, so it would run better on resource limited computers than using multiple function calls
The canDrinkBetter function at 4:37 is the best why would you need returns in every if when you can have a clear else. it's less readable having a console.log at the end of the function but returns everywhere so you don't really reach the end in 90% of the cases. and why would you split a 11 lines function in 2 ?! code a real software with this mentality and you'll stackoverflow without even failing a recurtion
The troubles i went through (started 30 years ago) with others saying that early exit is not allowed and every single function has to have a single exit point. That was also the cause of many deep nested ifs, because according to best practices it was not allowed to exit early. Happy to see it now changed, but be prepared for many 'seniors' that review your code will still don't get it. I am not really happy about putting stuff into another function. If that function is only called from the function you are refactoring just leave the code there. You otherwise quickly end up in a treasure hunt to see where things are actually happening. If you have to resort to another 'should not do solution' because writing not a single else becomes the new mantra then one problem is solved and another created. As long as an if/else is not nested it is fine. And for many situations a switch is also fine. Just use the one that is most clear and does not result in deep nesting. Most important for me is when i read a function i can read it from the top down without having to open other files, search for functions etc. Naming functions clearly then becomes the main thing. Oh by the way your deadline is tomorrow, so many times good enough is really good enough.
I get it, but if you have multiple nested ifs you've designed your code badly - not that multiple returns are OK. The reason for multiple returns is nothing to do with nested ifs. It's for consistent responses through your code - its all to easy to return with one response, and to change the response type or format in the next response. Having a single return guards against a different format return. Functions should be no more than 50 lines long anyway (I would say 20 really) and so multiple returns do not add any granularity in terms of visibility and add their own problems for debugging. I started 30 years ago as well, and I've modernised most of my coding. But having developed and maintained multiple enterprise level systems I can tell you multiple returns is insane.
@@gruffmorris9098 Were these multiple enterprise level systems not in type safe languages? Because there's no way to return different types in anything type safe - you'll get it thrown right back in your face as soon as you finish the line of code. I don't get this hate for nested if's, either. What's the problem? It's rare but, if a decision is complicated, you're going to need it. (You're also going to need some solid comments around the code for exactly the same reason.)
@@ZlothZloth Truly complicated decisions can be split up into smaller functions with descriptive names and parameter lists. (Or sometimes replaced with polymorphism.) I have not yet seen a situation where this could not be done.
My friend has been told by his college teacher not to use break or continue in the loops because its "bad practice" and instead use true / false flags. I would take everything you're being told in college with a grain of salt...
Continue using break and continue keywords. In real world, client will not ask for that "Did you use break or continue in your loop?" or whatever. As long as your program gets the job done then you're good to go.
I guess to use break might be a bad practice, I rarely use it, instead use a lot of continue.... if you think about it, why would you wanna break a for loop ? doesn't make sense only if you're looking for something in it, in that case you use a .find() ... Might be if some state changes in that case you want to run an recursive function to navigate an object/array and have a guard clause
@@genechristiansomoza4931 I consider that you should be cautious about giving those advices. Of course, a stakeholder will not care whether you clean code or not, it is a matter of maintainability of the software: the code you write has repercussions to the team/company you work with if it does not allow extensibility/reusability. I suggest you to read about refactoring and clean architecture to gain insight into this topic. Best regards!
@@jonatlop3816 I did not give bad advise. I just said not to be afraid of using break and continue on loops if necessary and if it gets the job done. Refactoring code is a different topic.
Continue and break are very widely used and useful. In many cases, replacing them with a Boolean in the loop will be tedious and cluttered. Don’t listen to those people.
At 5:45 you introduce the result-variable, and at 6:15 you add an else if back in. Instead you could change the order of the if statements, so that you check for "< 21" before you check for "< 18" thus removing the need for "else if" cause the "< 18" will overwrite the result if it is true.
What a great illustration of dangers of playing with logic and then breaking things up. At 6:16 you've broken the logic so that it would never return "Nope". When you break up if/else chains you need to keep in mind all the logic changes you're actually introducing.
Also it's possible to do this way: function canDrinkResponse({ age }) { return [ { validation: age < 18, value: "Nope" }, { validation: age < 21, value: "Not in the US" }, { validation: true, value: "Yes" }, ].find((c) => c.validation).value; }
Also you increase the run time from constant time to linear time, and also increase space from constant space to O(n). Not a good solution for speed or memory management.
I agree with David l. This is code I would have rejected in a code review. It doesn't add any value and only makes it more difficult to read, understand, explain - and slower as well. There may be cases where similar pattern may be useful, if you have many validations, but at that point you'd likely solve that differently and be more general to the approach.
What you show here is a typical case of over-engineering and it would most likely be rejected in a code review. Fancy: yes, understandable / readable at the first glance: absolutely not
One more big advantage is unit testing. I know from experience that breaking down of the function into modules makes it that much easy to unit test. Just make sure you don't overdo it ☺️
Yes, but he broke it down waaaay to much. There is zero point in testing the first function. you're pretty much writing the same function to test that function.
i had heard the 1 return per function, but i dropped that pretty quickly when i realized that multiple returns provided code "safety" to terminate when i wanted it to and prevented the chance of unintended logic flow continuing past where it "solved" the question of the function.
This is known as a guard. Your code is going for an ideal flow ("yes") and you check then return non-ideal flows to guard the ideal. I'm a fan of it, it's part of clean architecture
For some functions, like the one you showed I don't use else and I usually have more than one return. I think that there are still situations where using else is better and it even looks cleaner
While this approach is interesting, another solution is to use a state machine approach. State programming is much easier to maintain, and can also address considerations such as single return points (which in some systems/environments is important). It would also allow much easier substitution for drinking age in different regions, whereas this example would become difficult to maintain.
For regional code, it's probably just better to use a function table (or some other data structure that maps region to function). Considering the simplicity of the example, a state machine would be overkill.
Those who have issue with what functions might return or whatever, use typescript. And everyone is saying that else in his code didnt have to be removed etc..., but this was just an example, in a real world scenario, sometimes nested if else can get really messy to understand, its upto you to determine when its time to simplify the mess, in a code like this we won't have to do such things but again its just an example of how to improve the code not an example of what kind of code to be improved.
The main thing missing from why its worth doing this is unit testing. I guess JS doesn't have a huge testing culture, but that's one of the bigger reasons. It often takes more, and more complex test scenarios to get all the paths of nested if/else blocks. But with the smaller function with two ifs you know exactly what and how to test passing in just an integer not an object.
The argument is strong, but if the logic is the same and doesn't hurt badly the performance, it's also too strong to avoid like hell "else". In pursue of that, people use big hash tables everywhere, multiple key/values, it's nice, it's beautiful. But many times code turns not as readable as a simple vanilla if/else for every people you expect to work on that code.
I'm appreciating your creating and sharing these video works. You do a lot of things right. And that's cool that you also point out competing videos: excellent positivity. Chapsas has a lot of videos, but I have a difficult time following his speech: it's a bit too fast. Your speech is fast too, but your annunciation is quite excellent and I have zero problem understanding you. I love that you present directly, quickly, and clearly.
Oh god, I hate switch case! Yes sometimes I find them useful but most times they just make code clunky and unreadable! And the whole idea of "Falling through" unless I add break? WHY??? Couldn't they have done the reverse to fall through only with continue instead? The default behavior should have been to break and exit out if there's no continue. (Curiously, Liquid seems to implement it this way.)
There are generally two reasons why you would want to extract a piece of code into a separate function: reuse and decomplication. - Reuse is when it needs to be used from more than one place. - Decomplication is to “divide and conquer”, and make it possible to reason about (and possibly test!) each individual piece in isolation. The example in the video illustrated decomplication, and could arguably illustrate reuse as well (by adding some code which uses the string result differently from logging it to the console). The removal of “else” seems to have been just a happy coincidence of these, more fundamental, refactorings. I don’t think it should be a goal unto itself.
You may have already seen Nick Chapsas's video on this same topic but if not you should check it out. th-cam.com/video/_ougvb8mT7k/w-d-xo.html
His video was the inspiration for me creating this video as I loved what he had to say and wanted to add my own thoughts to the conversation.
It's funny. TH-cam recommended me one of your CSS battle videos, which led to being suggested more of your channel, which led to being recommended Nick's channel. Saw his vid on the else keyword too, feels like such a small world haha
Just the other day I was talking with my girlfriend about Nick's video!
Will you make a tutorial on game development
How are you using those emogies in code
@@anshjain9293 do you use Win10? If so, Windows Key + . (dot)
Take it from someone with close to 15 years of experience in multiple programming languages and styles: Always take advice like this with a grain of salt. Yes, "else" hurts readability *when used ineffectively* (mostly with long statement bocks, which obviously is bad), and sometimes early exit is preferable, or even more modern idioms like switch expressions if your language supports it (JS sadly doesn't). BUT in many cases it's much clearer and simpler to just use traditional if-else. If the statement blocks are both just a single line each, with nothing else going on in the function other than a single pair of if-else statements, then there's absolutely nothing wrong with it. If, on the other hand, the else keyword is hidden after pages of scrolling down, you have a complete mess, and it's high time to refactor your function and at the very least split it up into smaller functions - and early return won't save you in that case, it may in fact make things even worse. Obviously, be sure to have decent test coverage before you refactor.
Just use the idiom that conveys the semantics you intend to convey in whatever idiom feels most natural for the job at hand. More often than not, if-else does just that. It's always a matter of context. The important part is to take a moment or two to think about it rather than mindlessly applying whatever idiom someone on the internet told you to be the one and only true path. And if in doubt, ask a collegue for a review.
If the statement were phrased like "Don't use else SO MUCH", now that would be something I could get behind.
switch case statements are modern huh? Terry A. Davis would be so proud.
@@minerscale By the way, I *do* hope you know the difference between expressions and statements, at least if you call yourself a professional programmer
@@cod3r1337 I thankfully do not call myself a professional programmer. I'm just a hobbyist doing a music degree. As for the difference between expressions and statements, having just looked it up I think I have a decent understanding of the underlying concept that expressions evaluate to a value whist statements do side effects, and that expressions are inherently safer because the flow of data is more clear, but I don't think I ever put proper words to it, as I lack a comp sci degree or anything of the sort.
Is it incorrect to call a switch a statement?
LOL also comments help quite a bit to explain the function ;-)
@@minerscale First off, if your are a hobbyist it's fine if you don't know all the theoretical basics. I happen to be a hobby musician and don't know much about musical theory either. Just try not to wave your lack of knowledge around like a badge of honour. You are just going to piss people off for no good reason.
Your understanding of expressions is halfway correct, but the actual definition is more limited. Expressions can have side effects just like statements, the difference is just that they evaluate to a value. Most mainstream languages have no way of guaranteeing an expression to be "pure" (side-effect free); some FP languages do, but those are hardly mainstream (sadly, one might argue). Still, it's generally considered good practice even when coding in a non-FP style to avoid side effects in expressions, or at least make them as explicit as possible.
As for your question: It's absolutely correct to call switch a statement, but in most modern languages it can also be an expression. This allows for some constructs that are much more concise than the traditional statement style. C# has had this feature for quite some time now, Java has finally added it recently, most newer (and even a few older) languages have had it from day one. Some of the major examples that (to my knowledge) still *don't+ have this feature are JS (including TypeScript), C and C++.
I would say: "I very rarely use else". But there are some cases where an else statement is the only thing that makes sense, and it doesn't hurt readability.
So what about this:
x = random(1,3)
if x == 1 then
print("E")
else
print("notE")
...instead of this
x = random(1,3)
if x == 1 then
print("E1")
elseif x == 2 then
print("E2")
elseif x == 3 then
*_spontaneous combustion_*
@@smt4090 What about it?
@@smt4090 else if is built in every programming language. You will use it sometime like it or not.
@@smt4090 i dont know coding etiquette as i have none, but the first one can return in the if so you would need the else assuming you can write it in a function. The second one can be made into a switch case.
For me I only ever use a one level ELSE
One thing that throws me off sometimes are functions that suggest a boolean return but don't actually return boolean. E.g canDrink suggests true or false return, but it returns undefined or string.
True, can't really defend against that but in a way you could consider that function as a boolean with edited returns since it is giving a Yes/No answer, except for Not in the US part in this example. I like your name btw, can't wait to do that when my own laptop
In such a case I think the best thing to make the code cleaner is to change the function name so that it doesn't imply a boolean return. Function names starting with "is" and "has" should probably return a boolean.
I agree with the general point I just don't think it matters all that much in this context. It's just a small example and even without the explanation it's pretty clear what the code is actually doing in its entirety. The code won't be published and the function won't ever be used for the result it produces or doesn't produce.
Totally agree
That's why I can't live without strongly typed languages. Not only do you have the ability to check the type that is returned by all the functions because it's checked and enforced at compile time, but it's also possible to have dynamic returns that aren't bools by, for example in C#, creating a special enumeration (enum, does JavaScript have it?) type just for that functions return. That way even if the return isn't true or false you can easily tell what all the options are.
I can understand replacing the initial if (not null) that covers everything (early returns are great), but what you've ended up with here is instead of one function which is pretty easy to follow, you've got two functions and the logic is now bouncing all over the place, it's spaghetti code. As the complexity of your functions increase, and you have more variables to keep track of, you'll find yourself building functions with longer signatures and passing stuff all round the place, and it's going to end up very unmaintainable.
Also elses help you to understand the flow, and signify when you have a number of mutually exclusive cases, whereas a bunch of ifs don't really give you that same information.
I can definitely see the value of considering whether an else is necessary, but I think here you've half improved the code, and half made it worse.
That's what I thought. Bail out early to showcase which cases will not be handled, but a function needing a function simply to avoid an else statement makes no sense. You could do the same thing inline and save making the reader scroll around and the associated stack activity.
See the thing is....
console.log(validateAge())
function validateAge(expr)
{
switch (expr) {
case 1:
return "can drink"
case 2:
return "can not drink"
case 3:
return "egg"
default:
return "Huh???"
}
}
That's not what spaggheti code means. Also, making your code more modular usually makes it more maintainable, not less
@Jan Krynicky This shit iis how you end up with a main file that does nothing but load like 1000 lines of internal files that all are just helper functions
What could go wrong
Having a second function is not “spaghetti code“ at all! You don’t know what the term means
If you avoid else because it's "confusing" it's not the syntax that's the problem
Yeah, the example code is pretty much nice as is, and readable. However, in today's development flow I like the canDrinkResponse pattern better, as that function is reusable, the caller deals with the result, not hardcoded in function.
With one exception:
IF it's operation depends on the age method, then the not null check should be in that function, and it should return null on null input. Then it is self contained and not fail on faulty input.
By the way these return statements are not bad. May not help readability either, but sometimes an if/else is more descriptive, sometimes returns. Usually for guard conditions, error checking, returning early is the most wanted, while determining some state from variables the nested if/else make much more sense.
Transforming either one to another is a hack in my opinion. Back in the days in assembly jumping out of a loop were a norm. No other way. Then when high level languages came, everyone yelled that NO JUMPING OUT of loops, and all sorts of these design antipatterns which doesn't followed the block nesting logic of the language.
It is pretty much a pain in the ass to exit from the middle of block in a nested loop, which is iside of a something when an error occurs. Return or goto is the only nice choice (which considered evil), otherwise you have to make an error flag and propagate out the error, which is ugly as hell, hard to understand, but not antipattern.
Today, few decades later, it is acceptable again, even considered more readable to return from a loop. Mindblowing!
My final words, agreeing with Luke: if a code is not a deliberate hack, but written in a logical way, nested blocks where feels appropriate, returns from loops where feels the easiest way, then it is a good code. Most likely, if it is easy to write, then it should be easy to read. If someone can't understand it, then either get better someone, or have him spend more time on understanding.
We should not dumb down our thoughts for everyone to unrderstand.
I think the problem is nesting of if elses
@@sehbanomer8151 Why would that be a problem? Nesting is a power tool of a language, any decent developer should understand few levels of nesting. If the conditional flow describes pretty much the same as a human would be doing then is it a most understandable and readable way. One can hack it more clever, it might run faster, but surely won't be readable.
Sometimes it is necessary, for example there are embedded devices, but usually not needed even there.
@@gabiold It isn't about the case where your nesting lasts for 6 lines and is totally isolated that is the problem, but cases where the nesting is 3 levels deep, 50 lines long, and is included in / includes looping, etc. In cases like this, a single level of nesting can cause the code to have exponentially more branches, which makes debugging harder, comprehension harder, and maintainability goes down.
I would consider this "no else" idea a tool. Like any tool, there are times when it is useful and times when it is not. A good programmer will know when it will make the code more maintainable to reduce nesting to reduce branch complexity and when trying to do so would just make the code less readable.
I think it is also instructive in that, here we see that the naive approach is to have a single function with a lot of conditional behaviour, and by following this "no else" mantra we arrive at code which is significantly smaller and has resulted in two functions, each of which achieve only one task. This is a typical ideal of programming, that each function should only do one thing. So I think that even if this "no else" approach is not always the right tool, at least in this instance it has led us to more idealistic code in a meaningful sense by trying to adhere to it.
@@Hexolero I partially agreee and partially not.
I disagree in that regard, that the main problem in an 50 line function is the excessive elses. Probably not, rather than organizational issue, which should be sorted first, and maybe eventually not the elses have to be blamed. And in the original case probably one can't even return at every else as probably other independent functionality follows, which might independent from the preceding condition.
I agreee in that this is a tool, and a good programmer can (should) choose which approach is good in which case. The problem is that good programmers are not born from nothing, among other education materials, these sort of videos that SHOULD educate programmers to be good, thoughtful and open minded. This kind of teaching like "don't ever use this, always this", without options, without exaples of when it is apropriate and when not, which leads to short-minded thinking and mood-like aproaches of otherwise scientific things.
The video is not bad, just biased. While I like the responsibility separation in the video's example, the canDrinkResponse function is still not independent in the video (I consider this as a bug), because the if null check is removed from it. It is to excessively shorten the code, but logically belongs there, and nobody going to have problem interpreting an 5 line function. While it is separated to two functions, it really the canDrinkResponse IS the business function, the other is just a console.log wrapper in this case.
On a side note, in my opinion, separating an evil 20 line code to 5 function calls not necessarily makes it more readable, but most likely will make it run slower. Function calls have some cost over inline code, and excessive use is aa wrong as writing the whole code without functions. It is annoying that you have to open 10 files to understand what's going on, just to find out that all of them contains five 3 line functions which some way interleaved in each other. This is anything but readable. It is like buying books in 5 page packs. A good programmer should have enough brain to comprehend a healty sized file and a screen long function, if that's what required to solve the task.
After spending the better part of an hour composing this comment I'm hesitant to press the button. On the one hand, I don't want to come off as overly critical of a young developer. On the other hand here is a video with over 200k views, on a channel with over half a million subscribers, who gives some, what I argue is, bad advice.
While parts of the have sound advice I think you are shooting yourself in the foot long term, in particular when it comes to returning early. I completely agree with how you should restructure the code with a blocker so that you don't nest your blocks, but the rest is basically not good advice.
The first problem, as mentioned, is returning early. By returning early you have code with multiple places to change when you only want to change one thing, e.g. you may want to change what you return. It may look harmless when shown in an example as small as this but can cause havoc in a large code-base. It is a particularly big problem with untyped languages like JS because you don't have a compiler to help you find a, for example, type problem.
The second problem is listing if statements without else if. You already showed that
Setting a default value before knowing what you need is a performance hit for no reason.
@@godDIEmanLIVE Worry about having maintainable code before worrying about optimization. Outside of old school graphics coding or HPC very little of your code needs optimization more than it needs to be usable (and re-usable) for the long term.
Once you find a bottleneck then you can work on optimizing it. But as my algorithms prof. said, the coding needing optimization probably wont be the section of code you thought it would be.
And these days the compiler/runtime is better at optimizing your code than you are, so ensure your logic if formatted the same way the optimizer is expecting *cough*OraclePL/SQL*cough*
Edit: Now I'm not saying "do not optimize", apply standard optimizations, don't walk a sorted array looking for a value when a binary sort approach would work better. Don't implement what should be a batch processing job as an OLTP process. Follow common sense. But that is part of writing code that makes sense.
@@danlandia4399 My problem with this attitude as such is that this is the reason we have shitty and slow software. You just get used to premature pessimization for little reason and think that's normal and okay to do, because it makes your code "more readable" by 2% or what. Fundamentally, our job is not to write software that pleases us or is easy to read or follows some ridiculous paradigm for no technical reason. Those are secondary concerns. First order of business is to write fast and efficient software.
The default thinking should be "how do I write fast and efficient software", not "what can I get away with, because it doesn't matter". Performance ALWAYS matters. Then and if there is a substantial reason to write some a bit less efficient code, it might be okay to write something more visually pleasing or lazy etc.
Otherwise we're just not doing our jobs and we're making a joke out of software engineering. What other profession is as careless with their craft?
Imagine designing a combustion engine and trying to convince your boss, that the pessimization of 10% in terms of power efficiency is okay, because it was too hard to design something more efficient or building it like that makes it look pretty.
And there is plenty of software out there that is 100s of times slower than it should technically be. At that point, we truly are a joke profession.
Long rant and I don't really disagree with some of your sentiments, but I am of the conviction, that what I wrote above should be the default way of thinking about what we do, so we can be proud of our craftsmanship. If then we say, okay here it literally doesn't matter what we write, because it doesn't affect anything really or just pessimizes performance by a tiny bit and it has other significant advantages to do it this or that way. Okay, nothing against that.
P.S the thing about the compiler is very dangerous and most of the time it's wrong imo. Often bad performance is a problem of data locality and memory and cache access. You can easily pessimize your performance by fragmenting your data by a factor of 100s and your compiler can do zero to help you there. Same with choosing correct algorithms for the task at hand etc. etc. That is 100% your job.
I'm brand new, and thanks for posting this.
Over the years I've found "clean code" is very subjective because a lot of people have differing opinions about what they consider clean. However a sensible rule of thumb is to avoid a function having too many branches. At some point it stops being readable if you have to scroll to see what the function does, but having too deep of a stack trace isn't a good thing either if you can't even name them apart.
You should definitely avoid a function having too many branches. You should also avoid having too many functions whose only reason for existing is implementation detail. You should also avoid having a piece of self-contained logic being split into too many different locations (e.g. functions, classes, files, or whatever).
Too many of anything is to be avoided. That is what "too many" means.
I "clean code" perl… and trust me, if you don't speak Perl, you don't read my work. "clean code" does not exist. Python was made for "clean code" and $°°° but it's a bloody nightmare to read… And If you work with binary cond else is half the work.
@@pierretonnelier9994 I was a perl enthusiast too, one of my favorite algorithm involved a recursive function. It was compact and very clean in my opinion, but the thing is people around me just had a hard time wrapping their head around it, anytime I would write code in a functional way it was hard for them too and you could see by the way they wrote code that they just had an easier time with imperative procedural style. The lesson I learned from that was that you don't write code for yourself and sometimes something less compact with just simple logic is better. Language is just syntax, that's not really the most important thing, even though sometimes they canake things rather unpredictable (looking at you, JavaScript...)
@@pierretonnelier9994 I don't see why \t\t\t you think python code is a bloody nightmare.
Yeah abstracting the body of a function when all you've done so far is write a guard clause is way to premature.
The version shown at 6:35 is the most readable IMO and the one I would write. I find the final solution way less easy to read because of the unnecessary second function I have to jump to and from. In this particulier case, the if/else if/else statement represents the exact logic, that's what makes this form so effective and straight forward, while the additional function method requires a bit of mental gymnastic to sort out
And function calls are SLOW. You are very correct that second function is unnecessary. In fact, copying its contents back to the prior function is simple, adjust for person.age. That is the simplest and most readable IMO.
Coding is all about readability to the developer and the maintainers to come. I use the rule "keep it very simple, the same constructs used repeatedly. Definitely NOT every construct the language offers using a different one each time." Find a simple one you can reliably work with and use the hell out of it. That gives the greatest reliability and best maintainability, as new developers on the project (or you in 6 months when you've fully forgotten what you did there) don't have to learn and make allowances many and apply different thinking to each.
I use the rule "keep it so simple I can understand and work with it when I have a full blown head cold or flu and should be home in bed". Because that's the day you'll have to fix it, expand it, rewrite it. Or someone else will.
There is a difference between readability and something just being what you personally are used to seeing. Something familiar isn't objectively more readable, even though you can easily read it.
When someone says, "This is more readable IMO", they're using the word "readable" wrong. Readability that is opinioned is just familiarity.
Not exactly. Sore, in this context it seems unnecessary: all four cases log a string, why would we have three of them in one code piece and others in the other? There is a reason, although in this case I think Kyle chose a poor example. It does make sense to have a separate validation function for a separate valid business logic concept. Is age valid for drinking is a matter of domain logic. Is the person object, if valid, of drinking age, may be a subject of some, say, individual controller which is not sure which input it got.
The problem with this idea though is that the function returns a string which does not seem to make sense within it. But that could be just a poor example on Kyle part.
I think the idea he presents is quite valid, just the example he uses is not the best, and he may've mixed up different ideas here thinking it was one:
- using guard clauses improves readability (mostly, yes)
- single return per function decreases readability (often yes with a notable exception of non-interactive debugging like through logging)
- separating domain logic from data validation is a good idea (yes for more complex cases)
That because you and most of this comment section are terrible coders
yeah, like the first function IS the simplified function. He could've had the sole function use the exact same return functionality he used later, and just use `console.log(CanDrinkBetter(p))` for the initialisation.
Moral of the story: Go home early whenever possible!
I agree
i love this!
👏👏
Stay home*
is this applicable for WFH
Exit early or "else" 😉
Exactly - this is the same, just written differently. You still have to read all previous conditions when adding new one later in the code. Doesn't really solve anything. One good advice from this video is to use additional function to handle some cases. But still... this naming... it suggests boolean not THAT...
imma hit you with tha gunny
sunny
Yeah, the se isn't the problem. The problem is not has good design decisions
Meh it doesn't make sense, what if you cant exit early because you need to execute some parts outside the if? Idk about js, it is very necessary in c# at least
@@cristianjuarez1086 donot come cryin to me about your issus.
i use tweny thosand dollar bills as my tissues
I don't think avoiding else completely is a good idea. It can help your function if you use early returns to get rid of edge cases, but if you have a more complex check, or you are doing calculations instead of just returning on each leaf of the binary tree then this method will probably not work. Also you shouldn't really turn one working function into several just for the aesthetics, it's not going to make it more readable if you constantly have to jump between 3-4 functions that call each other. In some cases it can work, but you shouldn't take either of these as general rules, more like neat tricks you can do in specific cases
Yea but in the end he says this is more of an exercise to help you and else statements may still be a better option
if you use an else for a return for edge case you can just flip the if check to be inverted and achieve the same thing. if it's a specific else if, that's different, but still there are ways around it that use less code but maybe is less straightforward to read.
Just wanna add that if/else (branching) can be converted to binary logic and caching the results into booleans (howto-details on request). Timely I prefer this, aware it's merely another way of writing if/else. I use it for readability, and for reducing code path complexity in larger projects, which can be beneficial for testing and debugging (referring to code quality metrics). Else-If cascades I completely avoid. For conditional value assignment, I favor the ternary operator where possible (sign = value < 0 ? -1 : 1). Sometimes I use if/else for code flow control, while preferring the alternative: if (condition == true) call A, if (condition == false) call B. I know it looks a bit odd).
Not using else on usual day-to-day tasks: a good exercise to help you understand and actually improve your flow control skills as a developer.
Not using else when actually needed just because someone said it's better: stupidity.
The else keyword do not exist to endorse lazy development though. It has a purpose and it can and should be used as long as it fits that purpose.
I generally only abstract code into functions if I will or think I will use it more than once
Personally I think replacing 'if/else' with 'if return/if return', hurts readability more.
You still have to check earlier in the code if every previous 'if' block has a 'return', in case the next block will not be executed. With 'if/else' you know straightaway that it's one or the other, not maybe this one if the previous one(s) didn't exit.
In some cases it might help (it's more about the logic), but it is definitely not a 'good rule'.
the main problem with his initial code was the nested If/Else, not use of Else. The nested and deeply nested If/Else in a single method is what hurt readability and maintainability than everything else
I think that with the push to using functional programming that this style works better than if-else blocks. As Kyle proves in this video, there's less code to work with, and therefore less possibility of mistakes. If we can truly get away from relying on variables to control inter-code responses than whatever we write will be more reliable. Avoiding the 'else' statement takes us a little closer to that goal.
Agreed.. Early exit/fast fail is a no brainer, and factoring in general is great. But I'm really skeptical that converting the range mapping into a series of guard clauses creates any meaningful improvement in readability or reliability. If/else chaining is a well established pattern for this sort of thing and our brains are wired to quickly comprehend things we've seen before. As another TH-camr said - what's better than ideal? Standardized.
@@emissarygw2264 yea, if i had to work with kyle and he did that, id get out immediately, because whilst it might work for him, using the traditional style that literally everyone else uses is better, since everyone understands it immediately, and doesnt have to worry about the code being exited too early.
Not only that, he is spreading small amounts of code into functions, which means to read his code, youre gonna have to start at the top, then jump to the next function and so on and so forth, instead of just reading it as it is.
When trying to get a job, it doesnt matter how much better you think your method is, if everyone else doesnt like it, you aint getting that job.
I made some examples using a condensed version of his thing (just using guard clauses without the bulky functions), and then how you could do the same thing using the if / else method, with the code being shorter, and still on one line.
// using guard clauses
const a = { age: 17 };
function canDrink(person) {
if (person?.age == null) return console.log('You are not a person');
if (person.age >= 21) return console.log('Yes 🍻');
if (person.age >= 18) return console.log('Not in the US 😦');
return console.log('Nope 👶');
}
canDrink(a);
// What i would use
const b = { age: 17 };
function canDrinkBetter(person) {
if (person?.age == null) console.log('You are not a person');
else if (person.age >= 21) console.log('Yes 🍻');
else if (person.age >= 18) console.log('Not in the US 😦');
else console.log('Nope 👶');
}
canDrinkBetter(b);
I'd say it does improve things. You're converting the negative conditions to positive ones, which is innately easier to think about. You're also pulling nested conditionals into descriptively named functions to help compartmentalize the thought process.
With one exception: do not turn the code into a maze of returns, keep them as close to the beginning of the function as possible, or at least make them visible inside your code.
it become a maze if it only have one return, just like a maze in general it only have one exit.
if you have early returns you don't have to read all of the code just to understand one path of the logic.
one early return in line 3 will reduce the mental burden of reading line 4 to line 10, because you know that one path of logic already ends in line 3 and is not going to be touched in line 4 to line whatever because of return statement in line 3.
I just realized how many TH-cam videos teach “bad” practices with confidence. Maybe I should start doing this to make some money
are you saying this technique is bad, or are you saying other videos are bad? If so, why?
But hey, there are 25k likes, this cannot be anything else than 100% legit!
Right? This dude should be ashamed to think people are WATCHING this. I thought it was an April Fool's joke at first...
you have to teach about the else statement
yes could be optimized but hey shouldn't scare newbies with advanced technics
@@RoastLambShanks 1, you shouldn't make a function expecting the function calling it to have already done a null check. A switch statement would been the best way to do this.
I never had a problem reading code due to usage of "else". On the other hand I did encounter difficulties due to early returns and unneeded nested function calls which simply break the locality of logic.
I'm not in favor of spaghetti code. Just use commonsense when writing code and remember that there is nothing inherently wrong with "else". Don't waste your efforts as an anti "else" fanatic.
This. Different situations, and thus different requirements, require different implementations and different solutions. This video is superfluous and completely unnecessary. I would argue it would lead to more messy code following this videos advice. The concept he's loosely touching on is actually short-circuiting, which is a beneficial technique for improving algorithm efficiency. "Guard clause", in my opinion, is a terrible name for a short circuiting if
I've had issues due to overuse of else, but only in programming languages that offered no viable alternative. When if/then/else is your only flow control, anything slightly complex becomes a nightmare.
Normally I'd just advise avoiding such languages, but sadly it's not unusual when dealing with industrial control software (looking at you, Wonderware) that BASIC dialects are still the norm. Supposedly, they're easier for someone unfamiliar with programming to debug, but honestly if you have someone unfamiliar with programming tinkering around with heavy machinery controls, you should be sued into oblivion. This stuff can kill people.
The main issue with this video is that ... JS supports switches which in this case wouldve been the correct thing to use to simplify it not just visually but also in code so it only has to be evaluated once.
@@unknownalien3837 yeah, "guard clause" is a fine name for a clause that guards against invalid input. But this guy also uses the term incorrectly for simple short circuit returns that are not really guarding against any unexpected/invalid input.
@@Disatiere JS does not support switch statements.
Seems to me, the 'else's were the least of the problems in your code. It was the functional decomposition and the switch from '!= null' to '== null' that caused the real improvement.
Prechecks are great. But the rest... O_O
I was sitting here sweating when all of those console.log calls still existed halfway through the video. Thankfully he dediced to create a string function rather than a void function for the actual logic towards the end
@@holonaut tbf the usage there was mostly just for demonstrative purposes. yeah, could easily have been heavily simplified with only 1 console log, but, generally, when you do need to print that much stuff to console, it's probably mostly for debugging and you're going to remove them anyway :D
I would be vary about this “no else” approach. You are introducing implicit conditional structure into your code and while it might make sense if like in this case it is one liners, then I have seen plenty of cases where if statements are long and not knowing the if statements must come in that order is just bugs waiting to happen.
Guard cases on the other hand are great!
This! I would go one step further and adjust the if statements so the order doesn't matter at all. Yes this adds overhead but it will make sure that somebody who messes with your code will not run into issues without fully understanding what that code does.
well, with "else" ordering is also critical. The only way to ensure ordering does not matter is to expand every single condition, like "if (person.age < 21)" into "if (person.age < 21 && person.age >= 18)". But that's bad to maintainability too, do you agree?
@@yevhenkozlov286 Absolutely! The ordering matters in an "if", "elseif", "else" block too - But on refactoring I would tend to keep the order there more likely compared to a bunch of single if-statements. By design in such a block only one branch will be executed - I dont need to worry about the content of the branch itself. As perlohmann mentioned above its quite easy to read if its one liners.
Yeah, this looks like a lot of bugs waiting to happen, for whenever the people edit your code. Not a big fan of this
Exactly my thoughts. Besides, it applies pretty good on JS Web Programming. On System Porgramming or other areas, I dont think it's a good idea, beyond the Guard cases.
Whenever you're working with a single variable, try and use a switch statement instead. Something like this:
```
switch (true) {
case age < 18: return "Nope";
case age < 21: return "Not in the US";
default: return "Yes";
}
```
The beautiful thing about a switch statement is that, you can add one more condition, pretty easily without changing much.
Default still works like an else this not solves the problem. The point is that whatever you wanna use for if a decision has to be taken or not you dont wanna be in the condition that else or default accepts unexpected results like null or undefined so you need a guard as explained in the video! Have a good day ;)
@@Nunzio.o I get your point. But you can always have an if statement for checking whether the variable is `undefined || null` before getting into the switch.
OR, better yet, you can literally have an `if` statement inside the `default` case.
What I showed was just an example of how to use a `switch-case` statement for single variable conditions, rather than an `if-else` ladder.
Hence, that's the beauty of a `switch-case` 🙂
Multiple returns in code are a really bad idea. It is why they are excluded for safety critical system in MISRA and IEC 61508 (Functional Safety Standards). These standards have grown as essentially a list of lessons learned from a variety of safety critical development projects over decades.
You could still do this :
let result = ""
switch (true) {
case age < 18: result = "Nope";
case age < 21: result = "Not in the US";
default: result "Yes";
}
return result;
This solves the non-multiple return issue
switch statements are UGLY imo. You introduce more nested brackets which doesn't improve the readability.
In that is the case, make a static list or hash with a function pointer! That's more clean.
Using console.log is in my opinion a special case that’s not really useful outside of demonstrations.
I would have changed the function so that instead of canDrink (which btw sounds like it returns a Boolean), I would’ve named the function something like getDrinkingMessage and have it return the string. This way, you would not have to have an additional function and in your main program flow, you would console.log the result from that function instead. And because you return the string instead of using it inside the function, you also improve the reusability of your code.
+1 That's what I thought too!
Good point, that would improve reusability, and testability too.
isn't it what he did in the last exemple ?, btw i don't know javascript, but i guess Function mean you can return what you want?
for the name, i totally agree he should either find a different name or add something like CanDrinkStrg
It should be common knowledge that functions beginning with "can" or "is" *should* return a boolean. Such rules make code easier to read, especially in untyped languages like JavaScript.
@@gizel4376 no. He just split function in two for nothing, he could just replace all ifs with function, that he extracted and console.log(canDrink()) outside of function
The downside of early returning / continuing / breaking is that you can't tell by the IF's condition itself whether your code below it is going to trigger or not. You have to inspect the entire IF block for return / continue /break statements to find out if your code below is in a "virtual" else block or not.
I personally believe else increases readability by group items tougher.
And for cases more than 4, you should use a switch statement anyway
Yes, exactly what I think. But I think these videos provide good beginner tips, in general
switch(true)
The switch statement only works if you're checking specific values from only one variable.
One situation where you could easily have several chained if else statements is when doing validation with clear error messaging
Agree. Even in this case, i find it harder to see where the group of checking the age starts and ends with all the returns. With the if and a few else statements, it is way clearer to see what belongs together.
i used to think just like you... but i'm reading "clean code", by R. Martin, and it seems that using nasty if/else statements, or either switch/case, you would be violating the open-close principle. After using this technique i could significantly increase the quality of my code, also making it more readable... check this video from Kyle and then tell me your impressions: th-cam.com/video/-ptMtJAdj40/w-d-xo.html
it's a matter of the paradigm you are working with. The way you programmed is strongly align with functional programming, because you are centering yourself in within conditions (functional programming). And avoiding the need to keep track of a variable (state). That's why the code looks cleaner and more abstract. It's more cohesive. In the other hand using if/else and creating a variable (state) to then modify it. Makes more allusion to Object oriented programming. It's a matter of how you are centering yourself. I strongly prefer to encourage the way you code because functional programming makes allusion to recursion and it encourages people to create better abstractions. The answer to whatever you need to use If or else is mostly redundant. Both work, it's a matter of preference.
It works only when writer itself understands multiple paradigms and still be able to make choices depending on project standards. Unfortunately most programmers are unaware of it till significant amount of time until they realize this with other codebase, best practices and from their own experience.
In my experience it's always about sticking to common tongue that other developer's speak when it's a teamwork and always a personal choice when going solo.
At the end of the day all loops, conditions and switches are converted to jumps and jump with conditions in either real cpu or virtual machine, those who are fimilar with assembly can easily relate. This essentially abstracts english mnemonics that we use to write executable code.
I don't think guard clauses is a "functional vs OOP" situation. Guard clauses are just a generic way you can safe guard your arguments or safeguard the value of some variable/state according to some conditions. Which is equally applicable to both OOP and functional paradigms.
But yeah, it's a matter of preference. Guard clauses are great at cutting down needless nesting which is vastly superior in terms of readability/eligibility. Can't really say there's anything superior about nesting ifs and elses together. I would go as far to say it isn't really a matter of preference, as one is just sloppy. At least if you're using the example given in the video.
@sdsdsds dsdsdsdsds not necessarily. Depends on if in your recursion you are calculating the same thing multiple times, in which case you should use something like memoization to prevent this. A good example of this is Fibonacci with recursion. Fib(n) = Fib(n-1) + Fib(n-2). You are essentially calculating the same (n) multiple times so you should store the result in some kind of shared cache.
@sdsdsds dsdsdsdsds You're welcome :D
if else statements have nothing to do with object oriented programming. And neither does using a variable to keep track of stuff. Unless you see an object in what you just mentioned?
The problem with programming teachers is often they teach hard rules without explaining the intent behind the rules. So it just becomes blind adherence rather than something followed conditionally. A good example is the "one return per function". There is a good reason for this, sometimes. Other times, what he does with multiple returns is fine. When people understand the intent behind the "rules", then they know when they should be followed, and when they can be broken.
I’ve had some programming teachers show rules without explanation and some with explanation. What I’ve noticed that is the more advanced the class, the more in depth the explanation is. With your one return analogy, an introductory class might skip over the explanation to cover more content while a more advanced class will go into detail about unreachable code.
It can be a problem with bad teachers. But it's also a problem with bad students. If they don't know the reason for the rule, they just assume that there isn't any reason for the rule.
Another good examples is "stop using else". Hard rules are never good
The two absolutely hard rules of code formatting:
1. There are no absolutely hard rules
2. No goto
Agreed - the MISRA rules explain all reasons why things should be adopted. These are not theoretical - they are effectively a case study of lesseons learnty over decades of issues that caught people out in real world sceenarios.
I really appreciate all the comments on this video. It is a huge help to me as a beginner to hear everyone's different opinions. You all rock! 🤘
Here's another perception. The conditional is already done in the ( age < 21), the less than, equal to, greater than operators are already conditional checks, adding in the "if" statement is an extra conditional check.
The boolean operator will already evaulate to a 0 or 1, which can be used to multiply with the string.
If you multiply the string by 0, it will be "" blank, if you multiply by 1, it will be the string once.
Console.Log("No" * (age < 18) + "Maybe" * (age >= 18 && age < 21) + "Yes" * (age >= 21));
Now you completely removed the extra comparisons. That's branchless programming.
Note, branchless programming is not always faster, but it's worth being aware. th-cam.com/video/bVJ-mWWL7cE/w-d-xo.html
Just remember the 'web dev' is a new concept and we all USED to actually work on the actual computer without as many abstractions.
But when the logic gets more complicated you could end up with tons and tons of small functions which don't really improve code readability. I think the best option is not to avoid the else keyword but rather use common sense - if you feel like your code is getting harder to maintain, then refactor it. It's the same case as code optimization: think about it only when it starts to matter.
Yeah, this is only useful for use cases like the example shown when a function has a single primary purpose, not a function that is meant to tie a variety of other functions together into a workable program.
@@tomg0 True, but if a function doesn't have a basic primary purpose then your function is doing too much.
@@Martin-pb7ts I would disagree. There are cases when breaking down a function into smaller functions provides no benefit, and putting the majority of the code in one function would be better. Functions are useful and necessary to help reduce duplication of code, but they are not necessary to group it into parts that can easily be distinguished. And then there’s the performance overhead of function calls, minimal, but it exists, and should not be neglected.
@@noahsmith2555 I would disagree based purely on what is the intention of many small functions. If it allows you to have a more descriptive set of tasks based purely on the function names and allows better unit testing then its worthwhile. Functions aren't just useful for reducing duplication but should be used if it helps with communicating your ideas to others using that codebase, I would personally always go for readability if that is the intention of many small functions that a developer has chosen
@@dennisgatere7821 I think youve missed the point of the argument, which was that using a lot of small functions wasnt outright bad, but in certain situations it is. Most of the time using small functions is good, however, there are some specific situations where you wouldnt want to for various reasons. Maybe theres also confusion around everyones definition of a "primary function". What is classified as a primary function? Thats a rather subjective term. There is a point at which increased abstraction comes at a cost in terms of readability, and of course, performance. Also, regarding your view on readability, while using many small functions can certainly help make your code more readable to others, theres no reason that the same cannot be done if few functions are used (Comments exist for a reason).
Maybe I'm actually missing something, but this seems like a lot of work that could be simplified by just using a switch statement.
I literally can't believe I watched this through. No wonder there are so many comments on this video, i'm not going to re-invent the wheel for a single value checker when we have the switch statement... it will in fact make it even more complicated to read. At least when you see a switch, you can immediately see the intent.
@@ryanswatson nah, switch can check for exact vaues but it cannot check if something is greater or smaller than that value (well, it might depends on actual language used)
The Rust compiler turns if .. else statement into a match (switch) anyway.
@@Whimfoome most compilers do. It cuts down on number of ops executed on the metal.
Corry, I agree if the language is not JavaScript. In JavaScript, I’ve seen many people avoid switches because they are implemented to automatically continue from one case clause to the next and must be manually stopped with the “break” keyword. In general, not a huge problem but you always have to remember to break, which is just one more thing to worry about. As such most people would only use switch in JavaScript when returning a value because that automatically breaks out of the switch without running the results of the following case clause.
That would not work in his example because JS switches only check equality, not greater than or less than. Now if we were working in something like Elixir, we could use “cond do” and call it a day :D
It looks like my code before I learned about else :D
It was mentioned already, but this approach may cause bugs and loose of logic. You also need to follow exact order of your if statements and may need to repeat same criteria for more complicated code.
Also, it may be small difference, but in my understanding else allows to make code quicker. Every time you call IF, program need to check whether it is true or false. Else does not do so, because it already know the answer. Because you use more IFs and more likely repeat same IF conditions, your code slightly less efficient.
Yeah, it kind of bothers me because there is more to code than being clever. The compiler will optimize things based on best practice and often times clever code is actually significantly worse. I used to do this kind of thing all the time before I got into compiler architecture. Turns out best practices are set by the people who designed the language (and compiler 99/100 times) and not usually by other clever people. Who’d-a thunk? xP
That being said, creating unnecessary branches is not great and something to avoid. I would prefer this video be “You use else too much” rather than “You shouldn’t use else ever.” Guard clauses can be better for many cases.
I write code mainly for slow microcontrollers so I will probably continue to use else.
He doesn't use any more if statements though.
Yes because else is unconditional, it does not redirect the flow of the program. I don't know if you've ever tried writing simple code in assembler but you can see how these conditional jumps translate into several operations.
On a small scale program it often is not noticeable, segments of code that aren't optimal won't be observable but when dealing with a large set of data all these checks and jumps can drastically affect the speed of execution.
I don't see how there's any advantage to getting rid of else condition unless you have such a problem that literally requires you to specify all possible outcomes. But more than that, you should worry more about nested loops
I think ur point about else being efficient isnt applicable to this example.
It's the if that comes with cost. What you do following it, whether that be standard else or not, has the same cost.
So for example "else" is coupled in the cost of the original if. But if you do "else if" your adding another if
So if ur if will return or run the next block its the same computation whether u follow that with else or not
Tagent but that'd what at low level ur aiming to remove every "if" in gles I often had to figure out something that would remove the if by way of maths for example you might try and replace ur if with something that would nullify that part of the equation (e.g multiple the expression by your boolean)
There is also an interesting quirk/trick I've seen in c++ with empty if blocks and boolean functions that I'll have to dig up lol but I wouldn't recommend it lol
For maintainable code, I would recommend throwing in the guard clause for things like invalid objects.
Instead of separating into two functions just return the string and do the printing in the main. Other than debug messages, I prefer only the main loop to do any IO.
function canDrink(person) {
if (person?.age == NULL) throw 'Age is required member of person.'
if (person.age < 18) return 'Nope!'
if (person.age < 21) return 'Not in the US!'
return 'Yes!'
}
const patron = {
age: 29,
}
const drinkingToday = canDrink(patron)
console.log(drinkingToday)
Just learned guard clauses have a name. I'm 100% self taught and I started doing it on my own because it felt right.
Same!
Same!
Only in my case, I thought it's unnecessary nesting. If it's not that long though, I just use multiple ternaries in one line.
good luck to all of you guys! :)
I agree with this. I did not know what a guard clause... I think these are also called validators
Hey hey, same! And then when two functions in two separate files need to use the same smaller function, you can move it to a global 'utility' file. I discovered that you can return not just strings, but also the whole fetch, so you can append to your own functions. That just blew my mind 🤯
The problem with multiple return statements is that it's easy to forget some that are in the middle of a function and not realise some part of the code doesn't get executed when you modify the code later. it's a source of bugs that can be easily avoided, that's why they teach not to do it. Personally I do use early returns a lot in guard clauses exactly like you, defensive programming where you check your assumptions first is really good form. But past that I do avoid multiple return statement, caching the "result" in a variable is way more sensible, as you can add stuff later on to the end of the function, and you're sure it's gonna be reached in all cases.
Personally, I think that's just as prone to bugs, since you have to make sure you're not affecting the result between your "stop" point and the return. I'm not saying it will never be useful, but I do like the multiple return because it means I can safely ignore the rest of the function from the point of exit onwards without worrying about how I handle the variable later on.
I hear you. But if you stick to Robert Martin's rules for function length, multiple return statements are easy to spot.
"The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that. Functions should not be X lines long."
Different developers use different values of X. I find that keeping my functions under 20 lines makes them easier to understand and test.
@@MartinOmander I dislike these kind of rules, some functions are long because they do a linear process that logically belong together. cutting them in pieces just for an arbitrary length rule adds a lot of overhead and is extremely annoying to read through. plus it's slow to code, especially when you have to change parameters. that being said, I obviously avoid multi pages functions, but they end up being cut up according to my refactoring needs, not according to some arbitrary layout rule.
@@feandil666 Agreed, how we organize our code should be driven by our refactoring needs, not some rule.
Whenever I've written a function, I check its length. If it's over 20 lines, I take it as a signal to think about whether it should be split up. About 80% of the time, splitting it up makes the function easier to understand and test.
Maybe this "rule" should instead be called a "reminder to think".
@@MartinOmander I've kinda been thinking about this. Say you've got a struct that holds some data as well as two functions, one for generating a string out of that data (for saving to disk, sending over a network, printing to console, etc.) and another one that parses such a string to get the original data. In my experience, the parsing function can become fairly large, especially if the structs holds large amounts of data. For every field you need to get the corresponding string token, return early if it is null, then optionally convert it into an integer or float, then perhaps return early if the data doesn't make sense (input sanitization can be kinda important), and then apply it to the struct. You can hardly shorten it, and you also cannot easily break that up into multiple functions either. Any thoughts about this?
Developers should always be cautious of other developers telling them NOT to use aspects of a programming language. There are developers out there who say OOP is bad and that you should only use functional programming (OOP is good). if else is probably the first keywords in programming, macros are literally if else statements. All if else does is to determine what you give it, is true or false, if you have bugs that means you haven’t properly tested your code (and that’s ok. Don’t blame it on if else). And if your having trouble reading if else statements then your going to have problems reading code period. Like one commentor stated previously this is more of a style thing NOT a proper way of coding.
True.... Unless you're doing things like
if (x)
If (y)
console.log("x & y")
else
console.log("x")
else
console.log("y")
.... Never do this.
@@SrIgort but you are bot checking for one thing. You're checking for multiple.
There might be a
If(x) then resultx = a
Else resultx = A
If(y) then resulty = b
Else resulty = B
Return (resultx & resulty)
That is checking for two seperate thins. But still using the else keyword.
And that is not what is happening here.
Develpers should be cautious of developers who categorically say ”X is good”, as in meaning ”X is always good”. OOP has its uses, but there are many instances when you are much better of figuring out a functional solution to a problem. I can recommend this video which shows what is sometimes within reach if you consider the functional approach: th-cam.com/video/vK1DazRK_a0/w-d-xo.html
We're talking about JavaScript, a language originally designed for people who don't know what they're doing by people who didn't know what they were doing, some of whom even intended for it to fail and be replaced by VBScript.
^ this.
Years ago in C, if there is a lot of if/else then I would use the SWITCH statement, I found that helped make it clearer. Thanks for the reminder and advice to avoid using a lot of if/else in code.
Except switch doesn't work with comparisons, it needs discrete values as labels. :-B
Early returns can be good (as long as you've taken care of any co-concerns like events or logging and tracing). However I think the gymastics you need to do to get rid of "else" are usually not worth it. Most basic language constructs are avoidable if you're willing to put in the time and effort in doing something weird. Is it worth it? Sometimes but not usually.
Depending on the type of project you're working on, but usually not, it's more about flexing it seems.
I've written code that could be described as clever but often times it's not necessarily better, it just looks out of the box
Early returns are always good, why execute code needlessly?
@@joschmo4497 Eh, the CPU is actually executing exactly the same number of instructions in both the first example and the early return example. Either way after setting up the registers it does a compare, a conditional jump, sets a string pointer, calls another function, then returns unconditionally. Just in the first case the conditional jump fails so it falls through to the instruction in the next byte which is the start of the else block, in the second the conditional jump passes the instruction pointer gets overwritten and it executes inside the if block instead. The CPU really doesn't care one way or the other between these two it only starts getting extra workload once you start making parts of it into a new function call, a function call results in at least one instruction to put the current value of the instruction pointer to the stack, one jump to the functions address in memory, one instruction to pop the return address from the stack, one final jump to return. Obviously any additional parameters will require further instructions since the pointers to those values must each be put on to and then pop'ed from the stack also. So unnecessarily breaking down simple functions into very small parts is not particularly efficient.
@@seraphina985 if you want to optimize code, you need to take valid benchmarks before and after. As you say, the "better" version takes more steps to execute, and is otherwise just "different" not "better" as the same readability drawbacks from chained if/else statements exist in switch statements and in his consecutive short circuit returns.
they're very useful in C, especially in embedded programming.
As C doesn't have exceptions, you often return an error type that you define as an enum - something like OK and ERROR.
If you were to save this to a variable, you're wasting cycles that can be very scarce in MCU - say, you're recording values every 10us on a system with 150MHz (or you're doing something in hard real time). Then you only have 1500 cycles to do your thing. Declaring then saving to a variable and recalling from a variable is already a few cycles - let's say around 10 for a low estimate. That's 'only' ~0.7% of your available cycles, but hey, those cycles could be used for other things.
I still think that's a big problem with modern programming - most non-embedded things are so inefficiently coded that it doesn't even make sense to efficiently code. Write python? Unless you're using a C++ library such as numpy, just fucking do something - it doesn't matter anyways. Need something time critical implemented? implement it in C++. Javascript? Lol, don't even get me started.
Most 'software developers' are just slinging libraries around to quickly accomplish their task (inefficiently), because we have the processing power necessary for it.
@@iFireender now you're arbitrarily defining efficient. The company pays the programmer to do work. Does it make sense to pay more for something that produces the same results a few milliseconds faster for a normal human workflow? That all depends on how the program will be used. Will it run billions of times per second, or only a few times per minute? Will it run on a constrained IoT device or on a server? Will it need to run on a user's click and respond immediately, or on a schedule where it can take 5 minutes and nobody will notice?
JavaScript has its place as well. If you need a web app, or you are in a startup mentality of get it out now and fix it later, then it might make sense. If you want a backend service that's solid, executes fast, and will be maintained for a few years, then it probably doesn't make sense.
Generally speaking: yes. extracting complicated code in its own function is a VERY goot idea.
But:
MOVING RETURN ON THE SAME LINE AS THE IF DOES NOT REMOVE NESTING.
this merely HIDES the nesting. Also you had 2 levels of nesting, now you have 1 level of nesting (because of the initial guard clause) so, concerning nesting, you did not really change that much.
I think you missed the point.
@@dametocosita4994 he was simply making a point on the video's first point
It also didn't improve refactorability at all. As someone who actually teaches people to code, I am... rather concerned that his threshold for a function being "too big and complicated" is "it contains an else".
> *extracting complicated code in its own function is a VERY goot idea*
Functions are created to prevent duplicate code (a code block, algorithm, etc) and not for cleaning up code in one place.
If the created function is only called/used in one place, then it shouldn't have been created.
@@DejitaruJin it's an example function that is made simple for viewer comprehension. I think the meaning of the video is to build cleaner coding practices and to think about possible solutions with a different perspective.
Dropping else-statements in favor of return statements, is pretty bad from the POV of computer science since it removes the ability of code checkers to prove that your code is valid. Mathematically, you can no longer prove the code always does what the requirements tell it to do. And this is my main issue with this approach. Readability can be solved by refactoring into more functions.
If you don't understand nesting and the correspondence between provable code and correct code, the solution is not to throw out the else, but to get better at programming.
Huh? The transformation between the two styles is pretty mechanical.. Why would a checker choke on one style, but not the other?
This is a fairly common misunderstanding. Yes, static analysis and code provability uses a single return point to make certain kinds of analysis tractable. However, regardless of how many return statements you use, every function only has a single return point. This guarantee comes from structured programming and isn't negated by code style choices.
Consider that the control flow from any function call returns _to_ a single point, i.e. it returns _to_ the point from which the function was originally called. This is the single return point, not any of the zero or more return statements within the function. Even in the case of exception unwinding, control flow still passes as-if through that same single return point.
Given that you can rearrange any code that uses early return statements to not use them, it follows that for any early-returning code with a flow graph G that there exists a homeomorphic / equivalent non-early-returning form of the code with a flow graph G'. Modern static analysis tools already handle such code without issue.
"Readability can be solved by refactoring into more functions." This, seeing the code in the video I was just thinking "wouldn't you just do function canDrink(person, country) { return person.age >= getLegalDrinkingAge(country); }" so that your code actually describes "to drink you need to be above the legal drinking age" instead of putting too much of the solution into one function?
I do prefer guard clauses. When used to eliminate nesting levels on large if statements I think they increases readability in most cases. But I also think every situation should be evaluated on its own, rather than just arbitrarily restructuring every program to remove else. There are many usage cases where using else is cleaner and more efficient, particularly when there is more code that follows and you can't just return.
I would advise *against* falling into JS-only development patterns (even when writing JS), as it makes it harder for multi-language developers to deal with your code.
Yeah. I wanna see him try to do this for VHDL now.
@Brandon Busby
If you don’t want to read;
Tl;dr, this is the only language that contains early returns and is super unstandardized which will fuck everyone over programming in this style
let’s take this for example in c/c++, the current language I specialize in
```c
if(d == "a")
{
printf("you printed the letter
");
}
printf("you didnt print the letter
");
```
Both will execute, this concept does not work in c/c++
Not to mention in a python style language where curly brackets are not used in, this is just painful to deal with.
You should never stray from the standardized language systems that everyone is accustomed to. Everything in this video is bullshitting you and is just plainly lacking of basic common sense. It does not affect performance either so why bother with this? It’ll make your code horrible and not even readable.
@@snesmocha Have you actually tried, or did you just assume you're correct cause you specialize? C and pretty much every C-like language allows early returns.
This literally works in C:
void someFunc (int a) {
if (a < 0){
printf("a < 0
");
return;
}
printf("a >= 0
");
}
I suppose you might want to avoid early returns when dealing with malloc when it comes to C specifically, and so maybe you would want to make that a standard your C projects. But that's an issue specific to C, and so in many languages this is totally ok.
Yeah, "return console.log()" is a fucking aberration. It hides the fact that console.log doesn't actually return anything, and that your intention is not even to return anything, just to save one line of code.
Trying to turn everything into one liners by taking stupid short cuts like this will make your code a fucking nightmare to deal with
As a person who's been doing this for 4-5 years at this point, I've learned that if something is better, but inconsistent with the existing code, then it's simply not better. Imnsho guard clauses are objectively better for most cases. But gl&hf teaching that to people who have been coding with nested stuff for years. And no single convention is worse than mixing 10 different ones. Exact same thing can be said for immutability.
true uniformity and consistency with the current code base is far more important. experience developers emphasize upon design patterns, but really, what they should give importance is not doing anti-patterns.
This has been my experience as well.
this is the worst part of javascript, you can do even the very simple logic in many ways, too many syntactic sugar, too many freedom, make it painful to read. same shit diferent code.
imagine a programing language so bad that people make another language (typescript) just to fix it
"the system sucks so I'm going to write worse code instead of trying to fix it". I really suggest you try to push code cleanup on your projects, it is an integral part of development IMO
Good luck convincing business people (who are the ones distributing the money at the end of the day) that you're going to spend a day/week/month to rewrite code that has already been tested and known to work, just so your if-s are better or you distribute it better. Then have it retested... Very unlikely.
Sees "why I don't use else when programming" Me: WTF
Sees "web dev" Me: ahhh makes sense
Omg I imagine assembly of such code in c
@@nov366 it's not impossible, but I personally don't fully agree with the "avoid else AT ALL COST" statement. It can be readable with "else" statements imo, but when coming to such deep nesting, if you really go deep down a rabbit hole then the idea is a good practice to avoid so much heavy indentation. (weird how rabbits have nests in this scenario)
@@giladshmueli5831 imo avoiding anything at all cost is a bad practice because in most cases you're usually dealing with symptoms and not the problem. I mean overcomplicated code frequently is a result of a) not reusing your code properly b) not splitting functionality properly c) 'type as I go' attitude without any planning whatsoever. 'Else' is just a cherry on top of it.
@@AntonNidhoggr precisely. If the keywords didn't have a good purpose, they probably wouldn't be in the language.
I'm on the same WTF group, lol
Using guard statements is a valid way to make you code less cumbersome to read because you removed one depth of nesting, that's a really good takeaway. Removing anything *else* form that code doesn't really give anything, in this case it's better to just stick to whatever convention you have in your codebase already and stick to that.
Agreed if you are working in an existing code base. Making new form changes can really become a burden later. In this case where an else clause can be reduced to a single line, these are improvements, so long as the second function's code is put back into the first--the second function is unnecessary and SLOW.
I've 40 years programming experience, really learned the single return point from Pascal programming in 1980. I didn't like multiple returns from functions especially long ones or complex ones. Before syntax colored editors finding where the code actually exited was a pain.
The trouble with multiple subs breaking down the functioning is that they become scattered in large files, chewing up time finding them, and across multiple modules, such as when refactoring or expanding the calling parameters and adding new associated logic. There is also a large performance hit to calling a subroutine. So the rule there is "keep your code simple, use functions to encompass a reasonably strong amount of code, generally not just a few lines, and so minimize the number of functions needed. And keep them close together as best you can. Seeing them all together is usually necessary when extending their functionality or refactoring."
I think there's definitely something to minimizing nesting (at least when it improves the control flow) but I'm not sure that creating a bunch of tiny functions for every little thing is the way to do it. It cleans up the main function body, sure, but the way I see it if you're reading the function body there's a decent chance that you want to look at the contents of all those helper functions you defined to keep the code "clean".
It's agony when you're trying to find a bug etc. and every function just calls a bunch of other tiny 3< line functions. Honestly I prefer a chunky function here and there to a fractured codebase that is impossible to read serially (not that the example you showed fits in this category but I think that's what you eventually end up with when you apply the principle of defining helper functions gratuitously anytime you do anything). It's a balancing act of course but I just thought I'd make an argument for the opposite POV since you didn't really touch on that.
100% agree. Big functions can be unreadable, sure, but so can big call stacks. Also, functions that only have one caller can give the false impression that the two parts are logically independent when in reality they are coupled.
Definitely agree in taking caution with over-breaking-down functions into other functions. I've seen this in extreme cases in the wild where every single part of every function was broken into more files and functions, including the helper functions themselves! It took hours to days to do small refactors and bug fixes.
This is in an API which I took the time to refactor one of the endpoints. Went from over 18 files and hundreds of lines of code and fragmented logic, to about 4 files that were maybe 100-150 lines of code total. Turns out the helper functions just weren't really helpful.
Doesn't every IDE have a 'go to declaration/usage' hotkey? I find it super easy to navigate the abstraction layers as long as all the helper functions have good names. If it really becomes a problem then just inline the function to work on it and re-extract the helpers when you get it working again.
To me, everything that has a much higher/different level in detail gets it's own function or code that repeats. I use methods to keep the specific subfunctions grouped to the main aim. works ok-ish.
@@graffhyrum The point is that it is off course situational. A big function is not automatically hard to read as long as the control flow is nice and obvious. It is also perfectly possible to make a function much harder to read if it is broken up into poorly though out helper functions, to the point of creating a labyrinth of function calls that only serve to obscure the actual flow of the program rather than just abstract away the gritty details.
When I was a noob I was overly zealous about never letting any function get too long, and ended up with a mess of hard to explain helper functions scattered all over the place. The solution was to just accept that sometimes long blocks of code are perfectly fine as they are.
I guess it is just what you prefer. For me, the "better" function is actually more complicated and less readable. *shrugs*
Also, lots of little functions are less speed (and often, space) efficient. Control transfers (jumps, branches, calls, etc) are often more costly to execute than a simple stream of instructions.
> Also, lots of little functions are less speed (and often, space) efficient. Control transfers (jumps, branches, calls, etc) are often more costly to execute than a simple stream of instructions.
That's... Debatable.
The speed decrease is so small that it literally doesn't matter
@@redstonerti9918 That's also debatable. Micro optimization has its place.
@@redstonerti9918 thats bs, in some cases every little bit is a bit to much
@@timnonik2736 Maybe if you are writing extremely optimized and high performance code for something specific, but for a web developer it definitely doesn't matter
I can't tell you the number of hours I've wasted before trying to make guard clauses work for hypercomplex situations when a nested branch would have been the simplest and most efficient use of my time.
Guard clauses can save you a lot of headache, but they can also create confusion in your code and waste a lot of time and just as importantly, space and duplicated code, under certain circumstances.
This is a god technique to employ when possible, but never let your preferences get in the way of actually completing the task.
Code that runs is always better than code that doesn't. Everything in moderation.
PS: Also any time your code gets complex enough that it _could_ be non-obvious... just leave some comments in. Some people comment too much, others too little, and have strong feelings about either extreme... but frankly, comments can be a godsend when working with a base you haven't used before or in a long time. Just make sure your comments actually mean something. Sometimes its better to comment your code and use uglier branches than to write an excellently beautiful function that has the next guy jumping back and forth over a billion different functions and methods trying to understand where the single piece of behavior they're trying to alter is. Having everything in one place, but properly documented, can be easier to work with at times.
That "next guy jumping back and forth over a billion different functions and methods trying to understand where the single piece of behavior they are trying to alter is".. hits too hard.. omg the times ive opened a project and everything looks clean and neat but then this function leads to this file which in turn imports this file that imports that file isnt worth it. I think that's why even microservices are being rolled back to/into monoliths, or at least larger more manageable microservices
If you need to add a comment usually it is a sign you should refactor your code (use better function names, move code to external functions, etc...).
@@Kiev-in-3-daysI get where you are coming from, but I imagine this is exactly what leads to: "next guy jumping back and forth over a billion different functions and methods trying to understand where the single piece of behavior they are trying to alter is"
@@fryphillipj560 Well, that is just not what I am experiencing with my code. I know exactly what functions do since the name of a function is self explanatory. And each function is located in the feature or category object they belong to. If you organize your code properly I guaranty what you describe just doesn't happen. Sure you jump a lot but a jump is just a fraction of a second in visual studio code.
@@Kiev-in-3-days it happens 😁
Coding is not in small steps for me .. I always keep a bigger picture of what's happening and the code in front of me helps keep that picture .. so even if the function is a click away in some code editor .. it's still distracting ..
Ladies and gentlemen the result of hiding the dislike count
@4:13 the "single return policy" is a legacy of assembler programming. The purpose was to make sure the stack always got unwound properly.
returns in higher level programming, if they even get compiled to assembly before execution anyway, will get abstracted into comparisons that point branches at a single return.
(same with intermediate languages that have a return atom like MSIL, write C# code with multiple returns and the MSIL it is built into will probably still only have one return instruction)
so if this even still matters for assembly, you aren't writing assembly, there's no reason why the system that produces assembly from your code has to trust you to perform a literal return at the exact position that you told it to (because these languages are able to use jumps and breaks to get around, and that isn't an anti-pattern because of how low level they are)
@@MagicGonads I have to confirm what you say, but I agree with you. At least in C, when I look at disassembled code, I see returns always branch to the epilogue of the function (the code you don't see when writing a function) where things get tidied up for return. So I don't see that having multiples return is a problem for any modern compiler, nor I see a problem with using else, or any other keyword or statement.
Some programmers jump like crazy if you just mention that C has "goto" they always tell you "you should never used" because branching like that is a really bad idea. I always think "here is another programmer who has never looked at disassembled code or has no idea of any ISA". I remember attending a lecture from Donald Knuth where someone asked about this particular issue, in the end he not only said that he used "goto" multiple times in the past but also stated that any statement or logic expression can be dangerous. He just mentioned some study he conducted with his students back in the 70's, so no programming technique is good or bad, just depends on skills and scenario.
Else has a place, just like guard conditions have a place. If your entire function is basically a guard condition what is it guarding? It reminds me of how when someone learns something new, they try to find creative ways to use that new thing and end up using it in a way it isn't intended. To quote Abraham Maslow, “I suppose it is tempting, if the only tool you have is a hammer, to treat everything as if it were a nail.”
Very true. Use various scripts and classes where it makes sense. Use functions where it makes sense to use them. Use else conditions where it makes sense to use them. To just eliminate else altogether is pretty stupid, imo. There is no right answer on when and when not to use either, but use common sense when approaching both.
And this is something I think about when I program. How can I make my code in such a way where I can make quick changes in the future while understanding its impact on the overall outcome? Should I create a separate class so I can readily import it to various other scripts? Should I break up my code into chunks so I can reasonably call upon functions multiple times, instead of needlessly creating one that will only be called upon once. Should I use an else if statement or a nested if statement when handling various variables? These are all questions I ask myself, and I know the answer isn’t always the same across the board.
Using early returns in place of else only improves readability in very few cases. Most of the time, it just makes it more difficult to tell that code is conditional.
I cannot tell you the number of hours I have spent trying to figure out why some code wasn't running, only to find a "return" hidden deeply in an earlier if block.
I worked at a company where early returns were banned in the coding standards, for that reason.
Yeah, agree. This is a real pain especially when maintaining legacy code. You find what seems like the most appropriate place to make your change, only to find that the new code doesn't run sometimes because of the early return buried somewhere.
I favor an approach where you use guard clauses to sanity check the parameters to.a function early on, and return early if you can't proceed. But once your function gets into doing "real work", it should run through to the end with a single return point.
"hidden deeply" There's
the problem :-)
One could argue that adding an extra function to replace if statements also unnecessarily complicates code. I find reading the code without the else requires understanding the run-time order jumping between functions versus being able to parse the logic manually.
Code is for humans. So I write to be readable even for another person, a year or more later. Most times condensed code is harder to read and some times might lead to problems. I want to make my life easier, I don't want my colleagues to always ask me "what do you mean by this?"
If you want to do that at the cost of writing inefficient code then you really need to think it all over again. It is basic knowledge that nesting increases the complexity of a code (yes Ik it is still done in tons of cases). If there is a way that you are able to avoid it then going for it is the best thing you can do. Code is supposed to advance with time and it will keep getting complex with time as well. It is really a bad mindset to not wanting to improve and stay in your comfort zone just so that it doesn't become too much of a task to keep a tab on what's going on.
Humans adapt to change as we always have. Not just humans but every organism that are thriving currently were able to adapt to changes and that's why they are alive.
Constant improvement and wanting to be more efficient is a mindset that is extremely important if you don't want to be a typical C++ developer who just wants to stay at their comfort zone and keep writing the same shite every day for the rest of their lives cuz they are too afraid to learn something new as they are way too comfortable.
But then, that's just my mindset. I understand if you don't get along with it and no offence from my side. Just said what I thought was important. In no way I want to start any sort of argument or what not.
Peace.
@@TheDarknessDragon Damn, that's inspiring. Thanks.
@@dariosucevac7623 no problem mate :)
In the example in the video, as long as the functions are named properly and according to convention, the code is more readable than with nested if/else clauses IMHO.
@@andli True
Professors told me to do this good thing, but it wasnt till i started thinking for myself that i started doing this stupid thing.
What happens if later on there's a bug with your conditional that would typically show up inside the else clause? Since you don't have one you'll be dealing with a silent error and if you don't remember what you did before you could be in a world of hurt.
There's a reason the ternary operator enforces it: You should always have an else statement.
For straightforward programmatic code like this, it is not really needed since age can be well defined, and returned alongside the other outputs either for debugging or added to the console output "you are xx years old, you may..." so that it is clearly visible. In a case like this, one should also add a clause that demands that p be an integer, and below some reasonable number, and then raise an exception on failure.
It gets trickier with multiple variables, especially when they are mixed values and mixed formats. I work with big data, and most of my code does nothing more than validate the inputs as valid.
The other important aspect of exiting early is that it is much more efficient. Performing a bunch of Booleans on 10's of millions of data points runs slowly even on the Super where I work. Put the most common exit condition as close to the top as possible, and move on to the next datum.
@Erich If your if statement has faulty logic it could evaluate to false and continue until it reaches the else statement.
If you don't have an else statement you won't be able to quickly identify which condition is failing. I've seen it take hours before so all my conditions have an else clause.
@Raymond Andrews Hey thanks I learned something new!
That's why we have debugger I guess?
@Raymond Andrews No, not exactly. Think about it, if condition is false, and the ternary operator can only output when its true, then what happens? Not all programming languages have the concept of JS undefined, maybe the output shouldn't be undefined either. Your argument on the operator's name is unnecessary and it could be called something else if you wish.
I've often omit "else" after "return" myself, for example when I write recursion and start with the base case. Mainly because I'm lazy. But I disagree that it's generally more readable, and especiallly that it produces less mental overhead. To wholly understand what the function does, you pretty much have to add the missing "else"s to reconstruct the control flow. It's easy for experienced programmers, but it rather adds a bit more to the mental overhead than makes it less. And I don't think "canDrinkResponse" is a good example for extracting a function. I don't really see any advantage doing it except omitting that one "else". So it comes down to one level of else-nesting vs. an extra function. I'd say that one level of else-nesting produces less mental overhead.
I personally love to use switch-case. No matter how many conditions, you can pair them around and have the code be both explicit, safe and clean.
Nested switch cases with gotos are the best!
If you're building larger systems that will require long term maintnance switch is probably worse than using a string of else if due to how rigid it gets when multiple systems need to interact with it. Think about it, you need to explicitly state what to expect in a case, then if you need to change one of the cases, then you'll also need to change it for all other systems.
Sadly, python doesn't have switch-case structure yet. For me, this is very good advice.
I had a prof in college that wrote a subroutine that "simplified" the logic for us. The "simplified" logic had multiple nested switch statements up to 3 level nesting....
He taught primarily engineering and not much comp sci.
@@inigo8740 It was added recently. It's called match/case.
I like the guard clauses, especially right at the top of the function where it's easy to spot.
I saw a lot of beginners get into trouble by NOT using the else. They would write code that only checked the true condition with no thought about what happened when it was false.
All programming techniques have their place. Sticking to or avoiding certain techniques are limiting. Instead we should try to use the best technique for a particular use case. In the case of the code shown I would have rather used a switch statement as I think this is a more clean approach. Multiple If statements, or If Else statements also have their place though.
Wont the extra function calls add overhead (however minimal) instead of just using else within the same function? It really depends on the language and compiler but unless the compiler optimizes code in a very specific way extra functions will just add unnecessary time to execute. But i also agree you need to gain readability at the cost of performance sometimes so its definitely a fine line.
I've only ever messed around with Arduino stuff, but on microcontrollers (from what I've read) the 'if/else' way of doing things is generally preferred to switch/case statements or separate functions because of speed. For my feeble brain, I just stick to if/else because I can come back to project code many months or a year later and still understand my own writing. Also, the early exiting/returns make me nervous, since I'd like to test all available variables in a function first procedurally; I've been caught out doing the 'early exit' thing before.
Javascript compressors like Terser will eliminate those inner functions if they are simple enough. Generally, compilers offer "function inlining" optimization since decades.
6:15 If you change the order of the two "if" statements you can avoid using "else", and it would be solved without additional functions.
Youd have an additional assignment to result tough.
Switch cases: Allow us to introduce ourselves
How would you rewrite the same logic with switch case?
@@grantorino2025 with languages that support pattern matching
@@grantorino2025 function canDrink(person) {
switch (true) {
case person?.age == null:
console.log("You are not a person")
break;
case person.age < 18:
console.log("Nope")
break;
case person.age < 21:
console.log("Not in the US")
break;
default:
console.log("Yes")
break;
}
}
const p = {
age: 22,
}
canDrink(p)
Having nested Ifs and returns inside them is a Code Smell. Inputs / parameters verification with direct returns should be placed and at the top of the functions (also, split your logic in functions / methods).
Also if you're evaluating a variable just use switch case.
switch case has almost no benefits.
Can you provide an example?
Switch is even worse.
@@thefreeze6023 switch case is more readable in cases where you can have a different number of actions depending on the value of a parameter. It's more readable than trying to use Ifs.
This is also very use-case dependant.
@@JR-mk6ow i simply disagree that it is more readable.
Guard clauses are useful for stuff like the initial "not null" check, but I'm not sure I agree with the other changes as much. Moving stuff that's only used in one place into a separate function IMO makes code harder to follow because you need to move around to multiple different functions just to follow what's happening. With more complex functionality you might end up bouncing between like 4 or 5 different functions just to follow the logic of one operation, and in all that bouncing around it's easy to lose track of what exactly is happening, when, and why.
As for the early return clauses with every "if" statement, I don't really see how it's any easier to follow than just using "else". In fact it increases the chance that someone new to the code misunderstands it because they miss the return line and don't realize that only one "if" is going to be used. If you really hate "else" then it makes more sense to just use a switch statement; in fact all you're really doing by returning after each condition is jury-rigging your own switch statement out of ifs.
Yeah, breaking the code into several function just to avoid a straightforward If/else statement is what leads to "enterprise code" such as splitting FizzBuzz into a FizzResponseStringFactory and a BuzzResponseStringFactory, both being children of a StringFactoryFactory of course. (I know its one hell of a slippery slope and mostly for comedy)
me an intellectual: everything after an if return is an else.
Yes but also no, but also yes? XD
I was think the same thing. You're just replacing "else" with "return".
It improves readability tho
@@hil449 does it really? Your entire decision tree is on the same level as the rest of your code, regardless of how many logic levels deep you are. That's the antithesis of readability
"This entirely subjective matter is FACT!"
6:53 try putting the less restrictive constraint first. If < 21 then if < 18 and you got rid of the extra function *and* the elses
The least restrictive constraint is actually the inverse: !(age < 21). That is, everyone 21 or over (the majority of people).
If you put < 21 first, then < 18 would return ""Not in the US" instead of "Nope", which fundamentally alters the logic of the original function.
I appreciate that this is 2 years old and you may have already covered it in a future video. My favourite technique for dealing with your problem is using the try/catch coding style. It works similar that you switch your checks around.
this is more of a style thing versus a *actual* good coding habit
Can you elaborate. I just want to know what you mean not trying to attack :3.
@@Kevin-lh6xu seems like he is simply rearranging and splitting his original nested if-else block. What i mean by style is that one could easily elect to say use a Switch block, ternaries, null coalescing etc to accomplish the same thing. Sort of at the end of the day comes back to ones coding style vs a wrong and right way of doing things (similar to how if multiple people sat down and wrote an essay... we would all for the most part structure our essays by paragraphs differently/logic to accomplish the same thing)
@@harshilpatel387 I have worked in some big projects (hundreds of classes) and else and switch case statements are the critical places where bugs are introduced. It is good and common practice to avoid them and use little processing blocks. Switch cases can always be replaced by a better pattern and are reserved for the factory pattern.
@@gabrielyea "Switch cases can always be replaced by a better pattern and are reserved for the factory pattern." Please implement Duff's Device using the factory pattern.
Nested ifs and loops can get real nasty, so the functions can really help clean things up. But past that point you're probably right, unless there's some branchless path you can take which would definitely speed up your program.
The deeply nested if else code is known as the arrowhead anti-pattern. The name comes from the visual appearance of the code like an ... arrowhead because of the indentation.
The solution is to use a single-nested if / else chain, not to do away with else altogether.
@@ssw4m You probably meant to comment on the video and not in reply to my comment.
Totally questionnable : what if, for example, one developer calls your "canDrinkResponse" function with an undefined age ? If this function is only to be used inside your other function, it shouldn't have visibility anywhere else but in your "canDrinkResponse" function. "Else" statement is often used for code robustness matter, in embedded softwares development for example, and it can ease analysis when tests are concerned.
What he says about single return per function does make sense but for explicitly typed languages such as C++ or Java its better to only have 1 return only (although it would work)
Aside that: your point makes sense, why not make a class with private attributes and […]. It just gets cluttered. If and else exist for a reason
What are you talking about? Defensive programming clutters the code and will not "ease analysis".
@@rowolta i was being sarcastic when i said lets add a class. Why would you want to make a class just to be able to execute 1 function?
@@katalysmus sorry. The response was intended for @rc'n gel productions.
@@rowolta Oh ok dont worry
That’s so smart. I never thought of that as an option.
I absolutely agree with you when it comes to cases where you may have to many nested if/else-clauses that it gets unreadable. But readability is not all! If you use an ELSE, you exclude everything that is not covered by IF. This is means ultimatively, that you have to cover ANY case, that is not covered by your IF. It's just logically easier to use an ELSE instead of "IF'fing everything out"... you little hacker. ;) Very nice video.
In my opinion, "else" does not hurt a code's readability (unless, of course, long nested cases are being used). I think it makes the reading way more natural, especially for someone who is pretty new at coding. Also, removing it doesn't really improve it, it changes the way it appears better to you in terms of simplification, even though numerous functions for just one specific action do make the reading a little more complicated; because after all it's just 1 small process that makes the executor skip to the else's code block, so even if you removed it it wouldn't really change that many CPU processes and the code would be more clear to who's new at coding, as the "else" keyword is a fundamental ground to step on when coming to a code's reading naturality.
Having spent most of my career (35+ years) writing c based embedded code for automotive and aerospace, the biggest part of coding has always been on reliability and robustness. Most of that revolves around code that has zero ambiguity and decisions that have a coding answer to every permutation. This ensures that you have considered all these and single return points from functions are usually better from code size as the local stack handling for the function call is done in one place. Also, creating more function requires bigger stacks and has a performance hit, so understand the end target before just removing keywords! For this reason, MISRA C/C++ was created for highly robust and deterministic code. Will be different for web based coding, but it's principles are extremely valuable and well worth a read.
I've been at it about the same time... I've worked for various major aerospace and DoD contractors over the years... I seriously doubt that this kid has ever worked on a large project which required large numbers of developers in multiple groups at multiple companies...
It’s nice to understand the affect of branching statements on your code’s performance. Using / not using if-else isn’t usually a performance optimization.
It’s a valid tool, that has its benefits. Replacing if-else with any other type of statement will also have its affects on performance.
I low-key hate these videos that tell people to adopt a new style into their code. Just write regular code..
To elaborate:
If-else statements perform similar to switch statements - after about 6 comparisons, it’s recommended to switch (assuming your working with switch-valid values).
It’s not uncommon to write a method that requires a conditional, which might return a result.
You can use the syntax:
return (condition)? if_value : else_value;
Which can serve as a single-line statement, that could be used in lambdas, loops, or maybe a call to “this(…)”.
Else blocks might be necessary for a specific flow you might want. If you have a method:
int f (x) {
if( condition )
return 0; // constant value
else
return f(x+1); // recursive call
return x-1; // not constant value
}
When you say you don’t use else. I just think you’re coding to the fullness of your capability.
Some people try to avoid branching statements with arithmetic expressions : iff it is possible to do so - which can help the compiler. Assuming your running some pretty expensive code. That’s such a trivial situation too. Usually, we might look at fork-join pools to help optimize performance like that.
I was taught to use else in my first couple introductory programming classes. But when I took data structures and algorithms, my professor advised us to try not to use else. It was hard at first, having to get creative with different methods. But after using it for a while (just like recursion), I totally saw the value in using it and it totally made my code way better.
This is called "early out", and has been used for decades. It's interesting to see people rediscover it. However, I've leaning toward functional style lately, and that may require `else` in my language of choice: Rust. In Rust `if` is like a ternary operator, and `match` is like `switch` in Javascript and other C-like languages, but on steroids, and unlike `switch`, it's an expression, not a statement. Like watching a recursive algorithm execute, there's a kind of elegance in data flowing through a single expression that happens to be the only expression at the root scope of the function. Also, you can leave off the `return` keyword and the final semicolon of the final expression in a function to imply that it's returned.
Cutting through YT 10-min Padding:
* Use guard-clauses instead of else's
* Reject Single-Return, Embrace Multi-Return
* If nesting, consider making a function for it instead
Seriously - do not embrace multi-return. I have given an example above where you can have one return and no elses
"Single-Return"
Single-Return is a necessity for many areas. When intensive logging (entry, exit...) is your only friend, multi-return is the last thing you want to use.
Multi-Return is good for things like guards at the beginning. Other than that, it should be used carefully and logically so as to keep the flow of a function readable.
@@JohnSmith-xf1zu So tell me then, why even guards are not allowed to be used for control systems for nuclear power stations and aircraft? They are explicitly forbidden. Perhaps all these people extolling the use of them have more experience of stopping things going horribly wrong in disasterous scenarios - I doubt it though :)
@@JohnSmith-xf1zu The real thing is this is in safety critical software it is not even written like this. Most problems are first modelled as a finite state machine - there are no if statements really. The problem is modelled as an FSM using a UML, or even Yourdon. You do not encounter guard statements. All routes are caterred for, and fall out of invalid event and state combinations.
The whole issue with software development and naval gazing on issues such as this, is they have not actually designed the code properly in the first place, or used best practices from software that needs to be "safe", and also actually testable.
It is easy to start programming and look clever. It is way harder to think harder and design. It is what annoys people who develop software when functional safety is required hen too much emphasis is given to algorithms.
For example, examples given for Google / Facebook interviews all rely on recursion. If you rely on recursion in say a fly by wire aircrafy system - it is not determinstic, and you could be flying along and you blow the stack - end net result - plane falls out of the sky!!!
So SC engineers have to follow rules - which are lessons learnt from people making assumptions. Sooo design first, do not use multiple returns or guard statements, and do not use recursion - if you are clever enough you can linearise recursion algorithms in a fixed memory space. And so on and so on - the MISRA rules are always worth a read.
else statements have their place. Its just if things start getting hairy with them all over the place, then there's probably a better way. Sometimes a new function is the solution, sometimes a case block is. Just depends on the program requirements. But most of all - less is more as long as its human readable
That second if-else looked totally fine to me. I've seen so many articles like "switch is bad" or "don't use else"... The reality is: DO use early outs and polymorphism whenever it improves the readability of your code but DON'T restrict your coding to a certain style because you believe it's the only viable practice. Switch and else are clean and relevant in many situations.
A professional software is developed being restricted to well defined certain specific single style. That separates "professinal software development" from "coding".
We had this world class coder in the company, he was winning coding competitions all around world. He could code insaly complex functionalities inline to single line. He was free to not restrict himself to certain style, specially once he was fired. His code was unmaintainable as everyone else struggled to read and understand it. Software development is a team sport, if you do not work together in same way as the rest of the team, you are of no value to any team.
@@michaelholopainen2822 Does unconditionally banning else and switch look like "professional software development" to you? Of course we need a strict coding style and stay consistent with it (no matter if we're working in team or not!) But we should never forget the end goal: making the code look clean! And banning else and/or switch, as appealing as it sounds, goes completely against the idea of clean code.
I disagree with you. Although in your examples it is actually better to try to avoid else statement, in some more complicated programs avoiding else statements will make them less readable, especially when you have a long code and have to jump between functions every time you want an if statement (and you’ll also need to pass it all the variables required, which can be a pretty long and nasty function deceleration).
I do agree that in some use cases avoiding else might be better, but it many cases it will just make it more complicated.
Therefore it shouldn’t there shouldn’t be a rule “always try to avoid else statement”, it is different for every case, and the programmer should think what suits it the most.
It’s more intuitive to do error checking without else. However, by using extra function calls, you are creating more overhead. If/else has hardware support, so it would run better on resource limited computers than using multiple function calls
The canDrinkBetter function at 4:37 is the best why would you need returns in every if when you can have a clear else. it's less readable having a console.log at the end of the function but returns everywhere so you don't really reach the end in 90% of the cases. and why would you split a 11 lines function in 2 ?! code a real software with this mentality and you'll stackoverflow without even failing a recurtion
I avoid 'else' every moment that's possible to avoid to. Else... I have to use it.
same here
I avoid variables, functions, and branching, just so people think I'm a code god.
@@travis1240 is that so? care to post some code?
Same here. Else and its evil twin else-if have caused many bugs in my code in the past. Nowadays I avoid both.
The troubles i went through (started 30 years ago) with others saying that early exit is not allowed and every single function has to have a single exit point. That was also the cause of many deep nested ifs, because according to best practices it was not allowed to exit early. Happy to see it now changed, but be prepared for many 'seniors' that review your code will still don't get it. I am not really happy about putting stuff into another function. If that function is only called from the function you are refactoring just leave the code there. You otherwise quickly end up in a treasure hunt to see where things are actually happening. If you have to resort to another 'should not do solution' because writing not a single else becomes the new mantra then one problem is solved and another created. As long as an if/else is not nested it is fine. And for many situations a switch is also fine. Just use the one that is most clear and does not result in deep nesting. Most important for me is when i read a function i can read it from the top down without having to open other files, search for functions etc. Naming functions clearly then becomes the main thing. Oh by the way your deadline is tomorrow, so many times good enough is really good enough.
I get it, but if you have multiple nested ifs you've designed your code badly - not that multiple returns are OK. The reason for multiple returns is nothing to do with nested ifs. It's for consistent responses through your code - its all to easy to return with one response, and to change the response type or format in the next response. Having a single return guards against a different format return. Functions should be no more than 50 lines long anyway (I would say 20 really) and so multiple returns do not add any granularity in terms of visibility and add their own problems for debugging.
I started 30 years ago as well, and I've modernised most of my coding. But having developed and maintained multiple enterprise level systems I can tell you multiple returns is insane.
@@gruffmorris9098 Were these multiple enterprise level systems not in type safe languages? Because there's no way to return different types in anything type safe - you'll get it thrown right back in your face as soon as you finish the line of code.
I don't get this hate for nested if's, either. What's the problem? It's rare but, if a decision is complicated, you're going to need it. (You're also going to need some solid comments around the code for exactly the same reason.)
@@ZlothZloth Truly complicated decisions can be split up into smaller functions with descriptive names and parameter lists. (Or sometimes replaced with polymorphism.) I have not yet seen a situation where this could not be done.
My friend has been told by his college teacher not to use break or continue in the loops because its "bad practice" and instead use true / false flags. I would take everything you're being told in college with a grain of salt...
Continue using break and continue keywords. In real world, client will not ask for that "Did you use break or continue in your loop?" or whatever. As long as your program gets the job done then you're good to go.
I guess to use break might be a bad practice, I rarely use it, instead use a lot of continue.... if you think about it, why would you wanna break a for loop ? doesn't make sense only if you're looking for something in it, in that case you use a .find() ...
Might be if some state changes in that case you want to run an recursive function to navigate an object/array and have a guard clause
@@genechristiansomoza4931 I consider that you should be cautious about giving those advices. Of course, a stakeholder will not care whether you clean code or not, it is a matter of maintainability of the software: the code you write has repercussions to the team/company you work with if it does not allow extensibility/reusability. I suggest you to read about refactoring and clean architecture to gain insight into this topic. Best regards!
@@jonatlop3816 I did not give bad advise. I just said not to be afraid of using break and continue on loops if necessary and if it gets the job done. Refactoring code is a different topic.
Continue and break are very widely used and useful. In many cases, replacing them with a Boolean in the loop will be tedious and cluttered. Don’t listen to those people.
At 5:45 you introduce the result-variable, and at 6:15 you add an else if back in. Instead you could change the order of the if statements, so that you check for "< 21" before you check for "< 18" thus removing the need for "else if" cause the "< 18" will overwrite the result if it is true.
What a great illustration of dangers of playing with logic and then breaking things up. At 6:16 you've broken the logic so that it would never return "Nope". When you break up if/else chains you need to keep in mind all the logic changes you're actually introducing.
Just swap them around
What? He adds the requires else like 2 seconds later. It still works fine.
Also it's possible to do this way:
function canDrinkResponse({ age }) {
return [
{ validation: age < 18, value: "Nope" },
{ validation: age < 21, value: "Not in the US" },
{ validation: true, value: "Yes" },
].find((c) => c.validation).value;
}
Unnecessarily complicated for that number of conditions. Useful if there are many conditions to evaluate.
Also you increase the run time from constant time to linear time, and also increase space from constant space to O(n). Not a good solution for speed or memory management.
"Thank you" from discord here lmaooo, such a coincidence i find u here, my homie taking over the world, keep the hard work, love u.
I agree with David l. This is code I would have rejected in a code review. It doesn't add any value and only makes it more difficult to read, understand, explain - and slower as well.
There may be cases where similar pattern may be useful, if you have many validations, but at that point you'd likely solve that differently and be more general to the approach.
What you show here is a typical case of over-engineering and it would most likely be rejected in a code review. Fancy: yes, understandable / readable at the first glance: absolutely not
One more big advantage is unit testing. I know from experience that breaking down of the function into modules makes it that much easy to unit test.
Just make sure you don't overdo it ☺️
Yes, but he broke it down waaaay to much.
There is zero point in testing the first function. you're pretty much writing the same function to test that function.
i had heard the 1 return per function, but i dropped that pretty quickly when i realized that multiple returns provided code "safety" to terminate when i wanted it to and prevented the chance of unintended logic flow continuing past where it "solved" the question of the function.
This is known as a guard. Your code is going for an ideal flow ("yes") and you check then return non-ideal flows to guard the ideal. I'm a fan of it, it's part of clean architecture
Do you view the dependency inversion that is central to the clean architecture as enforceable in dynamically typed languages?
For some functions, like the one you showed I don't use else and I usually have more than one return. I think that there are still situations where using else is better and it even looks cleaner
Especially when you can't return in that part of the code and making a new function is wasteful.
While this approach is interesting, another solution is to use a state machine approach. State programming is much easier to maintain, and can also address considerations such as single return points (which in some systems/environments is important). It would also allow much easier substitution for drinking age in different regions, whereas this example would become difficult to maintain.
For regional code, it's probably just better to use a function table (or some other data structure that maps region to function). Considering the simplicity of the example, a state machine would be overkill.
Those who have issue with what functions might return or whatever, use typescript. And everyone is saying that else in his code didnt have to be removed etc..., but this was just an example, in a real world scenario, sometimes nested if else can get really messy to understand, its upto you to determine when its time to simplify the mess, in a code like this we won't have to do such things but again its just an example of how to improve the code not an example of what kind of code to be improved.
Or use switch statements
If we need only canDrinkResponse() once, we could further use IIFE (Immediately invoked function expression)
And then all the junior programmers will come to you and say WTF is this magic??
noice
The main thing missing from why its worth doing this is unit testing. I guess JS doesn't have a huge testing culture, but that's one of the bigger reasons. It often takes more, and more complex test scenarios to get all the paths of nested if/else blocks. But with the smaller function with two ifs you know exactly what and how to test passing in just an integer not an object.
The argument is strong, but if the logic is the same and doesn't hurt badly the performance, it's also too strong to avoid like hell "else". In pursue of that, people use big hash tables everywhere, multiple key/values, it's nice, it's beautiful. But many times code turns not as readable as a simple vanilla if/else for every people you expect to work on that code.
I'm appreciating your creating and sharing these video works. You do a lot of things right. And that's cool that you also point out competing videos: excellent positivity. Chapsas has a lot of videos, but I have a difficult time following his speech: it's a bit too fast.
Your speech is fast too, but your annunciation is quite excellent and I have zero problem understanding you. I love that you present directly, quickly, and clearly.
Kyle: Don’t use else statement
Me using switch-case: *cries in the corner*
Exactly my thoughts
Oh god, I hate switch case! Yes sometimes I find them useful but most times they just make code clunky and unreadable! And the whole idea of "Falling through" unless I add break? WHY??? Couldn't they have done the reverse to fall through only with continue instead? The default behavior should have been to break and exit out if there's no continue. (Curiously, Liquid seems to implement it this way.)
@@adtc i rarely use switch too. But I do rather use switch compare to what he did in the video. Imaging having 5 if compared to 1 switch
Switch-Case is a God!
@@ariffrahim6459 I'm not sure if switch case can work that way, as it jumps based on exactly what value it is, not whether it fits a condition
There are generally two reasons why you would want to extract a piece of code into a separate function: reuse and decomplication.
- Reuse is when it needs to be used from more than one place.
- Decomplication is to “divide and conquer”, and make it possible to reason about (and possibly test!) each individual piece in isolation.
The example in the video illustrated decomplication, and could arguably illustrate reuse as well (by adding some code which uses the string result differently from logging it to the console).
The removal of “else” seems to have been just a happy coincidence of these, more fundamental, refactorings. I don’t think it should be a goal unto itself.
I call those early bail conditions.
I like those. I use everywhere in my code.
But there are merits to use else. But you've to use it carefully.
I was with you right up until "you've to use it carefully".
@@brandonlewis2599 maybe then "responsibly".
His example is so trivial ...
Early bails also help your program run faster by not checking things that you already know are false