Do Programmers Actually Use Assertions?
P.Chalin queries: "Do programmers actually use assertions (like the assert statement of various programming languages)? If so, what should be done when errors or exceptions are raised during the evaluation of an assertion? I am collecting opinions and stats via a short questionnaire. Thanks."
and to avoid putting my email in a survey, I use debugging messages as needed while debugging. If it's a web site, I'll also have it catch and forward them to my email. I don't really use assertions except that I write a lot of code to deal with unusual input in a non-fatal fashion. I was an on error resume next sort of guy back in the VB days.
My philosophy has been that unless security issues are involved, failing on an error is not much better than not checking for an error at all, if the program crashes in both instances, as happens when you use an assert(). Ideally, either errors are handled in the least-fatal manner possible, or you develop the right abstractions to enable you to write error-free code.
I'm working on some old library code for my job now and it's chock full of assertions. I think it's rediculous that a library call should cause the calling application to exit because of a failed assertion. In a "normal" application, assertions don't belong outside of main().
-73, de n1ywb
www.n1ywb.com
I read the Eiffel book, but I've never been in a position to actually write code in it. But I love the concept of programming by contract.
I just use assertions to do preconditions, postconditions, and checks. Invariants are a nice idea, but in practice seem to be a big performance hit. I just do invariant-like assertions as needed.
I assert the heck out of my code. You can see some of it here.
I don't see too many assertions in other people's code. Then again, I don't see too much that looks like planning or insight in other people's code most of the time, so why should I be surprised. I can't believe how sloppy we are as a profession. Like my coffee cup says...if builders build buildings the way programmers write programs, then the first woodpeckers that came along would destroy civilization.
"Once we've identified and embraced our sickness, we'll have strength...and that's when we get dangerous." - John Waters
You also must realize, that with Maple, it's quite easy to detect errors in calculations, and in coding. In practical programming with an object oriented language, it's almost essential to have some kind of error handling routines. Whether you use try-catch or the actual "assert" function, or some other error checking setup, it will save you ages in debugging a larger code base.
I think the newer Object Oriented languages (Java, C#, Objective C) have great error handling that foolishly gets ignored when younger coders go to work, myself included.
"Victory means exit strategy, and it's important for the President to explain to us what the exit strategy is." G.W.Bush
Assertions can turn little errors into big errors.
As your software environment and processing power shrink (think game consoles, PDAs, cell phones...or simply low-level code that can't afford the bloat), you have to make assumptions. It's no longer realistic to believe that every code module should handle every set of states and inputs like a perfect black box.
As you increase the lifespan of the code and the number of coders working with it, the usefulness of asserts also increases. When you write your brilliant, super-fast method that only works on normalized floats or ints with 22 significant bits, you'll know the calling code is following your rules... but in 6 months when the function becomes more popular without your knowledge, you're going to want it to blow up the moment it's misused.
There are two kinds of error checking, one for debugging and one for using the final product.
Relying on assert() for a final product is not helpful for the user, the user does not generally have a method to fix and rebuild the product even if the user knows the line and file that found the error. For the developer the assert() is very useful to find places where bad conditions exist.
The end user needs a more sophisticated error checking that visibly explains what is wrong, or simply ignores creating a spheres that have a 0 or negative radius for example (or simply makes sure the sphere radius is nonzero and positive).
I write very large applications that have a huge customer base. Null Pointer exceptions or the like are totally unacceptable errors for the user to see.
Assertions allows the developers of large and complex applications to support their code over the long run which is always the biggest flaw of most large applications.
It is always better to have a slower application verbose application that a fast and silent one. Example:
--} IE - Overly optimized for performance improvements and pretty much impossible to debug complex JavaScript with.
--} Mozilla - Well written and fantastic to debug complex JavaScript with.
Obviously simple, small, run a few time applications should not need assertions.
JsD
Many people use assert() like an exception, and they're not made for that.
You should use assert() to check for situations when the condition should never be false, unless there's a serious flaw in the software logic.
For example, assert(malloc() != NULL) is bad, but something like this is ok:
if(list->head != NULL) {
void* last = get_last_element_of_list(list);
assert(last != NULL);
}
Assertions can turn little errors into big errors.
It turns out that that's exactly what assertions are supposed to do.
- Assertions are for notifying you that something occurred during debugging/testing that should be impossible. This could be notifications of bad data that's slipped past validation. Note that assertions are stripped out of release builds.
- Exceptions are errors that cannot be ignored. For example, failing to open a file before reading it.
- Error returns are errors that occur that are not a big deal to ignore. This could be parsing an empty or invalid string.
For example, you might have a constructor that allocates space for a private pointer and a function (call it SetIt) that copies data to it.
In this case, the constructor would throw an Exception if new[] fails. This is an error that cannot be avoided.
SetIt should assert if the private pointer is invalid. It could also assert if the incoming data is NULL, which should alert you that there's a way to send in invalid pointers.
SetIt can then return an error if the incoming data (user typed I assume) is too long or incorrect format.
Oz
The problem with the OS doing it is that means the OS enforces too much policy. Lets say I have a multifunction printer hooked up to my PC. The fax fails. WHat should the OS do? SHould it eliminate all access to the device? Just to the fax? Should it cut off scan and copy oo in case the scanner is what broke? What about print, it may have been the printer that broke.
Timeouts? How does the OS know whats a reasonable timeout? Program A may expect data over TCP every 30 seconds or it signifies a failure- for example straming data. Program B may be a telnet session being used infrequently. It may not have data for minutes, even hours. WHat timeout should the OS enforce? When it occurs, what should the OS do- shut down the connection? Ping the other machine? Send a keepalive message?
The OS *can't* handle these because every application requires different handling. Adding in such behavior would make it impossible to write apps that need other behavior.
The correct answer is not to push it into the OS. Its to write better abstractions and libraries on top of the OS, so programmers can reuse the code and not have to write the same program repeatedly. For example, I use my own networking library that replaces the recv() with a custom recv. My version loops calling recv on the socket until either n bytes have been read, an error occurs, the socket is closed, or a timeout (passed in as an int) goes up. It then returns what condition occurs to the caller.
Other layers then go from there. An HTTP or FTP layer would handle those timeouts and failures in a standard way. Some situations may require it to chain an error on up saying the protocol layer failed. And so on and so forth. THis allows maximum flexibility and reuse of all levels, because all layers leave the policy decisions up to the calling code rather than making their own. If each layer decided how to handle every situation, you'd end up having to rewrite each layer for each application, the same solutions would not be applicable. Worse, some applications would need different decisions for different parts of the application. Then you have a real nightmare.
I still have more fans than freaks. WTF is wrong with you people?
Hell, no way.
Assertions are for internal self-checking. Code like checking that a pointer isn't NULL can't be turned into a useful error message for an user. At best, it comes out as "Internal error in module foo", which isn't really helpful to anybody. You could remove it, but that's even worse, now the application will just continue and crash at some random point.
Error checking should ideally be done in layers. By that I mean that the DrawSphere function, Sphere class or whatever should either FAIL HORRIBLY the moment you try to use a negative radius, or do nothing and return an error to the caller, but definitely NOT pretend that everything is going fine. That's a sure recipe for getting some really strange bugs. The user interface should prevent that from ever happening anyway.
Now, why? Because checks in the lower levels like the drawing function are there to make sure everything works as intended. If a negative radius somehow got passed it means that the UI is bad, or the caller did something wrong. Once at the lowest level you've determined that something is wrong, very often the only sensible option is a fatal abort.
The sphere drawing code doesn't know what it's drawing, and what are the consequences of stuff not drawing as it should. The application's UI is a lot more qualified to control these things, and that's where it should be done.
More seriously, assertions that can be disabled can potentially hide little errors altogether. Consider what happens if evaluating the condition for your assertion has side effects, but in release builds that condition is never evaluated. Oh, dear: now you can test quite happily on your diagnostic build, but the one you ship to your customer has an extra bug. :-(
If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.
You could remove it, but that's even worse, now the application will just continue and crash at some random point.
Or not. A lot of times, things that are asserted just don't cause a problem later on. Especially if the rest of the code is fairly well written and free of lots of intermodule dependencies. Why have a probability of crashing at 1.0, when you can have one that is less than 1.0? Yes data corruption is an issue, but again, see the comment above about intermodule dependencies. Good code checks assumptions at multiple levels and gracefully recovers.
Once at the lowest level you've determined that something is wrong, very often the only sensible option is a fatal abort.
Completely wrong. A crash simply isn't the only thing to do. And in many cases, it can be a very bad thing. Sure, if a user-interactive program crashes, then the user restarts the program, no big deal. But if your code is running on Mars, then it sure is a big deal. And don't try to tell me that that is an extreme situation, that kind of thing happens all the time in embedded programming.
Mad Software: Rantings on Developing So
I think that the idea behind them is to turn little errors into big errors so that a "little" error to the program (loaded garbage data for an account balance, for example) becomes a big error before any harm is done. It is generally accepted that errors that halt execution are preferrable to logic errors, which seems to be the philosophy behind assert().
That's not a more accurate statement, it's simply a different one.
Perhaps, but if I'm the application developer and you're the library developer, then that's my decision, not yours.
I might have all kinds of clever diagnostic code running in my application, direct comms with the debugger, other activity going on in different threads, acquired resources, or 101 other reasons that I don't want to kill my application dead, even if your library encounters a serious problem. By all means tell me about the problem, and then if I want to, I can dead-end everything to make it obvious while I'm testing. But don't tell the rest of my application, about which your library has no knowledge, how it should handle an error in one part of the program. That is the worst sort of catastrophic failure, and violates every principle of modularity and encapsulation in the book.
If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.