185

I have an acquaintance, a more seasoned developer than me. We were talking about programming practices and I was taken aback by his approach on 'if' statements. He insists on some practices regarding if statements that I find rather strange.

Firstly, an if statement should be followed by an else statement, whether there is something to put into it or not. Which leads to code looking like this:

if(condition) 
{
    doStuff();
    return whatever;
}
else
{
}

Secondly, it's better to test for true values rather than false. That means that it's better to test a 'doorClosed' variable instead of a '!doorOpened' variable

His argument is that it makes clearer what the code is doing.

Which confuses me quite a bit, as a combination of those two rules can lead him to write this kind of code if he wants to do something when the condition isn't met.

if(condition)
{
}
else
{
    doStuff();
    return whatever;
}

My feeling about this is that it's indeed very ugly and/or that the quality improvement, if there is any, is negligible. But as a junior, I am prone to doubt my instinct.

So my questions are: Is it a good/bad/"doesn't matter" practice? Is it common practice?

200_success
  • 1,568
  • 11
  • 20
Patsuan
  • 1,627
  • 2
  • 9
  • 10
  • 3
    Related: [How would you know if you've written readable and easily maintainable code?](https://softwareengineering.stackexchange.com/q/141005/64132) – Dan Pichelman Jun 08 '17 at 13:40
  • 63
    Is it possible your coworker comes from a background where life and limb might be at stake? I believe I've seen a few mentions of this sort of thing in systems where a crap ton of testing and automatic analysis is done on code and where getting it wrong could literally cost someone their life. – jpmc26 Jun 09 '17 at 03:47
  • 12
    What does your company code style guide say? – Thorbjørn Ravn Andersen Jun 10 '17 at 11:12
  • 5
    Partial response to your "Secondly" question. If one of the action blocks is long and the other short, test a condition that puts the short block first, so that it's less likely to be missed when reading the code. That condition could be a negation, or a renamed to make it a test for true. – Ethan Bolker Jun 10 '17 at 13:39
  • 1
    The main argument for the empty `else` block has to do maintenance (though this is only one of several practices which must be considered). I don't offhand remember a good "demo" scenario, but there are a number of cases where sticking with a rigid structure, including empty else statements, aids in preventing or detecting editing errors which would be hard to detect but which would lead to screwy operation down the road. – Daniel R Hicks Jun 11 '17 at 02:02
  • This is what one uses an `unless` for. – tchrist Jun 11 '17 at 02:28
  • 2
    The use of if (!...) has to be evaluated on a case by case basis for readability. Plenty of examples are very readable, e.g., if (!faithful) {cheat on wife}. – Ben Crowell Jun 11 '17 at 03:34
  • And empty statement in a series of `if - else if - else if - else if - else` might sometime make the code more readable by splitting a complex expression into 2 simpler ones. Obviously one should use `{ }` for the empty statement and not a `;` alone. It might also reduce required indentation. – Phil1970 Jun 11 '17 at 04:16
  • 2
    In some places I do empty else (or if) with comment, like `else{ /*ignore*/ }` so it is clear that else part could happen, but I deliberately will do nothing in that case. Empty else (if) block looks to me like unfinished code – Piro Jun 12 '17 at 06:54
  • 9
    Resharper would have something to tell your friend, along the line of "Your code is redundant and useless, do you want me to delete/refactor it?" – BgrWorker Jun 12 '17 at 07:15
  • 1
    I clearly am in a minority here and I would never start writing code like this myself, but if I was given access to such a codebase I think it would be above averagedly readable. Would take a few hours to get used to, but after that I wouldn't mind writing code like this personally. – David Mulder Jun 12 '17 at 09:47
  • @BenCrowell Like DavidMulder I don't think I'd start writing code like this, but I think if there is a benefit in doing so -- mainly from consistency of layout -- it would have to be done (virtually) universally, not on a case-by-case basis. – TripeHound Jun 12 '17 at 13:12
  • 4
    to back up the comment by @jpmc26, it is worth noting that this is a MISRA rule. – Baldrickk Jun 12 '17 at 14:06
  • a block with no code is usually considered a bad programming practice fyi. It sounds like your friend is making up some strange standards. Perhaps he was working somewhere where both cases were so common he needed each? – user3916597 Jun 12 '17 at 18:13
  • 1
    If they are so concerned about getting it right then use unit tests. – The Muffin Man Jun 13 '17 at 04:12
  • The only time I got called out for testing negations was when I was coding in Scheme, where the negation is an explicit extra function call. It's `(if doorClosed )` vs. `(if (not doorClosed) )`. Extra, yes, but still a micro-optimization, even with Scheme. – occlean Jun 13 '17 at 09:29
  • I knew a guy who did that. He wasn't very good. – user1172763 Jun 13 '17 at 18:52
  • @TripeHound - I disagree. Consistency is often abused as justification for silly things. When choosing between a good pattern and a consistent pattern, I will always choose good. The problem with doing something poorly in order to be consistent is that when it is finally decided to improve, now the cost of doing so is higher. I'd rather have 1 block of good code and 1 block of bad code than 2 blocks of bad code. – Brandon Jun 14 '17 at 02:45
  • Generally, I don't agree with standards that use the words "always", "must", or "never". Especially on trivial things like whether to use an else block or not. – Brandon Jun 14 '17 at 02:48
  • 1
    @Brandon Totally agree re. good vs. consistent... my one "_Absolute Rule of Software_" is that "_There are no Absolute Rules of Software_". There might always be rare occasions when writing an `if` statement like this made sense, but what I think I was trying to say is that **if** there was a reason to routinely use it (and that's a big "if"), then the nature of that reason would probably demand (almost) universal use of it (e.g. highly safety-critical, where (perhaps misplaced) regulations demand it. – TripeHound Jun 14 '17 at 06:48
  • If I want to make sure I am explicit about a negated condition, I would use `if (false == doorOpened)` instead of `if (!doorOpened)`. – orad Jan 15 '19 at 01:24

15 Answers15

203

Explicit else block

The first rule just pollutes the code and makes it neither more readable, nor less error-prone. The goal of your colleague — I would suppose — is to be explicit, by showing that the developer was fully aware that the condition may evaluate to false. While it is a good thing to be explicit, such explicitness shouldn't come at a cost of three extra lines of code.

I don't even mention the fact that an if statement isn't necessarily followed by either an else or nothing: It could be followed by one or more elifs too.

The presence of the return statement makes things worse. Even if you actually had code to execute within the else block, it would be more readable to do it like this:

if (something)
{
    doStuff();
    return whatever;
}

doOtherThings();
return somethingElse;

This makes the code take two lines less, and unindents the else block. Guard clauses are all about that.

Notice, however, that your colleague's technique could partially solve a very nasty pattern of stacked conditional blocks with no spaces:

if (something)
{
}
if (other)
{
}
else
{
}

In the previous code, the lack of a sane line break after the first if block makes it very easy to misinterpret the code. However, while your colleague's rule would make it more difficult to misread the code, an easier solution would be to simply add a newline.

Test for true, not for false

The second rule might make some sense, but not in its current form.

It is not false that testing for a closed door is more intuitive than testing for a non-opened door. Negations, and especially nested negations, are usually difficult to understand:

if (!this.IsMaster || (!this.Ready && !this.CanWrite))

To solve that, instead of adding empty blocks, create additional properties, when relevant, or local variables.

The condition above could be made readable rather easily:

if (this.IsSlave || (this.Synchronizing && this.ReadOnly))
John Doucette
  • 246
  • 1
  • 7
Arseni Mourzenko
  • 134,780
  • 31
  • 343
  • 513
  • 183
    Empty blocks always look like mistakes to me. It'll immediately draw my attention and I'll ask 'Why is this here?' – Nelson Jun 08 '17 at 14:24
  • 2
    Negating a condition requires an operation, taking CPU time. However, the extra CPU time is hardly worth considering. Rather, performing a branch (jump) *is* expensive (at least relative to the negation), justifying perhaps to put the more likely to happen condition in the if block rather than the else block. – Neil Jun 08 '17 at 14:36
  • 56
    @Neil Negating a condition does not necessarily require any additional CPU time. C compiled to x86 could just swap the jump instruction from `JZ` (Jump if Zero) for `if (foo)` to `JNZ` (Jump if Not Zero) for `if (!foo)`. – 8bittree Jun 08 '17 at 16:32
  • 78
    "**create additional properties with clear names**": Unless you have deeper analysis of the domain, this may be changing the global scope (the original class) to solve a local problem (the application of a specific business rule). One thing that can be done here is to create local booleans with the desired state, such as "var isSlave = !this.IsMaster" and apply your if with the local booleans instead of creating several other properties. So, take the advice with a grain of salt and take some time to analyse the domain before creating new properties. – Machado Jun 08 '17 at 17:09
  • 9
    One other thing I'd do in the name of code readability is to create another local boolean to handle the whole "**this.IsSlave || (this.Synchronizing && this.ReadOnly)**" expression. Something like "**var isImmutable = this.IsSlave || (this.Synchronizing && this.ReadOnly);**" and then test like "**if (isImmutable)**". That would make things clearer for the next unfortunate developer who have to read my code. – Machado Jun 08 '17 at 17:15
  • 1
    It might also make sense to swap out `if(!cond) {doB();} else{doA();}` for `if(cond) {doA();} else{doB();}`. – Casey Jun 08 '17 at 18:09
  • 87
    It's not about "three lines of code", it's about writing for the appropriate audience (someone with at least a basic grasp of programming). An `if` is *already* explicit about the fact that the condition could be false, or you wouldn't be testing for it. An empty block makes things less clear, not more, because no reader is primed to expect it, and now they have to stop and think about how it could have gotten there. – hobbs Jun 08 '17 at 19:29
  • So If I have tests that also test for is master should I create a property that returns `!IsSlave`? What if it is nullable and I need to test for that too should I create yet another property just so I can test is true and be more readable? – SoylentGray Jun 08 '17 at 19:47
  • 2
    The other reason I'll do an empty "if" is to make it explicit that nothing is being done. It gives me someplace to hang a comment: `if(common case) { /*do nothing*/ } else {handle the uncommon case}` – Mark Jun 08 '17 at 20:51
  • 15
    @Mark that's less clear, even with the comment, than saying `if (!commonCase) { handle the uncommon case },` though. – Paul Jun 08 '17 at 23:02
  • Your last example can be rewritten without creating new properties _(which is often not an option)_: `bool isReady = this.Ready || this.CanWrite;` `if(!this.IsMaster || !isReady) ...` – BlueRaja - Danny Pflughoeft Jun 09 '17 at 09:06
  • 3
    Personal practice is to pull negations up to top level. `if (!(this.IsMaster && (this.Ready || this.CanWrite)))`. – Veedrac Jun 09 '17 at 12:03
  • 3
    @hobbs - At least in the limited the domain of high reliability computing, people are primed to almost always expect the `else`. That an `if` doesn't have an `else` is generally taken as a sign of a potential problem with the code. An empty else helps to alleviate those concerns. – David Hammen Jun 09 '17 at 14:35
  • 3
    Most of this answer I agree with, but it seems like duplicating properties of classes to have positive and negative versions would violate DRY somewhat and increase complexity. – Panzercrisis Jun 09 '17 at 21:17
  • I disagree that `if x {doA(); return a;}` `doB(); return b;` is better than `if x {doA(); return a;} else {doB(); return B;}`. In fact I'd argue this should probably be refactored to `return x? presentA() : presentB()`, which makes it completely clear that `A` and `B` are alternative options for the result. – leftaroundabout Jun 10 '17 at 12:12
  • 10
    I think the rule shouldn't be "don't test for negations" but "express conditions in their simplest form," i.e. `isFish` is better than `!isNotFish` but there is absolutely nothing wrong with `!isFish`. – Ant P Jun 10 '17 at 18:50
  • 1
    @Veedrac exactly, use De Morgan hammer and have single negation instead of three. – Kyslik Jun 11 '17 at 08:19
  • 1
    You want to create additional properties that are just in the reverse of ones that already exist? That's absolutely ridiculous, in my opinion. It's as if you want to add confusion (as if they had different core meaning) and failed to complete some sort of refactor. – Brad Jun 11 '17 at 20:35
  • @Brad: you're absolutely right (and this point was already mentioned in comments in the past). I edited my answer. – Arseni Mourzenko Jun 11 '17 at 21:36
  • I'd just like to point out that an empty `else` block won't necessarily incur three wasted lines, depending on the indent style. It would be 0 or 1 with 1TBS or similar styles, for example, depending on whether the programmer puts the closing brace of empty blocks immediately after the opening brace or on a separate line. Still distracting either way, though. – Justin Time - Reinstate Monica Jun 12 '17 at 00:09
  • 2
    @Nelson what about `if(x) {do_stuff();} {/*This space intentionally left blank*/}` ? – Baldrickk Jun 12 '17 at 14:05
  • @Nelson I'm very much inclined to agree, which makes me wonder what the MIRSA rules (which require explicit else, at least in versions I've seen) based themselves on. Was it just the writer's/committee's personal feelings, or was it based on some kind of studies? – mbrig Jun 12 '17 at 16:20
  • 1
    if (!this.IsMaster || (!this.Ready && !this.CanWrite)) -- you are not fixing anything, you're just moving it somewhere else. So you would have, isSlave = !isMaster; isSynchronizing = !ready, readOnly = !canWrite; if(isSlave || (synchronizing && readOnly)) ... But !master might not = slave, you can be master, slave, or something else, etc. So you wouldn't want to use slave = !isMaster, you would want notMaster = !isMaster, so now you have if(notMaster || (notReady && notCanWrite)) -- you have replaced 3 !s with 3 lines of assignment and the word "not" instead of an !... – user3067860 Jun 13 '17 at 14:39
  • 1
    _"It is **not false** that testing for a closed door is more intuitive than testing for a non-opened door."_ The irony ;) – Piovezan Feb 05 '21 at 09:29
