if err != nil { return err }
if err != nil { return err }
if res, err := actually_important_bits(...); err != nil {
return nil, err
}
The actually important bits are hidden in the middle of line noise. In the "common case" where `actually_important_bits` is just a simple function call it's not necessarily as bad, but the problem is when you have ten successive instances of this and one is slightly different. It's impossible to notice the important difference at a glance.For an industry that is just starting to understand that code is read hundreds of times more often than it's written, golang fails at the few things we actually know for certain about what makes it easier to understand code at a glance.
res, err := actuallyImportantBits(...)
if err != nil {
return nil, fmt.Errorf("actually important bits: %w", err)
}
And other times you have to do this: if err := actuallyImportantBits(...); err != nil {
return nil, fmt.Errorf("actually important bits: %w", err)
}
The problem here is that you really don't want "err" to leak to the outer scope, so the second case is preferable from an absolute reliability and least-surprise perspective... but you can only do that in certain arbitrary cases. I think it's a bit of a wart.You can certainly handle "res" in an else block, or even write "err == nil" and handle it there, but that is surprsing. I would say it's simply not done, ever, but the Go codebase itself does it (src/go/parser/interface.go.ParseDir was the first example I found; but my search returned many screenfuls of candidates so there are probably more cases lurking in there).
The fact that you have a choice is not ideal, basically.
But, having actually important bits in if err := ...; err != nil {} blocks is not detrimental to readability. You will know how to read that after 5 minutes of reading any Go program.
res, err := ...
if err != nil {
return nil, fmt.Errorf("...: %w", err)
}
for that exact reason. This doesn't scan any worse than any other text-based code. (Page in the people selling visual programming here.)I'm far from a Go expert, but I feel like this line is a bit pointless, especially if you write it a lot. At this point, why not just not handle the error, or panic?
At high scale, you want to perhaps try your query a few more times, backing off exponentially, maybe even round-robining (or more complicated load balancing) among different targets. Throwing up your hands and 5xx-ing a request that easily is not something you can afford at high scales of traffic where small outages happen _all the time_.
so basically you mean that this only applies to like 0.001% of all websites/apps.
thus it's stupid to do it. I mean even than you would have an error that you need to bubble up or circuit break it somwhere but probably not at this layer.
also KISS and MVP, never ever overcomplicate something when you do not know anything yet, so most people don't need to built for scale at day 1 and it also is stupid to do it.
Let's say you've got a function that reads from a file. If the file doesn't exist, what do you do? That entirely depends on the context the function is called in. If it's in a web server, you might return a 404. If it's a background task polling for some data, you might sleep for a while until the file is uploaded. Or you might want to create the file yourself.
IMO if Go didn’t have its tooling, no one would care about it.
every if err != nil; return err let's me mentally draw a line in the sand and not worry about exception handling for code above that line. It lets me start fresh and restart my mental model with the line of codes below the error handling block.
it doesn't take years of writing Go to understand this, all you need is a open mind to how Go does things.
My code is almost exactly:
err := doThing()
if err != nil {
return err
}err = doAnotherThing()
if err != nil {
return err
}etc.
There's no need for me to "start fresh". Each line of actual stuff doing might return an error which needs to be returned. That's it. It's a complete waste of space and inhibits readability to absolutely no benefit. And this is extremely common across our code base.