My professor keeps referring to this Java example when he speaks of "robust" code:
if (var == true) {
...
} else if (var == false) {
...
} else {
...
}
He claims that "robust code" means that your program takes into account all possibilities, and that there is no such thing as an error - all situations are handled by the code and result in valid state, hence the "else".
I am doubtful, however. If the variable is a boolean, what is the point of checking a third state when a third state is logically impossible?
"Having no such thing as an error" seems ridiculous as well; even Google applications show errors directly to the user instead of swallowing them up silently or somehow considering them as valid state. And it's good - I like knowing when something goes wrong. And it seems quite the claim to say an application would never have any errors.
So what is the actual definition of "robust code"?
-
14thedailywtf.com/Articles/What_Is_Truth_0x3f_.aspxJonathan Merlet– Jonathan Merlet2011年08月26日 22:48:20 +00:00Commented Aug 26, 2011 at 22:48
-
6This would only hold in a not-strongly-typed language. In a strongly typed language a variable of type boolean (not some integer posing as a boolean), can only be true or false, there is no third option...Marjan Venema– Marjan Venema2011年08月27日 08:38:29 +00:00Commented Aug 27, 2011 at 8:38
-
23ask him how would you test coverage on the 3rd case, because robust code should surely require testing, and if you don't manage to test the 3rd case, you wouldn't be able to find any bugs that might lurk in there.gbjbaanb– gbjbaanb2011年08月27日 13:53:57 +00:00Commented Aug 27, 2011 at 13:53
-
2@Marjan - in a not-strongly-typed language one would most likely just write: if (var) { } else { }kevin cline– kevin cline2011年08月27日 14:16:21 +00:00Commented Aug 27, 2011 at 14:16
-
2I do not know of any languages where both x and !x could be true. Note that I didn't suggest "if (x == true) ..."; I abhor such comparisons.kevin cline– kevin cline2011年08月27日 22:39:01 +00:00Commented Aug 27, 2011 at 22:39
10 Answers 10
what is the point of checking a third state when a third state is logically impossible?
What about a Boolean?
that allows for a NULL
state that is neither true nor false. Now what should the software do? Some software has to be highly crash-resistant like pacemakers. Ever seen someone add a column to a database that was a Boolean
and initialize the current data to NULL
initially? I know I've seen it.
Here are a few links that discuss what it means to be robust in terms of software:
- Robust Programming
- Robust Definition
- Robustness, the forgotten code quality.
- How to write robust code
If you think there is one universally agreed upon definition of "robust" here, good luck. There can be some synonyms like bomb-proof or idiot-proof. The Duct Tape Programmer would be an example of someone that usually writes robust code at least in my understanding of the terms.
-
14If this was a nullable boolean both Java and c# would throw so null should be checked first.Esben Skov Pedersen– Esben Skov Pedersen2015年05月16日 20:00:12 +00:00Commented May 16, 2015 at 20:00
-
There doesn't seem to be any universally agreed upon definition of what a cat or a dog are.Tulains Córdova– Tulains Córdova2017年07月04日 00:24:27 +00:00Commented Jul 4, 2017 at 0:24
-
Swift would say nil is not true, and nil is not false either. (You can compare optionals or optional and non-optional, and nil == nil, nil != anything else, anything else != nil).gnasher729– gnasher7292023年05月19日 00:19:41 +00:00Commented May 19, 2023 at 0:19
For the sake of my discussion a Bool can have 2 states, True or False. Anything else is non-conformance to the programming langugae specification. If your tool chain is non-conformant to its specification, you are hosed no matter what you do. If a developer created a type of Bool that had more than 2 states, it's the last thing he would ever do on my codebase.
Option A.
if (var == true) {
...
} else if (var == false) {
...
} else {
...
}
Option B
if (var == true) {
...
} else {
...
}
I assert Option B is more robust.....
Any twit can tell you to handle unexpected errors. They are usually trivally easy to detect once you think of them. The example your professior has given is not something that could happen, so it's a very poor example.
A is impossible to test without convoluted test harnesses. If you can't create it, how are you going to test it? If you have not tested the code, how do you know it works? If you don't know it works, then you are not writing robust software. I think they still call that a Catch22 (Great movie, watch it sometime).
Option B is trivial to test.
Next problem, ask you professor this question "What do you want me to do it about it if a Boolean is neither True nor False?" That should lead into an a very interesting discussion.....
Most cases, a core dump is approriate, at worst it annoys the user or costs a lot of money. What if, say, the module is the Space shuttle realtime reentry calculation system? Any answer, no matter how inaccurate, cannot be worse than aborting, which will kill the users. So what to do, if you know the answer might be wrong, go for the 50/50, or abort and go fo the 100% failure. If I was a crew member, I'd take the 50/50.
Option A kills me Option B gives me an even chance of survival.
But wait - it's a simulation of the space shuttle reentry - then what? Abort so you know about it. Sound like a good idea? - NOT - because you need to test with the code you plan to ship.
Option A is better for simluation, but can't be deployed. It's useless Option B is the deployed code so the simulation performs the same as the live systems.
Let's say this was a valid concern. The better solution would be to isolate the error handling from the application logic.
if (var != true || var != false) {
errorReport("Hell just froze over, var must be true or false")
}
......
if (var == true){
....
} else {
....
}
Futher reading - Therac-25 Xray machine, Ariane 5 Rocket failure and others (Link has many broken links but enough info that Google will help)
-
2"..unexpected errors. They are usually trivally easy to detect once you think of them" - but when you think of them, they're no longer unexpected.gbjbaanb– gbjbaanb2011年08月27日 13:51:59 +00:00Commented Aug 27, 2011 at 13:51
-
7There is some question as to if your code
if (var != true || var != false) {
should be an&&
instead.user40980– user409802014年07月07日 20:50:20 +00:00Commented Jul 7, 2014 at 20:50 -
1I can easily think of a bool that is neither true or false, but it's still unexpected. In case you say a bool cannot be anything else, if I check whether a letter is a digit character and then convert it to its integer value, I can easily think of that integer value being less than 0 or greater than 9, but it's still unexpected.gnasher729– gnasher7292015年05月16日 19:52:25 +00:00Commented May 16, 2015 at 19:52
-
2Null Booleans are supported in Java and C#, and have a real-world application. Consider a database containing a list of people. After a while you decide you need a gender (isMale) field. Null means "never asked so don't know"; true means male and false means female. (OK, trans-gender omitted for simplicity...).kiwiron– kiwiron2015年05月17日 08:55:15 +00:00Commented May 17, 2015 at 8:55
-
@kiwiron: Would a better solution not be to use a enumeration type, "Male", "Female", "Did Not Ask". Enumerations are better - can be extended when the need arises ( in your e.g. Asexual, Hermaphrodite, "Refused to Answer" come to mind).mattnz– mattnz2015年05月17日 09:48:25 +00:00Commented May 17, 2015 at 9:48
Actually your code is not more robust but LESS robust. The final else
is simply dead code that you can't test.
In critical software such as in spacecrafts, dead code and more generally untested code is forbidden: If a cosmic ray produces a single event upset that in turn makes your dead code being activated, anything is possible. If the SEU activates a portion of robust code, the (unexpected) behaviour stays under control.
-
I dont get it in spacecrafts is dead code forbidden? i.e. you cant write the last else? since you cant test it you cant put it in? but then what does it mean "If the SEU activates a portion of robust code, the (unexpected) behaviour stays under control."shabby– shabby2015年02月10日 09:16:43 +00:00Commented Feb 10, 2015 at 9:16
-
5Yes, in space critical software test coverage must be 100% and as a consequence unreachable code (aka dead code) is forbidden.mouviciel– mouviciel2015年02月10日 09:41:58 +00:00Commented Feb 10, 2015 at 9:41
I think the professor might be confusing "error" and "bug". Robust code should certainly have few/no bugs. Robust code may, and in a hostile environment, must, have good error management (be it exception handling or rigorous return status tests).
I agree that the professor's code example is silly, but not as silly as mine.
// Assign 3 to x
var x = 3;
x = 3; // again, just for sure
while (x < 3 or x > 3) { x = 3; } // being robust
if (x != 3) { ... } // this got to be an error!
-
1The last if certainly be triggered, it wouldn't require really that much effort. Any experienced C programmer have seen values suddenly change. Of course logically, in a controlled single-threaded environment, this should never happen. In real life, the code inside the if will eventually happen. If there's nothing useful you can do inside that if, then don't code it! (I had a funny experience during a particular software development where I raised an exception with curse words in case something impossible happened... guess what happened?).Alex– Alex2016年11月24日 18:03:22 +00:00Commented Nov 24, 2016 at 18:03
-
2True story:
boolean x = something(); if (x) { x = True // make sure it's really true, ... }
Andres F.– Andres F.2016年12月07日 19:21:39 +00:00Commented Dec 7, 2016 at 19:21
There is no agreed upon definition of Robust Code, as for many things in programming it's more or less subjective...
The example your professor gives depends on the language:
- In Haskell, a
Boolean
can be eitherTrue
orFalse
, there is no third option - In C++, a
bool
can betrue
,false
, or (unfortunately) come from some dubious cast that put it in an unknown case... This should not happen, but may, as a result of a previous error.
However, what your professor is advising obscures the code by introducing extraneous logic for should-not-happen events in the middle of the core program, so I will point you, instead, toward Defensive Programming.
In university case, you could even augment it by adopting a Design By Contract strategy:
- Establish invariants for classes (eg,
size
is the number of items in thedata
list) - Establish pre-conditions and post-conditions for each function (e.g., this function may only be invoked with
a
being less than10
) - Test each of those at the entry and exit points of each of your functions
Example:
class List:
def __init__(self, items):
self.__size = len(items)
self.__data = items
def __invariant(self):
assert self.__size == len(self.__data)
def size(self):
self.__invariant()
return self.__size
def at(self, index):
"""index should be in [0,size)"""
self.__invariant()
assert index >= 0 and index < self.__size
return self.__data[index]
def pushback(self, item):
"""the subsequent list is one item longer
the item can be retrieved by self.at(self.size()-1)"""
self.__invariant()
self.__data.append(item)
self.__size += 1
self.__invariant()
assert self.at(self.size()-1) == item
-
But the professor specifically said that it was Java, and specifically did NOT say what the type of var is. If it is Boolean, it can be true, false, or null. If something else, it can be unequal to both true and unequal to false. Yes, overlap between robust, defensive, and paranoid.Andy Canfield– Andy Canfield2011年08月27日 12:48:22 +00:00Commented Aug 27, 2011 at 12:48
-
3In C, C++ and Objective-C, a bool can have an indeterminate value, like any other type, but any assignment will set it to true or false and nothing else. For example: bool b = 0; b++; b++; will set b to true.gnasher729– gnasher7292015年05月16日 19:31:59 +00:00Commented May 16, 2015 at 19:31
-
@gnasher729 not an indeterminate value, accessing an uninitialized value means your whole program's behaviour is undefined, including the parts that happen before the access.Caleth– Caleth2023年05月19日 08:32:49 +00:00Commented May 19, 2023 at 8:32
Your professor's approach is totally misguided.
A function, or just a bit of code, should have a spec that says what it does, which should cover every possible input. And the code should be written so that its behaviour is guaranteed to match the spec. In the example, I would write the spec quite simple like this:
Spec: If var is false then the function does "this", otherwise it does "that".
Then you write the function:
if (var == false) dothis; else dothat;
and the code meets the spec. So your professor says: What if var == 42? Look at the spec: It says the function should do "that". Look at the code: The function does "that". The function meets the spec.
Where your professor's code makes things totally unrobust is the fact that with his approach, when var is neither true nor false, it will execute code that has never been called before and that is completely untested, with utterly unpredictable results.
Robust code is simply code that handles failures well. No more, no less.
Of failures, there are many types: incorrect code, incomplete code, unexpected values, unexpected states, exceptions, resource exhaustion, .... Robust code handles these well.
I agree with @gnasher729 's statement: Your professor's approach is totally misguided.
Robust means it's resistant to breakage/failure because it makes few assumptions and is decoupled: it's self contained, self defining, and portable. It also includes being adaptable to changing requirements. In a word, your code is durable.
This generally translates into short functions that get their data from parameters passed in by the caller, and the use of public interfaces for consumers - abstract methods, wrappers, indirection, COM style interfaces, etc - rather than functions containing concrete implementation code.
I would consider the code you gave as an example of defensive programming (at least as I use the term). Part of defensive programming is to make choices that minimise assumptions made about the behaviour of the rest of the system. For example, which of these is better:
for (int i = 0; i != sequence.length(); ++i) {
// do something with sequence[i]
}
Or:
for (int i = 0; i < sequence.length(); ++i) {
// do something with sequence[i]
}
(In case you're having trouble seeing the difference, check the loop test: the first uses !=
, the second uses <
).
Now, under most circumstances, the two loops will behave in exactly the same way. However, the first (comparing with !=
) makes an assumption that i
will be incremented only once per iteration. If it skips the value sequence.length()
then the loop could continue beyond the bounds of the sequence and cause an error.
You can therefore make an argument that the second implementation is more robust: it does not depend on assumptions about whether loop body changes i
(note: actually it still makes the assumption that i
is never negative).
To give some motivation for why you might not want to make that assumption, imagine that the loop is scanning a string, doing some text processing. You write the loop and everything is fine. Now your requirements change and you decide you need to support escape characters in the text string, so you change the loop body such that if it detects an escape character (say, backslash), it increments i
to skip the character immediately following the escape. Now the first loop has a bug because if the last character of the text is backslash, the loop body will increment i
and the loop will continue on beyond the end of the sequence.
I personally describe a code as 'robust' which has this one, important attributes:
- If my mom sits in front of it and work with it, she can't break the system
Now, by break I mean either getting the system into an unstable state, or causing an UNHANDLED exception. You know, sometimes for a simple concept, you can make a complex definition and explanation. But I'd prefer simple definitions. Users are pretty good at finding robust applications. If the user of your application send you many requests about bugs, about state loss, about unintuitive workflows, etc., then there is something wrong with your programming.
Explore related questions
See similar questions with these tags.