Context: I write server software for a living, the kind that stays up for weeks before the next version is loaded. So my answers may be biaised toward highly defensive code.
The principle.
Before we delve into the specifics of where to use assert
, it's important to understand the principle behind it.
assert
is an essential tool in Defensive Programming. It helps validating assumptions (assert them actually) and thus catch programming errors (to be distinguished from user errors). The goal of assert
is to detect erroneous situations, from which recovery is generally not immediately possible.
Example:
char const* strstr(char const* haystack, char const* needle) { assert(haystack); assert(needle); // ... }
Alternatives.
In C ? There is little alternative. Unless your function has been designed to be able to pass an error code or return a sentinel value, and this is duly documented.
In C++, exceptions are a perfectly acceptable alternative. However, an assert
can help produce a memory dump so that you can see exactly what state the program is in at the moment the erroneous situation is detected (which helps debugging), while an exception will unwind the stack and thus lose the context (oups...).
Also, an exception might (unfortunately) get caught by a high level handler (or an unsavory catch from a fellow developer (you would not do that, of course)), in which case you could miss completely the error until it's too late.
Where NOT to use it.
First, it should be understood that assert
is only ever useful in Debug code. In Release, NDEBUG is defined and no code is generated. As a corollary, in Release assert
has the same worth as a comment.
Second, it should be understood that malformed input is part of your life. Would you want your compiler display an assert
message each time you make an error ? Hum! Therefore:
Third, it should be understood that crashes are not appreciated. It is expected of your program that it will run smoothly. Therefore, one should not get tempted to leave asserts on in Release mode: Release code ends up in the end user hands and should never crash, ever. At worst, it should shutdown while displaying an error message. It is expected that no user data is lost during this process, and even better if upon restarting the user is taken back to where she was: that is what modern browsers do, for example.
Note: for server code, upon "hitting" an assertion, we manage to get back in position for treating the next query in most cases.
Where to use it.
assert
is on in Debug mode, and so should be used for Debugging. Whenever you test new code, whenever your test suite run, whenever software is in your (or your teammates) hands, whenever software is in you QA department hands. Asserts let you spot errors and gives you the full context of the error so that you can repair.
Even better. Since you know code will not be executed in Release you can afford to performexpensive checks.
Note: you should also test the Release binary, if only to check the performance.
And in Release ?
Well, in the codebase I work on, we replace the inexpensive asserts (the others are ignored) by specific exceptions that are only caught by a high level handler that will log the issue (with backtrace), return a pre-encoded error response and resume the service. The development team is notified automatically.
In software that is deployed, the best practices I have seen imply to create a memory dump and stream it back to the developers for analysis while attempting not to lose any user data and behave as courteously as possible toward the unfortunate user. I feel really blessed to be working server-side when I contemplate the difficulty of this task ;)