DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 966,155 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Jarrod Roberson
Jarrod Roberson

Posted on

Stop saying "Stop using Else" and other nonsense! ...

I keep seeing articles that demand the reader "Stop doing" something that is fine practice, if not good/best practices just because they saw some demo on youtube and that person was not doing that thing and it now gospel to never do it again.

Stop saying "Stop using Else" and other nonsense!

This is a very concrete example of advice that is at best ignorance and at worse malicious misinformation.

If you google "stop using else" you will get pages and pages of articles written mostly by people that only have passing experience in a single language, usually JavaScript, and have never had to maintain a program with total lines of code greater than 10K, espousing this nonsense as if it was a revelation of some new gospel.

Its not, it is an ancient screed from the Cult of the Lines of Code Gospel or the Followers of the Optional Braces disciples repeat these mantras and have no experience with what they are saying in the larger picture.

Explicit Is Better Than Implicit!

This is from PEP 20 - The Zen of Python but it applies to all higher level languages and is knowledge that was learned from working with previous languages that were dominated by the "Cult of the Lines of Code" acolytes, this cult still preaches such things as the First 80 Columns are the only holy columns fallacy in 2022 as well.

This statement should be self explanatory and non-controversial. Program code that is explicit is unambiguous in its intent on what its behavior is expected to be.

That is not to say that its behavior is correct, it is just that it is not a mystery what the desired output of any input is expected to be. Executable Code should be the definitive source of logic, and comments are not executable even if they are semantically code.

Else is explicit, its absence is intentional obfuscation!

The same cult that is espousing this nonsense now is the same cult that exposed the nonsense of leaving off {} from if statements and for the exact same reasons.

The most popular ones are, "they are not needed", "they increase lines of code or keypresses", "they are more to read", etc. All of them are backed by logical fallacies as why they are correct.

Leaving off an else statement never adds any information, it removes information about what the code is intended to do. Anything that removes information in this case is harmful.

Some languages are harmed more than others!

Leaving off the else block in languages like Python are even more harmful than the languages most of these authors are using to justify their ideology. Go and JavaScript I am looking at you.

The lack of block characters, or leaving off block characters (which is worse), makes these types of logic bombs even harder to reason about when they explode and you can not see where the issue is just looking at the code. Especially, when they cause intermittent bugs because the bad logic only triggers occasionally based on some state machine that is not defined and impossible to reason about when the application is running much when you are looking at the source.

Garbage In/Garbage Out

This stop using ??? advice is primarily predicated on bad advice that the reader does not comprehend as bad advice because of lack of comprehension from lack of experience.

These same people will mindlessly espouse concepts like DRY and SOLID, avoid side-effects and completely ignore that this advice introduces implicit logic solely based on side effects.

The reasoning in this case is basically some form of "else is just fluff and adds nothing useful" and then follows with "you should code the if clause and return inside it and then do whatever the else is without the else statement and return there". There is a Luke Skywalker quote from a terrible movie that applies to this but I will not subject you to here.

This is terrible practice and it immediately introduces an impossible to detect bug into the logic of the code. Maybe not one that causes incorrect output immediately, but one that can the very first time someone adds any code outside that initial if block.

All logic is special case!

The misinformation is that the if statement is a special case in some way and that the else is the default/non-special case and does not need special treatment or demarcation.

The fact that you can transpose this specialness just by changing the initial if to if not tells you this is a flawed reasoning.

if somethingSpecial:
    doSomethingSpecial()
doSomethingElseNotSpecial()

if not somethingSpecial:
    doSomethingElseNoSpecial()
doSomethingSpecial()
Enter fullscreen mode Exit fullscreen mode

Note that there is something even more fundamentally wrong with one of these pseudo-code logic above other than the missing else but that is another religious argument where the zealots are wrong as well.

Not Just One Billion Dollar Mistake!

null has been called a Billion Dollar Mistake by its creator. It is arguable that he was off by at least an order of magnitude initially, multiple orders by 2022 standards.

