Indeed you're supposed to, but that way if someone calls exit(0), it looks like the program worked fine, when in fact they committed some debug code and made the program no longer run to completion. "Yay, done" was put in for the scripts to flag this sort of thing, presumably based on experience.
I can say up until 2005 or so I was a real believer in printf() debugging but I deliberately switched to using a debugger as much as possible around that time. I found that no matter how hard people "try" if they modifying the code to do debugging there is some chance these get checked in -- whereas you can investigate many things with the debugger without checking anything it.
Some applications have more trouble with setup and teardown than others. Like I knew a professor who kept sending me C programs that would crash before main() and some systems have a lot of trouble with "crash on shutdown" which might be a problem (corrupted files) or a non-problem.
We still seem to have fairly bad tooling for advanced debugging use cases.
There's no good reason you shouldn't be able to have an IDE maintain a text overlay of debugging points which is solely supplied as breakpoint scripts to the debugger instead.
That has literally been table stakes for Windows development since the 90s.
Not having the debugger fully integrated into your integrated development environment is strictly a problem of the commercial Unix and open source crowd and their "Real Programmers are fine with stone knives and bearskins" machismo.
> I was a real believer in printf() debugging but I deliberately switched to using a debugger
This really does not need to be an either/or. They have different uses. You can stick in 20 printfs and get a quick feel for where the bug is far quicker than stepping through the code - especially if you set a breakpoint and hit run, only to realise that you've overshot. You can run the program 10 times with different parameters and compare the results with printf much more easily than you could with a debugger. But, once you've found the rough area, a debugger is much better for fine grained inspection, and especially interrogating state with carefully written watches.
I do get your point about the risk of leaving in some trace by accident. But it feels like overkill to throw away such a valuable tool just because of that.
"People deliberately misuse the very mechanism that was designed to indicate successful completion, so we added another, flawed detection mechanism based on IO, because there's no way they'll do the thing that they should have learned in the first year of school, and just call exit with any other argument than 0 on irregular completion".
Gosh I thought the engineering culture was bad where I work.
Then that's utterly and horribly wrong? And in my decades of experience, I've fortunately only relatively rarely had to deal with that particular kind of atrocity.
Next thing you know, you'll have to skip a major version number in your product because a significant fraction of your user base string parse its name to determine what version it is.
There is a parallel universe where the convention is that 0 is used for when something went wrong, and 1-127 are reserved for all the myriad and beautiful ways in which things went right.
It's POSIX convention to write to stderr for anything that's not strict program output. I have seen 2>&1 far too often in scripts. I don't worry about it and happily write error messages to stderr whenever my scripts exit without a 0 status code.
But the whole idea is that a warning is a warning. Solving a warning can be deferred, and a warning doesn't cause execution to fail. Your warning was transmuting itself into an error. I feel like "All means are fair except solving the problem" is the wrong conclusion to draw here. If it should have been solved immediately, it should have been an error in the first place. (And then you should have politely bumped the version so that you don't immediately break the code of all your dependents.) If there is no need to solve it immediately, then "all means are fair" to convert it back to a warning as was originally intended.
Does no one know what exit codes and stderr vs. stdout is anymore?
I work with old and new code bases used by many clients in complicated setups, but adding a warning to stderr while stdout was left untouched, and proper exit codes maintained, was hardly, if ever, a problem so far.
Of course, there's always some unpleasant exception, but it's rare.
And of course, I also understand that the author might have found themselves not only in one of those rare-ish instances, but also one where reasoning with the other side was fruitless.
> Instead, they were quick to point out that it’s hard to know where these warnings could come from, and we cannot risk all those critical workflows failing when some case of misuse surfaces in a new context.
Ah yes, Schroedinger's workflow. So important any disruption is a disaster, and simultaneously so unimportant they couldn't possibly spend a single dime on the tools critical to the workflow.
It reads as if the change was made to some library code that was depended upon by someone else's program that would "yay, done", which was in turn depended on by some workflow.
It's probably a non-starter to change library code so it hard fails if it detects its being used incorrectly, in situations where it previously ran and did something. That's a severe breaking change in behaviour.
Easing it in by printing a warning message sounds like a reasonable step toward hard failing. But then we get the situation yosefk relates.
We even have the options around in our compilers to treat warnings as errors. As continuation on that idea I for one was lucky earlier in my career to work with the brilliant idea of just asserting in production. Straight up crash the software when something was wrong. Wrong preconditions that could mess something in the future. "empty array where it isn't supposed to be empty, sorry crash - fix your bug". even when it kind of worked, which is the real issue... it kind of works until it doesn't. I've silently introduced this mindset into my work ever since and the quality of the result is so much better. Warnings are something that will be deferred when given the option so don't warn it is an error.
One should always imagine a 1000:1 ratio of “person who reads a warning message” to “person who can do anything about it”.
You drop a little stderr print in your library saying “foo is deprecated, please use bar”, but then that library is included in another library which included in a CLI called by a script somewhere and, now the output goes to someone’s terminal when they’re running some management CLI. They never wrote the call to foo, they don’t even know what a foo is, they are 3 layers removed from who the target audience is.
Runtime is never the time to alert to things like deprecations or “this shouldn’t be used this way” notices. For deprecations, use your language’s built-in way of making functions deprecated. If your language doesn’t have that, use a real programming language. For other types of warnings, if you’re not 100% sure your message will only be seen by the developer who can fix it, just don’t write it.
27 comments
[ 3.4 ms ] story [ 61.6 ms ] threadSome applications have more trouble with setup and teardown than others. Like I knew a professor who kept sending me C programs that would crash before main() and some systems have a lot of trouble with "crash on shutdown" which might be a problem (corrupted files) or a non-problem.
There's no good reason you shouldn't be able to have an IDE maintain a text overlay of debugging points which is solely supplied as breakpoint scripts to the debugger instead.
IDEs seem to conk out at click to set breakpoint.
Not having the debugger fully integrated into your integrated development environment is strictly a problem of the commercial Unix and open source crowd and their "Real Programmers are fine with stone knives and bearskins" machismo.
This really does not need to be an either/or. They have different uses. You can stick in 20 printfs and get a quick feel for where the bug is far quicker than stepping through the code - especially if you set a breakpoint and hit run, only to realise that you've overshot. You can run the program 10 times with different parameters and compare the results with printf much more easily than you could with a debugger. But, once you've found the rough area, a debugger is much better for fine grained inspection, and especially interrogating state with carefully written watches.
I do get your point about the risk of leaving in some trace by accident. But it feels like overkill to throw away such a valuable tool just because of that.
Gosh I thought the engineering culture was bad where I work.
If it's commercial software, you're paid to make it work, no matter how stupid that may be, and forced stupidity isn't your problem.
If it's FOSS, you can tell the user to deal with it and close the ticket.
I work with old and new code bases used by many clients in complicated setups, but adding a warning to stderr while stdout was left untouched, and proper exit codes maintained, was hardly, if ever, a problem so far.
Of course, there's always some unpleasant exception, but it's rare.
And of course, I also understand that the author might have found themselves not only in one of those rare-ish instances, but also one where reasoning with the other side was fruitless.
I also include the runtime of the previous command which is useful to find out how long commands take without having to remember to time each one.
Ah yes, Schroedinger's workflow. So important any disruption is a disaster, and simultaneously so unimportant they couldn't possibly spend a single dime on the tools critical to the workflow.
- sometimes you can get the status code, sometimes you can't.
- sometimes you can separate out stdout from stderr, sometimes you can't
- sometimes the program generating the error message identifies itself, sometimes it doesn't
- sometimes you don't know if you have a "good error" (ok to ignore) or a "bad error" (cannot ignore)
I am a fan of the HARD FAIL.
I think internal unit tests or things like that should hard fail, then get a human to either fix it, or put in a hard exception.
if it is user-facing... sigh
It reads as if the change was made to some library code that was depended upon by someone else's program that would "yay, done", which was in turn depended on by some workflow.
It's probably a non-starter to change library code so it hard fails if it detects its being used incorrectly, in situations where it previously ran and did something. That's a severe breaking change in behaviour.
Easing it in by printing a warning message sounds like a reasonable step toward hard failing. But then we get the situation yosefk relates.
You drop a little stderr print in your library saying “foo is deprecated, please use bar”, but then that library is included in another library which included in a CLI called by a script somewhere and, now the output goes to someone’s terminal when they’re running some management CLI. They never wrote the call to foo, they don’t even know what a foo is, they are 3 layers removed from who the target audience is.
Runtime is never the time to alert to things like deprecations or “this shouldn’t be used this way” notices. For deprecations, use your language’s built-in way of making functions deprecated. If your language doesn’t have that, use a real programming language. For other types of warnings, if you’re not 100% sure your message will only be seen by the developer who can fix it, just don’t write it.