65

Regarding the first rule, this is an example of useless typing. Not only does it take longer to type, it will cause huge amounts of confusion to anyone reading the code. If the code isn't needed, don't write it. This would even extend to not having a populated else in your case as the code returns from the if block:

if(condition) 
{
    doStuff();
    return whatever;
}

doSomethingElse(); // no else needed
return somethingelse;

Regarding the second point, it's good to avoid boolean names that contain a negative:

bool doorNotOpen = false; // a double negative is hard to reason
bool doorClosed = false; // this is much clearer

However, extending this to not testing for a negative again, as you point out, leads to more useless typing. The following is far clearer than having an empty if block:

if(!condition)
{
    doStuff();
    return whatever;
}
Deduplicator
  • 8,591
  • 5
  • 31
  • 50
David Arno
  • 38,972
  • 9
  • 88
  • 121
  • 130
    So... you could say that double negative is a no-no? ;) – Patsuan Jun 08 '17 at 15:20
  • 8
    @Patsuan, very clever. I like that. :) – David Arno Jun 08 '17 at 15:24
  • 5
    I feel like not many people would agree about how if-else with returns should be handled, and some might even say it shouldn't be handled the same in every scenario. I don't have a strong preference myself, but I can definitely see the argument behind many other ways of doing it. – Bernhard Barker Jun 08 '17 at 19:02
  • Regarding taking longer to type, he might use a template in his editor/IDE to create the `if/else` block in one keystroke. – Barmar Jun 08 '17 at 21:03
  • 7
    Clearly, `if (!doorNotOpen)` is awful. So names should be as positive as possible. `if (! doorClosed)` is perfectly fine. – David Schwartz Jun 09 '17 at 21:16
  • I'd argue having an `else` would be much clearer in the first example. Why should I have to read the contents of the `if` block to see whether it falls through or not? Having an explicit `else` clearly indicates there are two ways of handling things, returning or not. An implicit `else` leaves room for confusion. – crizzis Jun 13 '17 at 10:10
  • 2
    With regards to your door example, the two example names are not necessarily equivalent. In the physical world `doorNotOpen` is not the same as `doorClosed` as there is a transitional state between being open and closed. I see this all the time in my industrial applications were boolean states are determined by physical sensors. If you have a single sensor on open side of the door then you can only say the door is open or not open. Likewise if the sensor is on the closed side, you can only say closed or not closed. Also the sensor itself determines how the logic state is affirmed. – Peter M Jun 13 '17 at 14:48
  • *So... you could say that double negative is a no-no?* Well, no, not always; but yes, it is not encouraged by most, but apparently it is true, and I did not know this, that not putting anything in an if block is a good thing. So ... yes. – radarbob Oct 05 '17 at 22:21
