views:

1259

answers:

12

I used to work for a company where some of the lead architect/developers had mandated on various projects that assertions were not to be used, and they would routinely be removed from code and replaced with exceptions. I feel they are extremely important in writing correct code. Can anyone suggest how such a mandate could be justified? If so, what's wrong with assertions?

Edit: I realise they are for two completely different things, which is why a mandate that assertions can't be used, and should be replaced with exceptions seems so crazy.

Edit: relevant links to similar questions (thanks to Daniel Daranas) here and here.

Edit: Wow... those links are great, in fact I wouldn't have posted this question if I had seen them. Should I delete it?

+1  A: 

Is it true that an assertion exists in the debug build, but not in the release build?

If you want to verify/assert something, don't you want to do that in the release build as well as in the debug build?

ChrisW
Well, that's a convention. It's quite easy to make them appear in the Release build too though. And I think it's often a good idea. However, it can be useful to have Debug-Only assertions that do time consuming checks, like verify a list is valid every time a list operation occurs.
Jesse Pepper
I like to release the version of the software which the developers actually tested; so I tend to delete the 'debug' build from the project/makefile, so that there's only one version that can be built (and tested, and debugged, and released).
ChrisW
Unit tests should be automated and run on both debug and release builds. Testing by a test-team should be done using the release build.
Jesse Pepper
Chris that is such a bad idea. The debug build is there because its normally 100x easier to debug due to the lack of optimizations.
Lodle
Another thing is that you may, later, want to be able to debug whatever you released: if you can't debug it, can you afford to release it?
ChrisW
Again, see http://stackoverflow.com/questions/117171/design-by-contract-tests-by-assert-or-by-exception#397873 and http://stackoverflow.com/questions/17732/when-should-assertions-stay-in-production-code.
Daniel Daranas
@Chris, I agree, and also use the same version for debug and release. The 10%-20% performance gains from the optimizer tend to be very minor when compared to manual optimizations at algorithm level. A single build removes so many potential bugs.
Shane MacLaughlin
@Chris "Another thing is that you may, later, want to be able to debug whatever you released: if you can't debug it, can you afford to release it?" - Nothing stops you from installing also the debug version in a subfolder, and switching to it on site when some problem arises.
Daniel Daranas
@Daniel, good links. Maybe you should add them as a seperate post so they appear in the body of the content, or if you have the rep, edit them into the original question.
Shane MacLaughlin
@Daniel, what if the bug doesn't appear in the debug build but only the release version? For example, uninitialised variables and buffer overflows only exposed when the code is optimised. Worse still side effects caused by mis-use of asserts.
Shane MacLaughlin
@Chris, I think you have the making of another good question and lively discussion here ;)
Shane MacLaughlin
@smacl, ok I made a separate topic of it: see http://stackoverflow.com/questions/420343/separate-debug-and-release-builds
ChrisW
+4  A: 

assertions and exceptions are used for two different things.

Assertions are used for states that should never happen. For example, a signalton pointer should never be null and this error should be picked up during development using an assert. Handling it with an exception is alot more work for nothing.

On the other hand exceptions are used for rare states that could happen in the normal running of an application. For example using fopen and it returns a null pointer. It could happen but most times it will return a valid pointer.

Using assertions is nether wrong nor right but it comes down to personal preference as at the end of the day it is a tool to make programing easier and can be replaced by other tools.

Lodle
I know they're used for two different things, please read the question again!
Jesse Pepper
+5  A: 

The only really negative thing I can say about assertions is they don't run in retail code. In our team we tend to avoid assertions because of this. Instead we use contracts, which are assertions that run in both retail and debug.

