5 ms·
I strongly believe that the Go approach is the correct way to go. Every function that can fail should return an error code and then the programmer invoking said
by fxtentacle 7y ago
I strongly believe that the Go approach is the correct way to go. Every function that can fail should return an error code and then the programmer invoking said function is expected to handle the error code. It also used to be that way back in the old C / C++ days before SEH made it messy like Java.
And the beauty of using statically typed languages with error code return values is that one can use automated tools to find all the places where error codes are being ignored. So then random error conditions sprinkled throughout business logic go back from "this is how Java is done" to "this is a code smell".
- _pmf_ 7y agoThe old C way was returning an error indicator and providing the error code in the global variable errno(), which has ... issues.
- viuphiet 7y agoA few more steps and you'd have reinvented functional programming.
- wruza 7y ago>Every function that can fail should return an error code Every function that can fail, that can take invalid arguments, or can be called at invalid times. Which widens the set to basically every function. Old APIs had a convention to always return a status directly and results in out-params, except for pure mathematical operations (in which errors were mostly ignored, e.g. in a computer, a+b may fail as much as fopen(), but who cares). Observing this fact, one can free themselves from: if ((status=operation(args, &vague_error_descriptor)) return status; and handle the specific error event where it seems reasonable in the way that seems reasonable. Unwinding call stack by hand doesn’t automagically handle errors. At main() or at the other high-level call site you still have no idea what to do with an unexpected error except logging/presenting it and exiting (or continuing in hope invariants were not broken). >one can use automated tools to find all the places where error codes are being ignored The same is true for exceptions - every milestone like db.open(), file.save() and process.spawn() should be try-catched. And in both cases you don’t know which errors “codes” are still unhandled.
- loopz 7y agoYou are describing panic() which may happen due to programmer error, but not from a+b or any arbitrary event. It follows a short spec and stdlib, and may even be recoverable. Your if-example is old style and not allowed in golang. Your milestones escalates any small deviation into potential crash-handling territory (good example).
- StreamBright 7y agoI do not now how many hours was wasted on trying to debug Java applications that did not have this principle and produced 10000s of lines of stack traces in production with cryptic exceptions.
- tsimionescu 7y agoWhich principle would that be? With Java, even if you do a bad job of error handling, you usually still get at least a stack trace that you can investigate based on. If you do a bad job with error codes, you can easily be left with no way of investigating a problem other than trying to reproduce it.
- StreamBright 7y agoYes? Let me tell you just one story about this. I used to work as a systems engineer for one company where we had Hadoop as the data warehouse solution. One day my co-worker decided that he is going to get into DevOps and his first thing will be to touch DNS settings. He also thought that the best course of action is to push his changes without a review. His code was deployed to staging. I was called into to investigate an outage on staging later on. Needless to say I was unaware of the changes but this is not the point. Hadoop datanodes were failing with the following: "Cannot connect to NameNode: host.name.tld port x" I was was trying to investigate why. Unfortunately I did some quick testing as root and I could connect to the host and port combo. I went through all the changes in Hadoop configuration but there was no change that could cause this. Finally (several hours later) I wrote a simple Ruby code that did the exact same thing and I run it as hdfs user. I run the code and it failed with the following: "Cannot open /etc/resolv.conf" As you can see the Java codebase was failing with the same reason but nobody bothered to check for this problem in the library so the exception that you cannot open a file resulted in a different exception. It turns out my co-worker was very eager and security aware and he changed the resolv.conf permission from 444 to 400. I also made the mistake to investigate this as root first and not going through the unrelated changes. If Hadoop's Java was decent enough it would have printed the exact error message instead of something that is related but not exactly the error you are facing. This is what I push for regardless of the language, catch the error exactly where it happens and log it there and I think this is what the parent was referring to as well.
- tsimionescu 7y agoYou may believe that of course, but the reality is that we have no proof either way. I personally believe the opposite, that Go-style explicit manual error handling obscures both the business logic and the error handling logic, and makes it much harder to read code. Unfortunately, we don't have any studies that could shine a light either way, and it is very likely that if we did, we would see something similar to the typical static-vs-dynamic typing study : no real effect on defect rate; no way to accurately measure productivity. > And the beauty of using statically typed languages with error code return values is that one can use automated tools to find all the places where error codes are being ignored. Even better, the beauty of garbage-collected languages with exceptions support is that errors can't be ignored at all! And if you don't have garbage collection and need to manually cleanup every resource, both approaches get a lot more difficult, and it's much harder to tell which ends up being superior.
- erik_seaberg 7y ago> one can use automated tools to find all the places where error codes are being ignored What I want is an automated tool to display the 1/3 of code doing useful work, and hide the 2/3 useless noise that should have been generated. We shouldn't make human beings read exception bubbling boilerplate for the same reason we don't make them read stack frame setup boilerplate.
- fxtentacle 7y agoIntelliJ is doing that nicely for the anonymous classes needed for Java callbacks.