6 ms·
I feel like, in Go, the ideal is to handle the error in situ. If it has to be passed up, it goes the shortest possible distance before reaching code that can ha
by JulianMorrison 7y ago
I feel like, in Go, the ideal is to handle the error in situ. If it has to be passed up, it goes the shortest possible distance before reaching code that can handle it. This is a difference from exception-throwing languages and so it changes the importance of a stack trace. You don't need to know where it broke because it broke here. The same approach as in C. You test the result where you made the call.
- jniedrauer 7y agoOn the surface this sounds logical. But going back to the case I dealt with recently, all errors during authentication lead back to an HTTP 403 workflow. Errors themselves can occur a long distance from any sane central place to handle them. Unless you pass the http.ResponseWriter all the way up the stack, the only "in place" handling you could do is to log every error along with a unique string. Is this really the best we can do? if err := frobulate(); err != nil { log.Printf("Error on line 123: %+v", err) return err }
- q3k 7y agoYou example is code smell. When dealing with error handling in Go (and many other languages), you should do one _and only one_ of the following: - if you have enough context to handle the error (eg. retry, or degrade gracefully, or perform whatever other application logic to recover, possibly returning a more friendly error to your caller), do that, possibly logging the source error and do not return it further up the stack directly - you have now healed the error condition, and the application code can continue. - otherwise, return the error, decorating it with extra context if possible. Doing both, ie. logging (which is a case of handling the error) and returning it up the stack leads to extremely chatty and difficult to debug code. Errors are a fact of life, and your path of handling them should be as well engineered as the happy life. That's one of the most important things in writing reliable software. That's also one of the reasons there's no generic stack unwinding/exceptions on Go: it forces you to _think_ about error cases and at least reminds you to handle them gracefully. You might not like this approach of treating programmers this way, but that's one of the cornerstones of Go.
- jniedrauer 7y agoWell, sure, I agree with you. But then we're back to wrapping every single error in a unique string before returning it. Developers simply don't do that, so you end up with the un-debuggable mess that I had.
- klodolph 7y agoYou don’t wrap every error in a unique string, only in places where there is additional context. Developers I work with do this.
- wst_ 7y agoAnd hundreds of others don't. "It works in my place" is never a good argument. Thing is that people are taking shortcuts. We have that in our DNA. If something isn't imposed on us, will be abused. I saw a code base of almost 2M lines, of which 1.5M is written in Go by seasoned developers. You'd be surprised how messy it gets over time and how many times people forget to handle errors or just decide not to, out of convenience. Not to mention other design issues, simply, because Go doesn't protect itself against almost anything. If Go authors counted on self-discipline of the devs then, sorry, not sure what planet are they living on.
- xienze 7y ago> - if you have enough context to handle the error (eg. retry, or degrade gracefully, or perform whatever other application logic to recover, possibly returning a more friendly error to your caller), do that, possibly logging the source error and do not return it further up the stack directly - you have now healed the error condition, and the application code can continue. > - otherwise, return the error, decorating it with extra context if possible. This is precisely how checked exception handling works in Java, and yet there's no shortage of people complaining about it and praising how Go "makes you deal with it the way you're supposed to." > it forces you to _think_ about error cases and at least reminds you to handle them gracefully. So do checked exceptions. And 99% of the time, the solution to an error popping up in Java is to blindly re-throw it because hey, I'm just a component buried a couple levels deep in a REST call, what could I possibly do to fix the problem?
- barrkel 7y agoVery little error logic in the context of a sessionless server requires handling close to the point of error - requests should either succeed or fail en mass for easier reasoning and retry from the client end, which means no recovery. Which means you need to abort when you find an error condition, and roll back everything, all the way up the stack. The more stateful your problem domain, the more likely you need recovery logic, and error codes start becoming more useful. Add in nondeterminism and error conditions are non-exceptional and exceptions are often not the best model for them.
- JulianMorrison 7y agoSo how you do it is kind of Erlang style: Signal a channel "I died", log it, and die. You don't have to think in N-layers-deep call stacks, that's a Java way of thinking. Log the problem in sufficient detail where you found it, and stop. This is probably a use case for Go's panic and recover mechanism, where you actually do want to blow away a whole piece of the running code.
- inlined 7y agoThe point has also been made that errors in Go are O(1) so they cannot have stack traces. If you use the new %w to build a context trace in O(n) only as you need to bubble up the error.
- abvdasker 7y agoIn my experience this just doesn't hold for errors originating from a library. Because a good library interface should mostly be a black box it makes it really hard to understand the underlying cause of an error from something like a networking library or a database library without logging everything. God help you if you have client code that must be able to return errors originating from a library or from the client code itself. Stacktraces are probably the single most useful tool in debugging any issue and their absence in Go is one of my only enduring gripes with the language.
- jayd16 7y agoYou're only saying that because of the lack of wrapped errors. Once they add exception wrapping, you'll be able to essentially rethrow errors in Go. You'll have the same issue as every other language modern language instead of the current situation (which is a lack of error wrapping).