49

1. An argument in favor of empty else statements.

I oftentimes use (and argue for) something akin to that first construct, an empty else. It signals to readers of the code (both human and automated analysis tools) that the programmer has put some thought into the situation. Missing else statements that should have been present have killed people, crashed vehicles, and cost millions of dollars. MISRA-C, for example, mandates at least a comment saying that the missing final else is intentional in an if (condition_1) {do_this;} else if (condition_2) {do_that;} ... else if (condition_n) {do_something_else;} sequence. Other high reliability standards go even further: with a few exceptions, missing else statements are forbidden.

One exception is a simple comment, something along the lines of /* Else not required */. This signals the same intent as does the three line empty else. Another exception where that empty else is not needed is where it's blatantly obvious to both readers of the code and to automated analysis tools that that empty else superfluous. For example, if (condition) { do_stuff; return; } Similarly, an empty else is not needed in the case of throw something or goto some_label1 in lieu of the return.

2. An argument for preferring if (condition) over if (!condition).

This is a human factors item. Complex boolean logic trips many people up. Even a seasoned programmer will have to think about if (!(complex || (boolean && condition))) do_that; else do_this;. At a minimum, rewrite this as if (complex || (boolean && condition)) do_this; else do_that;.

3. This does not mean one should prefer empty then statements.

