Are assertions always bad? [closed]
Asked Answered
S

11

20

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?

Stature answered 7/1, 2009 at 6:37 Comment(8)
I don't like how the question is asked as i see it, he says "I know best, if you don't aggree i will hunt you down"Alpenhorn
Well it is clearly subjective. In my opinion it is also borderline argumentative, but I would wait and see what discussion develops.Euromarket
I'm happy to edit it... I really would like to hear good reasoning. I'm not out to create a fight. Sorry if it sounds aggressive. probably repressed feelings from having been forced to work that way.Stature
@Euromarket Why is it subjective?Stature
The question should not be "are assertions good?", but rather, "when are assertions good?"Isidore
yes, or are assertions always bad? shall I rename it?Stature
@Jesse when you are presenting a question as an always or nothing proposition it will almost always be subjective. There will usually be edge cases that do not fit whatever your proposition is. Good or bad is almost always a subjective call.Euromarket
@EbGreen: But I was saying that someone else told me they are always bad, see my rename of the question's title? I wasn't suggesting an all-one-way thing, I was saying, were they right in saying it was all one way, my feeling is that they have their place. That is not subjective.Stature
P
20

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.

Pisano answered 7/1, 2009 at 8:19 Comment(2)
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.Stature
+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!Papilloma
L
6

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).
Lobbyism answered 7/1, 2009 at 6:44 Comment(7)
Exactly my thinking... see my comment on Chris' answerStature
About this point, see this answer and my update in the end pointing to a related question: stackoverflow.com/questions/117171/…Futility
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)Porta
@Greg - only true if your testing goes through every possible state the application can be in which IMHO is highly unlikely.Pisano
@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.Whorish
@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?Pisano
@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.Whorish
F
5

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.

Fotina answered 7/1, 2009 at 6:41 Comment(1)
I know they're used for two different things, please read the question again!Stature
H
4

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.

Hebron answered 7/1, 2009 at 9:48 Comment(2)
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.Autum
Agreed, however besides NULL and mis-aligned pointers, and fence checking there is no definitive way to detect a corrupted pointer.Hebron
M
3

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.

Mousetail answered 7/1, 2009 at 10:8 Comment(0)
G
1

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?

Greatcoat answered 7/1, 2009 at 6:41 Comment(12)
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.Stature
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).Greatcoat
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.Stature
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.Fotina
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?Greatcoat
Again, see stackoverflow.com/questions/117171/… and stackoverflow.com/questions/17732/….Futility
@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.Pisano
@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.Futility
@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.Pisano
@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.Pisano
@Chris, I think you have the making of another good question and lively discussion here ;)Pisano
@smacl, ok I made a separate topic of it: see stackoverflow.com/questions/420343/…Greatcoat
C
1

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.

Carpet answered 7/1, 2009 at 6:53 Comment(0)
C
1

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.

Corpora answered 7/1, 2009 at 8:43 Comment(1)
Assertions are not normally left on for release builds, and test programs normally don't have critical cleanup.Pumice
L
1

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.

Lovelovebird answered 7/1, 2009 at 11:21 Comment(0)
S
0

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.

Salangia answered 7/1, 2009 at 10:11 Comment(2)
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.Pumice
+1 for avoiding any unnecessary differences between retail and debug builds. See my answer above on how we avoid this potential pit fall.Pisano
S
0

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

Sainfoin answered 26/2, 2009 at 14:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.