It was just one of many terrible ideas that language creators implement because most of them were not trained in formal logic or even trained in writing computer languages.

Necessity is the Mother

Many popular programming languages were created out of some need as a tool as a means to a very specific end, until C. C was created to be a general purpose language, and as such includes a kitchen sink of bad ideas that are only obvious in hind sight, sometimes even with 50 years of hindsight they are still obvious to some people.

One of the most subtle bugs in C is the one line if statement, that leaves off the {}. If someone comes along and just adds another line of code below the initial line in the if statement, what were their intentions?

Usually their intention was to add that line to the if statement, but that is not what happens. It is just executed regardless, which introduces can be an extremely subtle bug that can decimate data in an very quiet way that may not become apparent for hours, days, weeks or even months later.

Leaving off the else introduces the same class of bug, if not always the same level of subtly.

Languages like Go that are planned and designed for a reason just as a reaction to some problem leave a lot of these organic mistakes behind.

Unfortunately Go has just as many opinion based mistakes baked into them to replace the mistakes they avoided. Which personally I think is worse, because these mistakes are based on intention instead of accident. They become dogma immediately, no matter how bad they reveal themselves to be.

They become defining aspects of the language that drive away adoption because they are expected to be adopted by everyone; regardless of how bad an idea it actually ends up being, instead of cautionary tale features that are accepted as being avoided.

I do not hate Go, I have moved to using Go over all the previous languages I usually reached for before I learned Go. Go is a great compromise that can completely replace Java and Python at the same time. Instant compiles of even huge programs means it can be used for rapid development scripting type tasks, cross platform single executables with robust libraries and frameworks ticks the Java boxes.

New ideas are not always new or correct!

One of the most offending languages right now to this is Go. There is a preponderance of effort into pushing the idea that else is not needed, and if it is your code is not structured enough or some other hand wavy non-sense that is your failing and not the failing that it is a terrible practice.

Another, idiom of Go is the normalization of negative Boolean checks in if statements.

if err != nil {
    // do the expected logic here
}

Enter fullscreen mode Exit fullscreen mode

This is the idiom in Go, and it is terrible for many reasons.

First off, the general and correct consensus is that the initial if statement should be not be testing for a negative because it forces more processing on the organic computer that is parsing and interpreting the intent of the logic.

Who in their right mind would let the following type of code construct into an application that was used for anything non-trivial?

if x != true && y != false && z == true {
}
Enter fullscreen mode Exit fullscreen mode

Yet, I have seen thousands of lines of such checks in 20+ year old Java code that the original author insisted was *"best practices" 20+ years later. Lucky for me that IntelliJ idea was able to convert them all to something easier to reason about automatically. We still had to reason about the multiple checks and normalize that, but it did save time and make writing tests for this code possible.

The Go idiom is not exactly the same, but is in the same class of obfuscated logic for the sake of brevity, even if you consider it less harmful than the Java example. Less harmful is not harmless.

Religious Arguments Codified As Idioms are Dogma

Go codifies the "multiple return" religious argument dogma by making it an idiom. It does not solve the argument, it justifies it as "everyone says so", which is just a logical fallacy.

It does this, by making this the idiom in all the official Go source code.

This also has the side effect of justifying that else is never needed and thus it is idiomatic as well to always leave it out.
Leading many people to twist otherwise clear logic in obfuscated knots and write spaghetti code to be able to leave it out.

This Page Left Intentionally Blank

Documents that had important information that needed to be unambiguous in their facts and intention always have blank pages that say THIS PAGE LEFT INTENTIONALLY BLANK, on them in huge block letters. That way no one would wonder if that blank page was supposed to be blank and missing information? Was it a printing error? Is there missing information that could be a cause a serious error? No one would argue these semantic place holders were redundant or not important in the explicit context that they provide.

The else in the if/else symmetry serves this same semantic function. It says explicitly, THIS AND ONLY THIS is what is supposed to happen if the other thing is not true and leave no ambiguity.

It says, if you want something to happen if that special case is not true put it here. If you want something to happen regardless of that space put it outside here.

