6 ms·
Asserts are essentially checked comments and an automated test is not a replacement for comments. I've done things like the following many times: if (x !=
by Buttons840 3y ago
Asserts are essentially checked comments and an automated test is not a replacement for comments.
I've done things like the following many times:
if (x != 5) {
... 40 lines here ...
} else {
assert(x == 5);
... more lines ...
}
The main purpose of the assert is to remind the reader that x == 5. There was a time where I would use a regular comment for a reminder like this, but since it's something the computer can check, I mine as well turn my regular comment into a checked comment.
- kccqzy 3y agoThe danger is change. What if someone comes and then due to a new business requirement changes the code to if (x != 5 && x != 6) { ... 40 lines here ... } else { assert(x == 5); ... more lines ... } Because of the 40 intervening lines the person might never see your assertion! If you work in a project with automated tests that do not have sufficient coverage, this is just a spectacular way of blowing up production. Assertions are bad because they are checked at runtime which is too late. The ideal checked comment should have the checks happen at compile time. Something like Liquid Haskell.
- Terr_ 3y ago> blowing up production. I prefer what Java does: Assertions are disabled at runtime by default, however you can turn them on without requiring a different code-release and you can even do it for specific packages/classes. So you might even use them in production: If feature X is already failing in some bizarrely uninformative way, turning on assertions in relevant code may provide you a better hint to what's going awry.
- spc476 3y agoThen the assert() triggers and you fix the assert() (or the bug). What's the issue here?
- kr0bat 3y agoIn this example the assert is less explanatory than a comment. Why do if (condition): ... else: assert(!condition) ... When you could instead if (condition): ... else: #if (condition) ... Or otherwise explicitly comment the condition that was checked earlier. At least then if the comment is unchanged, whoever is touching your code will assume the comment is outdated, rather than assuming that you're asserting that x should be some undocumented magic number.
- nerdponx 3y ago> If you work in a project with automated tests that do not have sufficient coverage, this is just a spectacular way of blowing up production. Great, I'd rather have this error cause a loud and obvious failure that can be easily hotfixed, than have it lead to subtly incorrect behavior that goes unnoticed for days and takes another day to debug.
- diogenes4 3y agoThe entire point of failing fast is to fail fast.
- kccqzy 3y agoAssertions do not fail fast enough. They need to fail at compile time as much as possible not at runtime.
- diogenes4 3y ago> They need to fail at compile time as much as possible not at runtime. Wouldn't that be nice! Seems like you're conflating two different classes of failures, though. There's no way to bypass runtime constraints entirely, though—all you can do is move it out of the production code and into tests, which takes a great deal of effort compared to an assert (which work just as well under tests, btw).
- kccqzy 3y agoI'm not talking about tests. I'm talking about the compiler.
- spc476 3y agoThis [1] might be a better example. C isn't Pascal, you can't just define a type of integer with a fixed range, so here, I check to ensure that the code above has done its job. I don't want an if statement here for this, for if the code above works, the if will never be triggered, and thus, as far as I'm concerned, is dead code. [1] https://github.com/spc476/mod_blog/blob/master/src/backend.c#L998 https://github.com/spc476/mod_blog/blob/master/src/backend.c...
- Buttons840 3y agoThat's not possible. If I'm about to write some code that assumes the gyroscope is spinning, I might assert that the gyroscope is spinning. That cannot be checked at compile time. Also, until almost all the world's software has been rewritten in Idris, compile time checks just cannot do very much. This is a reality of our century. Keep in mind that my original example isn't really about checking that x == 5. The focus is not on checking integer values, it's just an example. Although, again, even something as simple as checking integer values is beyond the compile time abilities of our compilers. Halting problem sends its regards!
- IshKebab 3y agoWell use static_assert then. Not all checks can be done at runtime. Also isn't failing the assertion the whole point here? Without the assertion you would have a possibly hard-to-find bug rather than an assertion failure.
- armchairhacker 3y agoWhat if `... more lines ...` depends on `x == 5`? Then you'd want the compiler to catch the assertion error, rather than whatever obscure error is thrown later on. And if it breaks production, you are breaking production without the assertion; runtime assertions aren't ideal but they're better than nothing. If you don't rely on `x == 5` in the else branch it's a bad idea, and too many of these spurious assertions will create a habit of just removing assertions whenever they fail.
- flohofwoe 3y ago> this is just a spectacular way of blowing up production. If your devs and QA team (you do have QA and not just rely on automated tests I hope) don't trigger this assert *before* deployed to production then there's something seriously wrong with your development workflow. After all, the code after that assert also assumes that the (now invalid) assumption that was checked by the assert is true. Without the assert telling you exactly what is wrong and where, you'd have a much bigger problem: some piece of code that now runs under wrong assumptions, and which may or may not cause much harder to diagnose problems further down the road. Think of asserts as trip-wires, they are most useful at the entry of a function and prevent an error from propagating (because the earlier an error condition is caught, the easier it is to diagnose).
- Tempest1981 3y agoDo you have QA running debug builds? In "release" builds, the asserts are compiled out. (Not sure what language and platform you're using) Or does QA switch to release builds at some point? When asserts fail for QA, what do they see/collect?
- flohofwoe 3y ago> In "release" builds, the asserts are compiled out. That's just a convention, and IMHO not a good one. All the C++ games I helped shipping had asserts enabled in the release exe. We had our own custom assert macros, and different levels of assert checks (e.g. special "hot path asserts" were not included in release builds, but that was an exception and should be avoided - specifically, range-check asserts for array accesses were never removed since this was the most common source of problems). In general, the performance difference between keeping asserts in or removing them was mostly negligible (IIRC less than 2% in a typical frame), the only downside is that the executable becomes about 25% larger (and easier to reverse engineer) because of the embedded assert condition strings. This would have been solvable by replacing the assert strings with comptime hashes, but we didn't deem this important enough to implement). In addition, the custom asserts also generated a stack trace and a mini dump for easier post mortem debugging. > When asserts fail for QA, what do they see/collect? A MessageBox with: - the assert condition string - filename and line number of the assert - an optional printf-style programmer message (depending on the assert macro type) - the pretty-printed function/method name which contains the assert (very useful for generic code, e.g. you don't just see `Array<T>::push()` but also the resolved template parameters) - a stack trace (we generated a PDB file also for release builds to make the 'inhouse' stack traces human readable, but didn't ship the PDB to users - so stack traces for the shipped game would just display raw addresses, but those could be resolved on our side with the PDB we were storing for each release version - we wrote an extra inhouse tool for that) - plus a mini dump is written for post-mortem debugging, but with all the information above that was hardly ever needed for figuring out what the problem was The content of Windows MessageBoxes can be Copy-Pasted, and that's what QA was supposed to do when writing a ticket. PS: also important to note that we didn't use the C++ stdlib (with very few exceptions, like std::sort), e.g. most importantly we wrote our own container and string types).
- UncleMeat 3y agoDepends on your goals for production. The code above indicates a bug. In some domains it really is preferable to crash with righteous fury rather than to continue executing with broken invariants and doing who knows what. The assertion didn't cause you to have limited test coverage that let you edit the branch condition without detecting that it introduced a bug in the false branch. Yes, if you've got a language that supports compile time checking of complex invariants then that's awesome! But few languages support this today.
- kccqzy 3y agoThe problem with crashing is that it is almost always a bad idea. You almost always want a limited scope crash that affects whatever currently is being processed. Imagine that in some languages an assertion raises an AssertionError exception which is caught by some faraway handler that causes the server the respond with a 500 error. That's strictly better than crashing. I also used to think it might be preferable to crash in some domains. With stories like https://news.ycombinator.com/item?id=37461695 https://news.ycombinator.com/item?id=37461695 it makes me think perhaps there are none. In any case my main point is that assertions would be perfect if only they are checked at compile time.