The only time we use assertions now is if one of the following are true.

  1. The assertion code has a noticable performance impact
  2. The particular condition is not fatal
  3. Occasionally there is a piece of code that may or may not be dead. We will add an assertion that essentially says "how did you get here." Not firing does not mean the code is indeed dead but if QA emails me and says "what does this assertion mean," we now have a repro to get to a particular piece of code (it's immediately documented of course).
JaredPar
Exactly my thinking... see my comment on Chris' answer
Jesse Pepper
About this point, see this answer and my update in the end pointing to a related question: http://stackoverflow.com/questions/117171/design-by-contract-tests-by-assert-or-by-exception#397873
Daniel Daranas
Design by contract and usage of assertions is about proving your state is correct at all times in a precise mathematical way. Once proven correct, you need not prove it all the time at runtime which may slow the program. This can be a good thing (tm)
Greg Domjan
@Greg - only true if your testing goes through every possible state the application can be in which IMHO is highly unlikely.
Shane MacLaughlin
@smacl - in mathematics we use proof by induction and the like to avoid brute force testing of every possible state (which may indeed be impossible) to prove correctness. This can be applied to software algorithms in many circumstances too.
Daniel Paull
@Daniel, proof by induction is all very well, but most seasoned software QA people will tell you that 100% test coverage is very rarely if ever achieved in mosts systems tests, remembering that test coverage != code coverage while testing. Given this is true, and most applications are delivered with some bugs, contracts are a great help in analysing root cause of the bug. Does anyone here seriously believe they deliver 100% bug free code all of the time?
Shane MacLaughlin
@Shane - I'm happy to claim that the bug rates in my team are lower than most (note quite zero though) and that formal proofs and testing that is guided by such proofs are a part of our success. We also use Monte Carlo tests to exercise the code in unusual ways. Interestingly, Monte Carlo testing often shows up problems that you might not consider bugs, such as a pathological case where performance is unacceptable. Without programming by contract and liberal use of assertions, I truly believe that we would be *much* worse off.
Daniel Paull
+1  A: 

The only guess is that because an exception is often non-fatal that it makes for a codebase that does not die in some odd state. The counter-point is that the fatality of an assertions points right to where the problem is, thus easy to debug.

Personally I prefer to take the risk of an assertion as I feel that it leads to more predictable code that is easier to debug.

notbenh
A: 

Assertions are your friend while developing, but not much more elegant than printf debugging.

Typically (from a C vantage point) NDEBUG is going to be set when compiling a production release. That means, to get anything meaningful out of assert in your customer's logfiles .. you'll have to implement your own that writes some data to some file instead of raising abort() if the assertion is thrown.

This is a very bad idea, because if your asserting an allocated space to exist and it fails, how can you rely on functions that need to allocate memory to log the error? Similar brain farts are alarmingly popular in exception / signal handlers.

An assertion is useful only to preempt an exception .. when inserted in key places, i.e. to assert a pointer is not null before trying to dereference it. Once it quits complaining, you remove it and move on to figuring out why the next one is complaining. Usually, code riddled with asserts() is a sign of a programmer that is not sure of themselves .. or a programmer trying to figure out someone else's mess.

I typically rip them out prior to release. There is no excuse for not running the program through its paces in a debugger and using some kind of memory checker (i.e. valgrind) before releasing.

Tim Post
I disagree with most of what's written here (taken at face value). You'd remove an assert after it fires for the first time? (i.e. you've fixed one particular problem that makes it fire) Asserts are like inline unit tests. If you recommended unit tests over asserts, I'd partly agree.
James Hopkin
+11  A: 

We use a modified version of assert, as per JaredPar's comment, that acts like a contract. This version is compiled into the release code so there is a small size overhead, but disabled unless a diagnostics switch is set, such that performance overhead is minimized. Our assert handler in this instance can be set to disabled, silent mode (e.g. log to file), or noisy mode (e.g. display on screen with abort / ignore, where abort throws an exception).

We used automated regression testing as part of our pre-release testing, and asserts are hugely important here as they allow us to find potential internal errors that cannot be picked up at a GUI level, and may not be initially fatal at a user level. With automation, we can run the tests both with and without diagnostics, with little overhead other than the execution time, so we can also determine if the asserts are having any other side effects.

One thing to be careful of with asserts is side effects. For example, you might see something like assert(MyDatabasesIsOk()), which inadvertently corrects errors in the database. This is a bug, as asserts should never change the state of the running application.

Shane MacLaughlin
This sounds like a really nice strategy. It's funny when you're looking for people who think asserts are bad, you never hear from them.
Jesse Pepper
+1 for avoiding side effects. I've seen people write code where they think 'assert' means "make it valid" rather than "is it valid". Obviously the code then fails if the assert is compiled out!
Paul Stephenson
A: 

Assertions can be left on simply by not defining NDEBUG, so that's not really an issue.

The real problem is that assertions call abort(), which instantly stops the program. This can cause problems if there is critical cleanup your program must do before it quits. Exceptions have the advantage that destructors are properly called, even if the exception is never caught.

As a result, in a case where cleanup really matters, exceptions are more appropriate. Otherwise, assertions are just fine.

Henk
Assertions are not normally left on for release builds, and test programs normally don't have critical cleanup.
David Thornley
+3  A: 

It depends on the criticality of your system: assertions are a failfast strategy, while exceptions can be used when the system can perform some kind of recovery.

For instance, I won't use assertions in a banking application or a telecommunication system : I'd throw an exception, that will be catched upper in the call stack. There, resources can be cleaned, and the next call/transaction can be processed ; only one will be lost.

philippe
An issue arises as to whether the bad data arose from user/programmer supplying incorrect values (recoverable) or rogue-pointer trashing memory (unrecoverable). In the latter case, the train is off the track. Exceptions just take you further off track. Sometimes failfast and restarting is best.
Mr.Ree
Agreed, however besides NULL and mis-aligned pointers, and fence checking there is no definitive way to detect a corrupted pointer.
philippe
+3  A: 

Assertions are an excellent thing, but not to be confused with parameter/return value checking. You use them in situations that you don't believe will occur, not in situations that you expect could occur.

My favourite place to use them is in code blocks that really shouldn't be reached - such as a default case in switch-statement over an enum that has a case for every possible enum value.

It's relatively common that you might extend the enum with new values, but don't update all switch-statements involving the enum, you'll want to know that as soon as possible. Failing hard and fast is the best you can wish for in such circumstances.

Granted, in those places you usually want something that breaks in production builds as well. But the principle of abort()ing under such conditions is highly recommended. A good stack trace in the debugger gives you the information to fix your bug faster than guessing around.

A: 

One reason to veto assert() is that it's possible to write code that works correctly when NDEBUG is defined, but fails when NDEBUG is not defined. Or vice versa.

It's a trap that good programmers shouldn't fall into very often, but sometimes the causes can be very subtle. For example, the code in the assert() might nudge memory assignments or code positions in the executable such that a segmentation fault that would happen, does not (or vice versa).

Depending on the skill level of your team, it can be a good idea to steer them away from risky areas.

slim
If the code's that fussy, it's even more likely to work or not depending on whether it's a debug or optimized build, or whether it's working on a test or realistic data set. Assertions are way down on the list here.
David Thornley
+1 for avoiding any unnecessary differences between retail and debug builds. See my answer above on how we avoid this potential pit fall.
Shane MacLaughlin
+1  A: 

We use assertions to document assumptions.

We ensure in code review that no application logic is performed in the asserts, so it is quite safe to turn them off just shortly before release.

jamesh
A: 

Note, throwing an exception in a destructor is undefined behaviour.

John W