The second section says "prefer" rather than "thou shalt". It is a guideline rather than a rule. The reason for that guideline to prefer positive if conditions is that code should be clear and obvious. An empty then clause (e.g., if (condition) ; else do_something;) violates this. It is obfuscated programming, causing even the most seasoned of programmers to back up and re-read the if condition in its negated form. So write it in the negated form in the first place and omit the else statement (or have an empty else or comment to that effect if mandated to do so).



1I wrote that then clauses that end with return, throw or goto do not require an empty else. It's obvious that the else clause is not needed. But what about goto? As an aside, safety-critical programming rules sometimes disallow early return, and almost always disallows throwing exceptions. They do however allow goto in a restricted form (e.g., goto cleanup1;). This restricted use of goto is the preferred practice in some places. The Linux kernel, for example, is chockfull of such goto statements.

David Hammen
  • 8,194
  • 28
  • 37
  • 6
    `!` also is easily overlooked. It is too terse. – Thorbjørn Ravn Andersen Jun 10 '17 at 11:13
  • I like this answer. The reality is that hard and fast style rules usually end up making the code harder to read in at least some cases. While I do not think the empty "else" and forced positive conditions enhance readability very much, there are situations where I could see a style guide enforcing these rules within a codebase. High reliability code (think NASA software) will often enforce draconian rules in an effort to prevent developers from making silly mistakes. Example: http://sdtimes.com/nasas-10-rules-developing-safety-critical-code/ – Kyle A Jun 10 '17 at 13:44
  • 4
    No, an empty else doesn't make it clearer that the programmer has put some thought in it. It leads to more confusion "Were there some statements planned and they forgot or why is there an empty clause?" A comment is much clearer. – Simon Jun 10 '17 at 20:26
  • 11
    @Simon: A typical form is `else { /* Intentionally empty */ }`, or something along those lines. The empty else satisfies the static analyzer that mindlessly looks for rules violations. The comment informs readers that the empty else is intentional. Then again, seasoned programmers typically omit the comment as unnecessary -- but not the empty else. High reliability programming is a domain of its own. The constructs look rather strange to outsiders. These constructs are used because of lessons from the school of hard knocks, knocks that are very hard when life & death or $$$$$$$$$ are at hand. – David Hammen Jun 11 '17 at 02:28
  • 2
    I don't understand how empty then clauses are a bad thing when empty else clauses are not (assuming we choose that style). I can see `if (target == source) then /* we need to do nothing */ else updateTarget(source)` – Bergi Jun 11 '17 at 17:05
  • @Bergi - Because, as Arseni Mourzenko put it so nicely, "it is not false that testing for a closed door is more intuitive than testing for a non-opened door." – David Hammen Jun 11 '17 at 20:33
  • @DavidHammen Yes, but in some cases it's the closed door where I need to do nothing, and testing for the opened door would be more arcane :-) – Bergi Jun 11 '17 at 22:53
  • @ThorbjørnRavnAndersen in some languages like C++ you can replace `!` with `not` so that `if(!done)` becomes `if(not done)`. Some people are very against using it though. – Ruslan Jun 12 '17 at 11:22
  • @Ruslan Sounds like something you have to do with a preprocessor. That is usually not a good idea. – Thorbjørn Ravn Andersen Jun 12 '17 at 14:21
  • 2
    @ThorbjørnRavnAndersen nope, `not` is a keyword in C++, as are other bitwise and logical operators. See [alternative operator representations](http://en.cppreference.com/w/cpp/language/operator_alternative). – Ruslan Jun 12 '17 at 14:40
  • As a long-time maintenance programmer, I disagree with the first assertion. If I see something like that, my first guess is that there *used* to be code in there, but someone came along afterwards and sloppily removed it. The only *decent* logic I've seen for doing that is that in Cish languages the fact that the branches don't *require* curly braces is so dangerous to readability that protective measures are in order. My response is that if you really care that much about C's crappy syntax being dangerous, you should be using Ada. – T.E.D. Jun 12 '17 at 17:15
  • @T.E.D. - If you were a flight software maintenance programmer and you saw a missing else with no comment regarding why, your first guess most likely be to wonder about the overall quality of said flight software. In many places that write flight software, it's a courtesy (but not necessarily mandatory) to tell future maintenance programmers in a comment that the empty else is intentionally empty. That empty else: It's mandatory. With regard to braces: They're mandatory, and that's easily checked with a static analyzer. (So are missing else statements.) ... – David Hammen Jun 12 '17 at 19:33
  • ... With regard to Ada, that's pretty much a dead language, at least in the US. While there are existing projects (e.g., the International Space Station flight software) that use Ada, new projects almost inevitably tend to use a mix of C, C++, autocoded C or C++, and perhaps a tiny, tiny bit of assembly for low-level functionality. This is at least the case for the flight software for every new space vehicle with which I am familiar, and I'm familiar with quite a few of them. – David Hammen Jun 12 '17 at 20:00
  • @DavidHammen - I think you just shorted out my brain. I'm not saying you're *wrong*, but the concept that all optional syntax in a language is expected to be used is just a really alien way of thinking to me. I suspect there are implications to that which it would take some time to digest. I've had exposure to a lot of real-time avionics code in my career, but I think it was all in either Ada or Fortran. – T.E.D. Jun 12 '17 at 20:03
  • @DavidHammen - I don't come across a lot of shiny new Ada, but I'm in a business where code is expected to last a long time (like Human generations), so Ada and Fortran are commonly encountered. I'd be careful calling any programming language "dead". If there are still working compilers floating around, at best you can call it [moribund](https://en.wikipedia.org/wiki/Endangered_language). Not a lot of programming languages are truly "dead". Perhaps [Draco](https://en.wikipedia.org/wiki/Draco_(programming_language)) and [CMS-2](https://en.wikipedia.org/wiki/CMS-2_(programming_language)). – T.E.D. Jun 12 '17 at 20:09
  • @T.E.D. - Yes, they're not quite dead. Then again, I've yet to come across someone writing new FORTRAN-IV code, or new Ada code (any variant) on a shiny new project. Dead isn't quite the right word. Perhaps Norwegian Blue? (And yes, I do know that when I do `import numpy` in python, I'm essentially getting some FORTRAN-IV code that except for bug fixes and perhaps for an automated pass through `f2c` remains unaltered since the mid 1960s, or perhaps earlier.) – David Hammen Jun 12 '17 at 22:07
  • If you are a Python programmer, then in your case I'll allow that they are pinin' for the fjords. – T.E.D. Jun 12 '17 at 22:26
  • "*Missing else statements that should have been present have killed people, crashed vehicles, and cost millions of dollars*" - any public reports / discussions you can name or link to? (Curiosity, rather than 'citation needed') – TessellatingHeckler Jun 13 '17 at 00:21