// something that might return an error
if err != nil {
    return err, nil
}
// something ELSE or something unrelated to the previous if we have to look and parse to know that might return an error
if err != nil {
    return err, nil
}
// something ELSE or something unrelated to the previous if we have to look and parse to know that might return an error
if err != nil {
    return err, nil
}
// something ELSE or something unrelated to the previous if we have to look and parse to know that might return an error
if err != nil {
    return err, nil
}
return nil, somethingNotNil
Enter fullscreen mode Exit fullscreen mode

is what most would consider idiomatic
the following is not wrong but is preferable because of it is explicit and unambiguous to the expected behavior

if err != nil {
    return err, nil
} else {
    return nil, somethingNotNil
}
Enter fullscreen mode Exit fullscreen mode

the needlessly laconic idiom version saves an entire 7 characters, but is introduces the possibility of endless subtle bugs as unattended side effects, incorrect logic and just bad output.

Thankfully, even this idiomatic structure is being challenge by the new/late comers to Go, but in an early enough stage and by enough seasoned developers that it can hopefully be corrected as harmful dogma from early zealots.

The fact that if err != nil is well considered to be just copy paste boilerplate with dozens of macros, libraries, frameworks, and code generator solutions to this should tell you it was never a good idea.

Ignore advice that makes the world worse!

It is very simple:

If the advice in any way makes your code less explicit, introduces any kind of possibly ambiguousness or obfuscation to the intent of the logic expressed as code, then ignore that advice.

This should tell you that all these stop using else articles just as bad for the industry as the same arguments espoused on Usenet in the 1980s promoting omitting optional braces in C for the same incorrect reasons.

Top comments (23)

Collapse
 
cicirello profile image
Vincent A. Cicirello

Your first section heading, Stop saying "Stop using Else" and other nonsense!, would have been a better title. The first time I saw one of those (here on DEV) I thought what sort of "nonsense" is this before I even went into the post.

Collapse
 
jarrodhroberson profile image
Jarrod Roberson Author

done as requested

Collapse
 
codingchili profile image
Robin Duda (they/them)

I found this page from c2 to explain it well, most articles on both sides misses the point. Early returns can be harmful, else can be harmful too - but it's situational and blaming the control structures rather than the surrounding code is irrational. Using decomposition and extraction like described in the link is a good solution, that we should focus on more instead. For more complex cases state machines, decision trees and even ML can be very useful.

wiki.c2.com/?ElseConsideredSmelly

Bonus: fun read on branching,

blog.cloudflare.com/branch-predictor/

Collapse
 
jarrodhroberson profile image
Jarrod Roberson Author • Edited on

no else can NEVER be "harmful", period ... stopped reading your comment when I saw that non-sense.

Collapse
 
syeo66 profile image
Red Ochsenbein (he/him)

So. This basically means you did not understand where the "don't use else" actually comes from. Else allows you to construct huge monsters of functions which you might want to break up. Just think about sequences of if/else or unreasonably nested ifs and elses. When you try to avoid else you're forced to split such a monster. So, else can be harmful, too. The point you're trying to make about being explicit is sort of true... but at least equally important is to be intentional.

Thread Thread
 
jarrodhroberson profile image
Jarrod Roberson Author • Edited on

if/else chains are not else. They are chains of if/else that can be abused. Your argument is a strawman logical fallacy, you are trying to make the argument about something else entirely. (unavoidable pun, I spent 5 mins trying to avoid it :-)

Your strawman can be shot down with one simple strawman. Guns do not kill people, people kill people. and thus Else does not make bad code, people make bad code.

Your complaint is a slippery slope logical fallacy as well, it is easily debunked by just do not do that. Jetbrains has code inspections and your slippery slope argument is one of the built in ones. You just turn it on at ERROR and your code will not compile without you manually marking it as ignore or dealing with it. This is a complete 100% chicken little non-issue.

You can abuse any language feature or construct to create atrocious spaghetti code. And one of those ways objectively is intentionally excluding else when there is an else clause. Just like excluding optional {} on single line if or for statements. It is a best naive ignorance from misinformation and lack of experience or it is malicious hubris.

