>The verbosity of Go’s error handling has been much-maligned. It’s simple and explicit, but every call to a function that may fail takes an additional three lines of code to handle the error
Putting error nil checks into a function is an anti-pattern in Go. There is no need to worry about the LOC count of your error checking code.
> Putting error nil checks into a function is an anti-pattern in Go.
I assume you mean into a helper function like I've done with check()? If so, I agree with you for normal "production" Go code. But for simple throw-away scripts you don't want half your code littered with error handling, when you could just throw a stack trace.
> There is no need to worry about the LOC count of your error checking code.
Well, it means some functions are more than half error handling, obscuring the guts of what a function actually does. Even the Go language designers agree that Go's error handling is too verbose, hence proposals like this from Russ Cox: https://go.googlesource.com/proposal/+/master/design/go2draf... (there are many other proposals, some from the Go team)
agreed. when I see people talking about LOC my eyes roll. its verbose for a reason, the language designers WANT YOU to pay attention to the errors, not ignore them.
I think the ultimate question for me is, does increased tedium demand/imply/require "attention", or does it just create a new opportunity for mistake? If devs are so frequently writing these "check()" style functions, are they paying attention to or ignoring errors?
That's exactly why every other language took the wiser decision of actually having runtime errors.
To force you to pay attention to them before your application state went into an unknown configuration, thus making it nearly impossible to troubleshoot or even pretend to be deterministic.
I still have no idea how any programmer thinks this is OK. Nondeterminism and unknown/un-considered application state are literally the source of all bugs. I much prefer (and honestly believe it makes a ton more sense) to do what Erlang/Elixir does, which is to fail, log, and immediately restart the process (which only takes a few cycles due to the immutability-all-the-way-down design).
If you hit my Phoenix application with a million requests in 2 seconds and each throws a 500 error, my webserver will keep chugging along, while every other technology's webserver will quickly exhaust its pool of ready-to-go webserver processes and fall over like a nun on a bender.
I don't really know how to take comments like this.
I work on a pipeline that processes millions of events per day in a pipeline that contains two pretty busy Go programs. One has an embedded Javascript engine and does all kinds of pattern matching and string manipulation. The other does a ton of database read/write and some crypto-calculations to do data integrity for us.
Millions of events. Per day. Never a panic in production.
It is easily possible to write deterministic, highly performant, and durable applications in Go.
I was talking about millions of events per every couple of seconds, not per day. A million events per day can be accomplished with just 11 per second, btw; a very... achievable number.
WhatsApp, which is built on OTP, handled 64 billion messages in a day. Averaging out to 744,000 a second. Almost 10 years ago. (Granted, also on 8000 cores. But thanks to the concurrency...)
> every other technology's webserver will quickly exhaust its pool of ready-to-go webserver processes and fall over like a nun on a bender
Most other technologies don't use one process per request, but instead have a single server process which handles all exceptions that come out of a request by returning 500 without crashing the server.
You can absolutely do this in Go. In a webserver you panic and recover your goroutines for 500’s or other failures that you deem invalid states.
There’s a qualitative difference between what we call 500 errors or 400 errors and it’s a good thing to handle the former by throwing exceptions or panicing and the latter with normal program flow and error values.
Errors that you can sensibility handle and display are part of your domain logic, just values and functions. They should be handled and contextualised right there at the site they come up.
Errors that represent invalid program states should be thrown as far as possible and handled at the edge.
You may have misread this. The OP means "extracting error nil checks into a function is an anti-pattern", not "your functions should not contain error handling".
Putting error nil checks into a function is an anti-pattern in Go. There is no need to worry about the LOC count of your error checking code.
inb4 this ends up on pcj