First off, I also find the interface-nil thing to be a frustrating edge-case. I also know that this design was the result of some difficult tradeoffs, as such things often are. We can argue about whether they made the right tradeoff, but I don't think it's fair to refer to it as a "mindblowingly wrong idiocy".
Second, on error handling. Having now written a large amount of server code in Go, I find that I strongly support their approach, even when I find it a bit verbose. Here's how I find it actually plays out in practice:
// You write something like this:
result, err := getResults()
if err != nil {
return err
}
// Then you reuse err for the next call
result, err := getResults()
if err != nil {
return err
}
stats, err := computeStats(result)
if err != nil {
return err
}
Eventually, you realize that `return err` just isn't enough most of the time, because it's impossible to make sense out of your logs. So we added a simple "error chaining" function that allows you to pass context when you return the error, which makes the logs much clearer than just a single error message, or raw stack trace. And of course it is often the case that you want to do more logging in your error blocks, even as you ignore the error and move on (e.g., "spurious error reading memcache entry; falling back to the slow thing").The practical reality of writing good server code is that exceptions which get caught way up the stack are inscrutable in your logs (at least that was our experience), so it makes sense to know up-front exactly where failures can happen, and to be forced to think about them the first time you write your code. Yes, it can be a bit verbose, but we've found the tradeoff to be net positive.