A lot has been written about error handling in go.
It’s a hot topic, and for a good reason.Coming from an object oriented language, errors as return values seem awkward to say the least. Most developers I know, including myself, dislike errors codes.
In this blog post I’ll outline my take on error handling in general, and go’s error handling in particular.
My take on error codes
I HATE error codes. I used to write a lot of C back in the day, and one of the things I missed is error handling. I’m not a fond of the whole “return an error code and pass object references to change state”. I’m a big fan of functional programming and such patterns are a big no-no in functional programming land.
I think that the biggest problem with error codes is that they are easy to dismiss, especially for novice developers.
My take on exceptions
Exceptions are as beautiful as they are ugly: They can make your code clean and concise, and give you the power to handle errors deterministically. But they are often misused as control flows, which, IMO, do more harm then good.
There are two big problems with exceptions:
The first - using exceptions for flow control. If you do that, I highly recommend reading this article.
The second - broad exception handling. The following is a snippet I see a lot in production code:
try: |
Why is it problematic? because you’re silencing errors you have no idea about. Catching unknown errors is very risky: Once an unexpected error corrupts the state of your program, you can’t recover. The only way to catch it is via logging.
I’m a big of catching exceptions you know how to handle, throwing the rest and crashing gracefully. That might sound obvious, but many programmers don’t do that.
try: |
A bit on error handling in go
As I said, errors are a hot topic. At first, I hated go’s verbose error handling. It’s very similar to the C style of handling errors. I started changing my mind after reading Go’s Error Handling is Elegant & Errors are values.
I believe that if used correctly, error handling in go can be as beautiful as exceptions, without their added overhead. There are a couple of extremely powerful libraries / tools that make the above easy achievable.
stacktrace
Stack traces are a necessary evil when you need to debug an error. Palantir wrote an awesome library called stacktrace that makes stack traces easier to debug.
This is difficult to debug:
Inverse tachyon pulse failed |
While this gives the full story and is easier to debug:
Failed to register for villain discovery |
The library’s intent is not to capture the exact state of the stack when an error happens (look at go-errors for a library that does that), but to attach relevant contextual information at strategic places along the call stack, keeping stack traces compact and maximally useful.
panicparse
panicparse parses panic stack traces, densifies and deduplicates goroutines with similar stack traces. Helps debugging crashes and deadlocks in heavily parallelized process.
errcheck
errcheck checks for unchecked errors in go programs.
You can use it from the command line:
errcheck github.com/<user|org>/<repo>/... |
or, integrate it into your code editor. I use vim-go.