36

I use an empty else-branch (and sometimes an empty if-branch) in very rare cases: When it is obvious that both the if and else part should be handled somehow, but for some non-trivial reason the case can be handled by doing nothing. And therefore anyone reading the code with the else action needed would immediately suspect that something is missing and waste their time.

if (condition) {
    // No action needed because ...
} else {
    do_else_action()
}

if (condition) {
    do_if_action()
} else {
    // No action needed because ...
}

But not:

if (condition) {
    do_if_action()
} else {
    // I was told that an if always should have an else ...
}
gnasher729
  • 42,090
  • 4
  • 59
  • 119
  • 5
    This. I find the statement `if test succeeds -> no action needed -> else -> action needed` to be semantically very different to the statement `if it applies that the opposite of a test outcome is true then an action is needed`. I'm reminded of Martin Fowler's quote: "anyone can write code computers can understand; good programmers write code humans can understand". – Tasos Papastylianou Jun 10 '17 at 15:17
  • 2
    @TasosPapastylianou That's cheating. The opposite of 'if test succeeds -> no action needed' is 'if test does not succeed -> action needed'. You padded it with about 10 words. – Jerry B Jun 11 '17 at 17:55
  • @JerryB it depends on the "test". Sometimes negation is (linguistically) straightforward, but sometimes it isn't, and would lead to confusion. In those cases, it's clearer to talk about "negation / opposite of the outcome being true" vs "outcome not being true"; however the point is that it would be even clearer to introduce an 'empty' step, or invert the meaning of the variable names. This is typical in "de morgan" situations: e.g. `if ((missing || corrupt) && !backup) -> skip -> else do stuff` is far clearer (+different implied intent) than `if ((!missing && !corrupt) || backup) -> do stuff` – Tasos Papastylianou Jun 12 '17 at 00:58
  • 1
    @TasosPapastylianou You could write `if(!((missing || corrupt) && !backup)) -> do stuff` or better `if((aviable && valid) || backup) -> do stuff`. (But in a real example you must check if the backup is valid before use it) – 12431234123412341234123 Jun 12 '17 at 12:34
  • @TasosPapastylianou is "if not missing and not corrupted, or there is a backup, do stuff" that unclear? – Baldrickk Jun 12 '17 at 14:24
  • @Baldrickk the point is not one of 'clarity'. I'm not claiming that kind of formulation is _undecipherable_. I'm claiming it is denser to read and breaks the flow of the code, as it forces the reader to backtrack and confirm the meaning of all the implied double negatives; the reader will mentally have to convert it to something like gnasher's formulation (or 1243123's second example) inside their heads to make sense of it anyway, so why not frame it like that in the first place? – Tasos Papastylianou Jun 12 '17 at 14:45
  • @12431234123412341234123 I believe the first version in your comment suffers from the same problem as my comment to Baldrickk; I agree the second is as good as the original formulation and reads well, but I would say it doesn't necessarily flow _better_ than the original, and it comes at the cost of having to redefine two variables / functions. But yes, my choice would be between that and gnasher's original formulation,but _not_ simply transforming into a valid double negative just to save one line of comment. (gnasher: apologies for hijacking your answer with my ramblings, I'll stop now :) ) – Tasos Papastylianou Jun 12 '17 at 14:48
  • 2
    @TasosPapastylianou where is there double negatives in what I said? Sure, if you were using _uncorrupted_ as a variable name then you would have a clear point. Though when you come to mixed boolean operators like this, it _may_ be better to have temporary variables to use in the `if`: `file_ok = !missing && !corrupt; if(file_ok || backup) do_stuff();` which I personally feel makes it even clearer. – Baldrickk Jun 12 '17 at 16:18
23

All else being equal, prefer brevity.

What you don't write, nobody has to read and understand.

While being explicit can be useful, that's only the case if it makes obvious without undue verbosity that what you wrote is really what you wanted to write.


So, avoid empty branches, they are not only useless but also uncommon and thus lead to confusion.

Also, avoid writing an else-branch if you exit directly out of the if-branch.

A useful application of explicitness would be putting a comment whenever you fall through switch-cases // FALLTHRU, and using a comment or an empty block where you need an empty statement for(a;b;c) /**/;.

Deduplicator
  • 8,591
  • 5
  • 31
  • 50
  • 7
    `// FALLTHRU` Ugh, not in SCREAMING CAPS or with txtspk abbrevs. Otherwise, I agree and follow this practice myself. – Cody Gray - on strike Jun 09 '17 at 07:11
  • @CodyGray Well, I think at least one static analysis tool wants a comment looking just like that. True, it's a bit ugly, but if it helps with the tooling... – Deduplicator Jun 09 '17 at 08:26
  • 7
    @CodyGray -- This is one place where SHOUTING is a good idea. Most switch statements end each case with `break`. Failure to have that `break` has led to some spectacular software failures. A comment that shouts to readers of the code that the fall-through is intentional is a good thing. That static analysis tools can look for this specific pattern and not complain about a fall through situation makes it an even better thing. – David Hammen Jun 09 '17 at 09:03
  • @CodyGray, Some code editors (vim being one that I happen to know) will automatically give special highlighting to certain 'screamed' comment keywords. For example: in a C code file, the comment `//JS 20170521 TODO: add support for xyz` the word TODO is shown in inverted colours and literally leaps out at you, even when rapidly scrolling through a file. One could easily add "todo" and "fallthru" to add high-visibility highlighting. I'm sure other editors are similarly configurable. – Wossname Jun 09 '17 at 09:11
  • @DavidHammen Having a comment about the fallthrough seems different from having an empty `else` block, but I'm having trouble articulating the difference. How would you define the difference? – Zev Spitz Jun 09 '17 at 10:54
  • @ZevSpitz -- I occasionally work in environments where an empty else (or a comment to that effect) is mandated, for much the same reason that a comment about fall through is mandated. I wrote an answer to that effect. – David Hammen Jun 09 '17 at 10:58
  • @DavidHammen. I end non-breaking `switch` statements with an explicit `// no break`. Makes sense to me. – TRiG Jun 09 '17 at 11:35
  • heh, "all `else` being equal" I see what you did there – HotelCalifornia Jun 09 '17 at 18:12
  • 1
    @Wossname: I just checked my `vim`, and it doesn't recognize any variation of `FALLTHRU`. And I think, for good reason: `TODO` and `FIXME` comments are comments that need to be grepable because you want to be able to ask your `git grep` which known defects you have in your code, and where they are located. A fallthrough is not a defect, and there is no reason to grep for it, what so ever. – cmaster - reinstate monica Jun 11 '17 at 01:02
  • 4
    @DavidHammen No, shouting is not a good idea: If you have a //fallthrough in place of an expected break;, that should be enough for any developer to understand immediately. Also, the //fallthrough does not talk about a defect, it simply signals that the circumstances have been considered, and that the break; which appears to be missing, is indeed intended to not be there. This is a purely informational purpose, and nothing to shout about. – cmaster - reinstate monica Jun 11 '17 at 15:17
  • @cmaster, I didn't say vim highlighted "//fallthru" by default. I said it *could* be configured to do so should the need arise. – Wossname Jun 11 '17 at 16:14
  • 1
    @cmaster -- That shouting is not a good idea is **your** opinion. Other people's opinions differ. And just because **your** configuration of vim doesn't recognize `FALLTHRU` doesn't mean that other people haven't configured vim to do so. – David Hammen Jun 11 '17 at 16:50
6

There is no hard and fast rule about positive or negative conditions for an IF statement, not to my knowledge. I personally prefer coding for a positive case rather than a negative, where applicable. I most certainly will not do this though, if it leads me to make an empty IF block, followed by an ELSE full of logic. If such a situation arose, it would take like 3 seconds to refactor it back to testing for a positive case anyway.

What I really don't like about your examples though, is the completely unnecessary vertical space taken up by the blank ELSE. There is quite simply no reason whatsoever to do this. It doesn't add anything to the logic, it doesn't help document what the code is doing, and it doesn't increase readability at all. In fact, I would argue that the added vertical space could possibly decrease readability.

Langecrew
  • 142
  • 5
  • 2
    That unnecessary vertical space is a consequence of mandates for always using braces, for disallowing missing else, and for using Allman style for indentation. – David Hammen Jun 09 '17 at 11:03
  • Well, I would think that always using braces is a good thing, because it makes a developers intentions explicit - no room for guesswork when reading over their code. That might sound silly, but trust me, especially when mentoring junior developers, mistakes related to missing braces do happen. I've personally never been a fan of Allman style indentation, for exactly the reason you mention: unnecessary vertical space. But that's just my opinion and personal style. It's just what I learned initially, so it has always felt more comfortable to read. – Langecrew Jun 09 '17 at 13:44
  • Personal style: regardless of which brace style I use (Allman, 1TB, ...), I always use braces. – David Hammen Jun 09 '17 at 14:31
  • Complex conditionals or ones you think might be difficult to understand can almost always be solved by being re-factored into their own methods/functions. Something like this `if(hasWriteAccess(user,file))` could be complex underneath, but at a glance you know exactly what the result should be. Even if it's just a couple conditions, e.g. `if(user.hasPermission() && !file.isLocked())` a method call with an appropriate name gets the point across, so negatives become less of an issue. – Chris Schneider Jun 09 '17 at 19:47
  • Agreed. Then again, I'm a big fan of refactoring whenever possible :) – Langecrew Jun 09 '17 at 19:54
5

Explicit else block

I disagree with this as a blanket statement covering all if statements but there are times when adding an else block out of habit is a good thing.

An if statement, to my mind, actually covers two distinct functions.

If we are supposed to do something, do it here.

Stuff like this obviously does not need an else part.

    if (customer.hasCataracts()) {
        appointmentSuggestions.add(new CataractAppointment(customer));
    }
    if (customer.isDiabetic()) {
        customer.assignNurse(DiabeticNurses.pickBestFor(customer));
    }

and in some cases insisting on adding an else might mislead.

    if (k > n) {
        return BigInteger.ZERO;
    }
    if (k <= 0 || k == n) {
        return BigInteger.ONE;
    }

is not the same as

    if (k > n) {
        return BigInteger.ZERO;
    } else {
        if (k <= 0 || k == n) {
            return BigInteger.ONE;
        }
    }

even though it is functionally the same. Writing the first if with an empty else may lead you to the second result which is unnecessarily ugly.

If we are checking for a specific state, it is often a good idea to add an empty else just to remind you to cover that eventuality

        // Count wins/losses.
        if (doors[firstChoice] == Prize.Car) {
            // We would have won without switching!
            winWhenNotSwitched += 1;
        } else {
            // We win if we switched to the car!
            if (doors[secondChoice] == Prize.Car) {
                // We picked right!
                winWhenSwitched += 1;
            } else {
                // Bad choice.
                lost += 1;
            }
        }

Remember that these rules apply only when you are writing new code. IMHO The empty else clauses should be removed before checkin.


Test for true, not for false

Again this is good advice at a general level but in many cases this makes code unnecessarily complex and less readable.

Even though code like

    if(!customer.canBuyAlcohol()) {
        // ...
    }

is jarring to the reader, but making it

    if(customer.canBuyAlcohol()) {
        // Do nothing.
    } else {
        // ...
    }

is at least as bad, if not worse.

I coded in BCPL many years ago and in that language there is an IF clause and an UNLESS clause so you could code much more readably as:

    unless(customer.canBuyAlcohol()) {
        // ...
    }

which is significantly better, but still not perfect.


My personal process

Generally, when I am writing new code I will often add an empty else block to an if statement just to remind me that I have not yet covered that eventuality. This helps me avoid the DFS trap and ensures that when I review the code I notice that there is more to do. However, I usually add a TODO comment to keep track.

  if (returnVal == JFileChooser.APPROVE_OPTION) {
    handleFileChosen();
  } else {
    // TODO: Handle case where they pressed Cancel.
  }

I do find that generally I use else rarely in my code as it can often indicate a code smell.

Deduplicator
  • 8,591
  • 5
  • 31
  • 50
OldCurmudgeon
  • 778
  • 5
  • 11
  • Your handling of "empty" blocks reads like standard stubbing out code when implementing thngs... – Deduplicator Jun 09 '17 at 10:05
  • The `unless` block is a good suggestion, and hardly confined to BCPL. – tchrist Jun 11 '17 at 02:29
  • @TobySpeight Oops. It somehow escaped my attention that what I've written as `...` was actually `return`. (In fact, with `k=-1`, `n=-2`, the first return is taken but this is largely moot.) – David Richerby Jun 12 '17 at 09:12
  • Modern Perl has `if` and `unless`. What's more, it also allows these after a statement, so you can say `print "Hello world!" if $born;` or `print "Hello world!" unless $dead;` and both will do exactly what you think they do. – user Jun 12 '17 at 12:49
1

For the first point, I have used a language that forced IF statements to be used this way (in Opal, the language behind a mainframe screen scraper for putting a GUI front end on to mainframe systems), and with only one line for the IF and the ELSE. It wasn't a pleasant experience!

I would expect any compiler to optimise out such additional ELSE clauses. But for live code it is not adding anything (in development it can be a useful marker for further code).

A time I do use something like these extra clauses is when using CASE / WHEN type processing. I always add a default clause even if it is empty. This is long term habit from languages that will error if such a clause is not used, and forces a thought as to whether things really should just drop through.

Long ago mainframe (eg, PL/1 and COBOL) practice it was accepted that negative checks were less efficient. This could explain the 2nd point, although these days there are massively more important efficiency savings which are ignored as micro optimisations.

Negative logic does tend to be less readable, although not so much on such a simple IF statement.

Kickstart
  • 119
  • 4
  • 2
    I see, so it _was_ common practice at some point. – Patsuan Jun 09 '17 at 12:06
  • @Patsuan - yes, to an extent. Although that was when mainframe assembler was a serious alternative for performance critical code. – Kickstart Jun 09 '17 at 14:30
  • 1
    "Long ago... it was accepted that negative checks were less efficient." Well, they _are_ unless the compiler optimizes `if !A then B; else C` to `if A then C; else B`. Computing the negation of the condition is an extra CPU instruction. (Though as you say, this is unlikely to be _significantly_ less efficient.) – David Richerby Jun 12 '17 at 10:03
  • @DavidRicherby And if we are talking in general, some languages can make such optimizations really hard. Take C++ with overloaded `!` and `==` operators, for example. Not that anyone should *actually do that*, mind you... – user Jun 12 '17 at 12:52
1

I would second the stand of most answers that empty else blocks are virtually always a harmful waste of electronic ink. Don't add these unless you have a very good reason to do so, in which case the empty block should not be empty at all, it should contain a comment explaining why it's there.

The issue about avoiding negatives deserves some more attention though: Typically, whenever you need to use a boolean value, you need some blocks of code to operate when it's set, and some other blocks to operate when it's not set. As such, if you enforce a no-negatives rule, you enforce either having if() {} else {...} statements (with an empty if block!), or you create a second boolean for each boolean that contains its negated value. Both options are bad, as they confuse your readers.

A helpful policy is this: Never use a negated form within a boolean's name, and express negation as a single !. A statement like if(!doorLocked) is perfectly clear, a statement like if(!doorUnlocked) knots brains. The later type of expression is the thing you need to avoid at all cost, not the presence of a single ! in an if() condition.

1

There is one point when considering the "always have an else clause" argument that I haven't seen in any other answer: it can make sense in a functional programming style. Sort of.

In a functional programming style, you deal in expressions rather than statements. So every code block has a return value - including an if-then-else expression. That would, however, preclude an empty else block. Let me give your an example:

var even = if (n % 2 == 0) {
  return "even";
} else {
  return "odd";
}

Now, in languages with C style or C style inspired syntax (such as Java, C# and JavaScript, just to name a few) this looks weird. However it looks much more familiar when written as such:

var even = (n % 2 == 0) ? "even" : "odd";

Leaving the else branch empty here would cause a value to be undefined - in most cases, not what we want to be a valid case when programming functionality. Same with leaving it out completely. However, when you're programming iteratively I set very little reason to always have an else block.

blalasaadri
  • 187
  • 6
0

I would say it's definitely bad practice. Adding else statements is going to add a whole bunch of pointless lines to your code that do nothing and make it even more difficult to read.

  • 1
    I hear you have never worked for an employer who grades your performance based on SLOCs produced per period... – user Jun 12 '17 at 12:53
0

There's another pattern that hasn't been mentioned yet about handling the second case that has an empty if-block. One way to deal with it would be to return in the if block. Like this:

void foo()
{
    if (condition) return;

    doStuff();
    ...
}

This is a common pattern for checking error conditions. The affirmative case is still used, and the inside of it is no longer empty leaving future programmers to wonder if a no-op was intentional or not. It can also improve readability since you might be required to make a new function (thus breaking large functions into smaller individual functions).

Shaz
  • 2,614
  • 1
  • 12
  • 14
0

...an if statement should be followed by an else statement, whether there is something to put into it or not.

I disagree if (a) { } else { b(); } should be re-written as if (!a) { b(); } and if (a) { b(); } else { } should be re-written as if (a) { b(); }.

However, it's worth pointing out that I rarely have an empty branch. This is because normally I log that I went into the otherwise empty branch. This way I can develop purely off of log messages. I rarely use a debugger. In production environments you don't get a debugger; it's nice to be able to troubleshoot production issues with the same tools that you use to develop.

Secondly, it's better to test for true values rather than false. That means that it's better to test a 'doorClosed' variable instead of a '!doorOpened' variable

I have mixed feelings about this. A disadvantage to having doorClosed and doorOpened is it potentially doubles the number of words/terms you need to be aware of. Another disadvantage is over time the meaning of doorClosed and doorOpened may change (other developers coming in after you) and you might end up with two properties that are no longer precise negations of each other. Instead of avoiding negations, I value adapting the language of the code (class names, variable names, etc.) to the business users' vocabulary and to the requirements I'm given. I wouldn't want to make up a whole new term to avoid a ! if that term only has meaning to a developer that business users won't understand. I want developers to speak the same language as the users and the people writing requirements. Now if new terms simplify the model, then that's important, but that should be handled before the requirements are finalized, not after. Development should handle this problem before they start coding.

I am prone to doubt my instinct.

It's good to continue to question yourself.

Is it a good/bad/"doesn't matter" practice?

To some degree a lot of this doesn't matter. Get your code reviewed and adapt to your team. That's usually right thing to do. If everyone on your team wants to code a certain way, it's probably best to do it that way (changing 1 person - yourself - takes less story points than changing a group of people).

Is it common practice?

I can't remember ever seeing an empty branch. I do see people adding properties to avoid negations, though.

0

I'm only going to address the second part of the question and this comes from my point of view of working in industrial systems and manipulating devices in the real world.

However this is in someways an extended comment rather than an answer.

When it is stated

Secondly, it's better to test for true values rather than false. That means that it's better to test a 'doorClosed' variable instead of a '!doorOpened' variable

His argument is that it makes clearer what the code is doing.

From my point of view the assumption is being made here that the door state is a binary state when in fact it is at least a ternary state:

  1. The door is open.
  2. The door is neither fully open nor fully closed.
  3. The door is closed.

Thus depending on where a single, binary digital sensor is located you can only surmise one of two concepts:

  1. If the sensor is on the open side of the door: The door is either open or not open
  2. If the sensor is on the closed side of the door: The door is either closed or not closed.

And you can not interchange these concepts through the use of a binary negation. Thus doorClosed and !doorOpened are likely not synonymous and any attempt to pretend that they are synonymous is erroneous thinking that assumes a greater knowledge of system state than actually exists.

In coming back to your question I would support verbiage that matches the origin of the information that the variable represents. Thus if the information is derived from the closed side of the door then go with doorClosed etc. This may imply using either doorClosed or !doorClosed as needed in various statements as required, resulting in potential confusion with the use of the negation. But what it does not do is implicitly propagate assumptions on the state of the system.


For discussion purposes for the work I do, the amount of information I have available about the state of the system depends on the functionality required by the overall system itself.

Sometimes I only need to know if the door is closed or not closed. In which case only a single binary sensor will suffice. But at other times I do need to know that the door is open, closed or in transition. In such cases there would either be two binary sensors (at each extreme of door motion - with error checking against the door being simultaneously open and closed) or there would be an analogue sensor measuring how 'open' the door was.

An example of the first would be the door on a microwave oven. You enable the operation of the oven based on the door being closed or not closed. You do not care how open the door is.

An example of the second would be a simple motor driven actuator. You inhibit driving the motor forward when the actuator is fully out. And you inhibit driving the motor in reverse when the actuator is fully in.

Fundamentally the number and type of sensors comes down to running a cost analysis of the sensors against a requirements analysis of what is needed to achieve the required functionality.

Peter M
  • 2,029
  • 2
  • 17
  • 22
  • *"From my point of view the assumption is being made here that the door state is a binary state when in fact it is at least a ternary state:"* and *"You enable the operation of the oven based on the door being closed or not closed. You do not care how open the door is."* contradict each other. Also, I don't think the question is about doors and sensors at all. – Sanchises Jun 14 '17 at 08:41
  • @Sanchises The statements are not contradictory as you have taken them out of context. The first statements is in the context that two opposing binary measurements of a ternary state can not be interchanged via binary negation. The second statement is in the context that only partial knowledge of a ternary state may be sufficient for an application to correctly work. As for the doors, the OPs question referenced doors being open and closed. I am just pointing out an obvious assumption that can influence the how data is treated. – Peter M Jun 14 '17 at 11:21
-1

Concrete style rules are always bad. Guidelines are good, but in the end there's often cases where you can make stuff clearer by doing something that doesn't quite follow the guidelines.

That said, there's a lot of hate for the empty else "it wastes lines" "extra typing", that are just bad. You could make the argument for moving the brackets onto the same line if you really need the vertical space, but if that's an issue you shouldn't be putting your { on a separate line anyway.

As mentioned elsewhere, having the else block is very useful to show that you explicitly want nothing to happen in the other case. After doing a lot of functional programming (where else is required), I've learned that you should always be considering the else when writing the if, although as @OldCurmudgeon mentions there are really two different use cases. One should have an else, one shouldn't. Sadly it's not something you can always tell at a glance, let alone with a linter, hence the dogmatic 'always put an else block'.

As for the 'no negatives', again, absolute rules are bad. Having an empty if can be weird, especially if it's the type of if that doesn't need an else, so writing all that rather than a ! or an ==false is bad. That said, there are a lot of cases where the negative makes sense. A common example would be caching a value:

static var cached = null

func getCached() {
    if !cached {
        cached = (some calculation, etc)
    }

    return cached
}

if the actual (mental/english) logic involves a negative, so should the if statement.

zacaj
  • 185
  • 1
  • 1
  • 4