The point is, excluding it when there is else logic that is just fall thru is a side effect programming, that introduces multiple classes of errors that are all invisible errors. Which is worse than someone naively writing some state machine that may become unwieldy.

I have seen dozens of production errors in Fortune 500 and Fortune 50 companies that have cost millions of dollars a minute because of misguided dogma like stop using else. I have been the one that spent tens of hours searching for the issue, and yes there were tests, but the tests did not test for the side effects of unexpected data that triggered the issues that ranged from the app crashing to the app corrupting data so badly that backups had to be used in one case.

Your entire stack of logic is flawed, it is an if/else morass of logical fallacies, the first of many is a strawman, it is easily debunked by the fact that you can just not do what you complain about. No need to enumerate the rest, that one is disqualifying enough.

Collapse
 
codingchili profile image
Robin Duda (they/them)

Exponential growth in cognitive load can never be harmful? Disagree. Exceptions can be harmful, loops can be harmful, even water can be harmful. Keyword "can".

Thread Thread
 
jarrodhroberson profile image
Jarrod Roberson Author • Edited on

that is a bunch of mumbo jumbo garbage, being explicit can never be anything but helpful. There is not a single actual instance that using an else block can be harmful, even else { // this block left intentionally blank } is helpful, it explicitly informs the reader that there is no else condition. That removes the cognitive load from the reader, they no longer wonder if the code after the if block is the else block implicitly that some noob intentionally omitted because of misinformation, misguided hubris and/or dunning/kruger effect.

Thread Thread
 
necrophcodr profile image
necrophcodr

You've made good points, but for me personally being explicit will in certain cases just increase the cognitive load on me as a reader, and I'd prefer to not have it.

Thread Thread
 
jarrodhroberson profile image
Jarrod Roberson Author • Edited on

sincerely I would like to see an actual piece of code that is in software that is actually in use by a significant number of people or supporting a significant amount of money that would meet this criteria, that is NOT just bad code to begin with that could be remediated.

My 38 years of experience and the tens of millions of lines of code I have had to read that I did not write, some of which was 20+ years old, and had been touched by at least 100 different people over the 2 decades before me, I have never come across anything that meet your criteria. But I have seen plenty that forms my opinion on this.

Thread Thread
 
necrophcodr profile image
necrophcodr

I don't have any code off hand, but I've seen it in previous companies I've worked for. Experience or not, my personal opinion on the subject is that it increases my own cognitive load, and so I avoid that. It IS subjective after all.

Thread Thread
 
jarrodhroberson profile image
Jarrod Roberson Author

it is not anymore subjective than gravity.

Thread Thread
 
necrophcodr profile image
necrophcodr

There are exceptions to literally everything. Gravity also is not set in stone anymore than mathematics is. That's still much less subjective than this though. This is very subjective. It is because my while you're pleading a very good case, it does not work for me. Therefore it is not objectively true.

Collapse
 
alonedatascientist profile image
alonedatascientist

Came for all the offended devs in the comments and was not disappointed.

Half the "tutorials" you see on YouTube and even numerous blogs are nonsense cash grabs. For example many if not most of those I am talking about also sell you a course that they swear will make you a full stack master in 72 hours if you'd only make the investment in yourself and purchase their 500$ to 5000$ course. Tons of concepts that are not actually beneficial get promoted on blog posts because the person making the blog is looking for content ... because content is money. There is a lot of "mocking bird" behavior in the tech circle. Hell, there are people that run blogs that haven't even had a dev job before -- they are just great at making content and their target audience never bothers asking too many questions. Its one thing if you are a hobbyist and present yourself as such. Its quite another to be giving interview advice when you've never had an interview in that sector. Its easy enough to spend a day watching YT videos and then just repeating what you heard there elsewhere. For that sweet ad sense money at the least as well as the hope of you buying their course. Rinse. Wash. Repeat.

Are they all like this? No. But there are plenty that are. Enough so that newer devs really should tread with caution because not all sources are quality sources. To put it bluntly: not everyone out there speaking actually knows what the hell their talking about. A lot of "the blind leading the blind".

Collapse
 
liftoffstudios profile image
Liftoff Studios

The irony lol
But good article !

Collapse
 
jmfayard profile image
Jean-Michel Fayard πŸ‡«πŸ‡·πŸ‡©πŸ‡ͺπŸ‡¬πŸ‡§πŸ‡ͺπŸ‡ΈπŸ‡¨πŸ‡΄ • Edited on

Great article

Not just one billion dollar mistake.
null has been called a Billion Dollar Mistake by its creator. It is arguable that he was off by at least an order of magnitude initially, multiple orders by 2022 standards.

I actually talked about this in my article

You are totally right that there is not only one dollar billion mistake.

Tony Hoare, the creator of null, is not alone here. He stands out as a programming hero because he spoke out candidly about the fact that he made mistake. Which made it easier for people after that to realize that the mistake actually needed to be fixed instead of cargo-culting what previous languages did.

Note that even after that people still don't understand what the mistake really was. I talked about it in this article

People understood that the existence of null itself is a mistake. Absolutely not.

  • βœ… null itself is fine, null is a perfectly fine way to say that some data is not present in a list, a map, an API or something. Forcing people to use Option or even worse "design patterns" (really: work-arounds) just makes the world worse.
  • ❌ but null is not a Boolean - Booleans have only two values damn it, that's in the very definition of a boolean - , null not a String, null not a RandomGenerator, it's not anything of that.

The argument was that allowing null to be a valid value of any arbitrary type is a mistake, because it means the compiler can't have your back. Once you fix that mistake, null becomes your friend in languages like Kotlin

See kotlinlang.org/docs/null-safety.html

Ignore advice that makes the world worse!

Holy shit I'm gonna steal this as a mantra.

Collapse
 
jarrodhroberson profile image
Jarrod Roberson Author • Edited on

null itself is fine, null is a perfectly fine way to say that some data is not present in a list, a map, an API or something.

nope, and empty list, map or API return code can tell you that explicitly.

null is implicitly ambiguous, and both of those things you should avoid at all costs, combined they enable apocalypse class of errors.

concrete example;

I had to deal with this personally at work recently, upstream legacy data source has records peppered with null. Downstream they have been processed by multiple systems, before the report system gets the data. The report system has no idea why these fields may be null. Some of them are mandatory for the reports.

Why are they null? Especially TIMESTAMP fields!

Are they null because null actually represents some specific state in the original system. Plenty of database fields that are null actually gets interpreted as 0 or maybe -1 in some app somewhere probably both, how is a downstream system supposed to know that? Same with TIMESTAMP.

No, null is a naive mistake that people keep making. Go made it with nil. null by any other name. They do not allow anything but references to be nil but like Shakespear said, "A rose by any other name...".

The argument that a field like "LastEditTimestamp" should be null when the row has never been edited is the most common presentation. It is wrong as well. LastEditTimestamp should initially be CreatedTimestamp. There no null.

Phone numbers that were never given can easily have "invalid" phone numbers like "1-555-555-5555" that have meaning to represent the missing information. Any person looking at this data will immediately recognize it as not a phone number and can question what it means. And a check for null and a check for this placeholder are the same amount of code. But one tells you something specific, the other is Β―\_(ツ)_/Β―

Missing reference that is a UUID, well there is a UUID for that NIL UUID

This goes on and on, in 38 years, every time someone just insists that null is the only solution, I have found a better one. Signpost values are the best solution to these cases.

null is never the best solution.

Any system that has null in it should regard it as a hard error and stop the world, those systems are robust.

We did this in the specific case at work and the number of issues with reports in downstream systems went to ZERO immediately. Now when there is bad data it never gets far enough downstream to break the reports, it has to be fixed before it can get that far and we know EXACTLY where in the pipeline the bad data either came from or was introduced.

Any language that disallows the concept of null or nil or whatever excludes entire classes of bugs by default. Those languages usually disallow mutation as well, combined the number of classes of bugs that you have to deal with are reduced to something manageable and something you can reason about just by reading the code most of the time, because you do not have to keep huge state machines in your head.

null is bad in all cases.

Collapse
 
jmfayard profile image
Jean-Michel Fayard πŸ‡«πŸ‡·πŸ‡©πŸ‡ͺπŸ‡¬πŸ‡§πŸ‡ͺπŸ‡ΈπŸ‡¨πŸ‡΄ • Edited on

Hello,
Thanks for your response. I think that we in fact say complementary things, which is cool to reach a greater understanding.

So in my Kotlin codebase we have no issues using null, but we use it sparingly, probably 95+% of our fields are non nullable. And also 95+% of them are immutable. Non nullable and immutable is the default, and we deviate from it only when we know why.

I would say that's relatively common to do so in the ecosystem, starting with the stdlib designed by JetBrains with pretty clear rules on when to use them or not.

I agree with many of your points:

  • lists: fully agree, exactly like with nullable booleans - meaning non booleans - I nitpick every PR who think they are a good idea to use instead of empty list :P
  • "API return code are more explicit": Agree. In my case I do GraphQL and Facebook in its great wisdom decided that all return code must be HTTP 200 OK.
  • The report system has no idea why these fields may be null-> Also agre that it's important. And that's why we don't use null for reporting failures, they don't have enough information. Instead we use, not checked exceptions like in Java, because they are glorified GOTO statements, but an Either<Failure, Success> type. An Either is a Monad but if the language supports type X = Failure | Success that's the same idea.

null is bad in all cases.

"Missing reference that is a UUID, well there is a UUID for that NIL UUID "

That's useful to know for langauges without null safety.

But for the others that mean you must remember to check manually every time:
if (uuid.isValid()) { ... } else { .. }

And who is more likely to never forget a safety check: a compiler or a normal human being?

Or what about this imaginary command line tool?

fun fetchCountries(): Map<String, CountryInfo> = mapOf(
    "fr" to CoutryInfo(localName = "France", currency = "EUR", prefix = "+33"),
    "de" to CoutryInfo(localName = "Germany", currency = "EUR", prefix = "+49"),
)

val isoCode: String = askUsertoEnterCountryCode()
val country: CountryInfo?  // the value is explicitely nullable
   = fetchCountries().getOrNull(isoCode)

// the friendly compiler has your back and raises an error: 
// "hey no you can't do that, you must add a null check first!"
println(country.localName)

// that one is validated by the compiler
if (country == null) {
  exitWithError("sorry we don't support country $isoCode yet :(")
} else {
  println("Glad to have you people from ${country.localName}")
  // ...
}
Enter fullscreen mode Exit fullscreen mode

I'm sure you can find a more complex solution that works, but here I will apply your advice:

That would make my codebase worse so I feel safe to ignore the advice :P

Collapse
 
webbureaucrat profile image
webbureaucrat

If you're not already familiar with it, I think you'd enjoy the Elm programming language. It actually requires an else for every if because Elm evaluates everything as an expression, so the if is really just a ternary operator with keywords.

I ended up liking it so much I started using the ternary operator for everything in other languages. Most people would call it too terse (arguably failing the "explict > implicit" test in that sense) but to me it's worth it to have a syntax-level guarantee that I'm not creating subtle bugs like the ones you describe above.

Collapse
 
brense profile image
Rense Bakker

Well... I wouldn't put it as strongly worded as you did, but I mostly agree I guess... Would rather summarize it as: "be careful with using guard clauses to obfuscate if/else logic." 😜

Collapse
 
ravavyr profile image
Ravavyr

For everyone, the TL;DR: "Use If/Else, it's easier to read and understand when you do."

Collapse
 
jarrodhroberson profile image
Jarrod Roberson Author

no, it is safer and less error prone when you do

Collapse
 
gregorygaines profile image
Gregory Gaines

The truth as always!

🌚 Friends don't let friends browse without dark mode.

Sorry, it's true.