Hacker News new | ask | show | jobs
by dullgiulio 2869 days ago
A cursory look at the article, shows that the most important observation about error handling in Go is missing.

Errors should be "decorated" (wrapped, contextualized...) in 99% of the cases.

In the end you get errors that describe step by step what your program tried to do and why it failed, for example:

* could not load profile: could not open file: permission denied.

* could not download profile image: could not open URL: HTTP GET failed: network is down.

This has many advantages:

1. Much more readable than stack traces (especially if they include source file and line information or exception class names: users don't care about those.)

2. Errors are still easy to grep in code to work out the program flow (the stack trace, basically.)

3. When reading the code, you can see from the error context strings what the code is actually doing. Basically it serves a function of comments and (unlike comments) error strings remain up to date.

It is definitely verbose, especially the not equal nil part, as it's a result of Go attempt not to have special cases. Also it's a pity that errors can be silently ignored: maybe Go2 could be stricter here.

Overall, I think this is one of the best approaches at error handling.

1 comments

In my experience, this just becomes arbitrarily close to "re-implement your stack trace by hand with space-delimited words instead of camelCaseFunctionNamesOrWhatever".

I'll overwhelmingly prefer an always-correct stacktrace over a hand-recreated one that sometimes collapses multiple branches into a single ambiguous on. At least then the devs can help me when it fails. And stack traces and concatenated strings are in no way appropriate error responses for humans unless you're expecting them to be able to navigate the source code, so neither does anything for the "provide a helpful error message for non-programmers" problem.

---

this is why stuff like https://github.com/pkg/errors exists. wrap at the deepest level / where the error originates, and it's relatively rare that you need to add context at higher levels. If you want user-friendly errors, you need something dramatically more sophisticated.

Just using WithStack() from "github.com/pkg/errors" on any error that originates from outside my repository has been my go-to rule for any Go project. It has never disappointed.
Like an exception?
Except it's just a returned value and not a goto lookalike.
Exceptions—which go straight up the call stack, like returns—are nothing like gotos.
The similarity in both needing to have stack trace does not make them very alike yet. They serve similar purpose and for that purpose the stack trace serves value to the developer.

Returns go straight up the stack only if you choose them to. Caller doesn't have to propagate errors and return immediately, it can hold onto them and/or process them in the natural place they occur, they are just values. The returns from your function can be found with "grep return".

Exceptions, by default, break the natural control flow unless you wrap everything with try/catch. Even then, a lot of constructs won't be very natural and you really will have to get out of your way to identify the source of the exception, which very frequently is more important than it's type.