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

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

Jarrod Roberson on October 02, 2022

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

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

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

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

Collapse
 
gregorygaines profile image
Gregory Gaines

The truth as always!