For me personally, with also nearly 2 decades of C# and C++, exceptions is the best possible way to handle errors there is, especially for large projects. I have worked on projects which were built on exceptions, and on projects which were built on return codes. Exceptions is hands down the cleanest. Instead of becoming a mess of call/error handle blocks, you code can clearly separate business logic and error handling. If I had 10 cents for every time I've fixed a bug when an error analysis is skipped because it is not supposed to happen until it is...
Golang way is terribly flawed. As a matter of fact I've watched a video yesterday on golang best practices, and got very excited. And then I realized, this is still a language where you can't throw an exception and that made me very sad, as I realized golang is going nowhere. It will be exciting for a few more years, like ruby was once, and then everybody realizes what a PITA it is for large codebases, and it shall become another ruby.
And for me having worked in both, errors as return values have been hands down better. The complexity added by an entirely different flow control has resulted in far more bugs for me to track down than anything else.
Similarly, it's for good reason that there's a whole other class of languages using things like the Result monad over throwing up all over everything.
That's not to say that there isn't better code written in exception based languages, but that your view is pretty myopic. The ergonomics are different in each approach, with different tradeoffs.
I'm pretty sure exceptions are better, for a couple of reasons.
The first is that the idea all Go programmers reliably propagate or wrap error codes, without losing important context, is not true. One of my first encounters with a serious Go codebase was at a consulting client, where I had a task to use their API. I sent it some input and got back a 500 Internal Server Error, no other info. OK, not ideal, but it was in development so I asked them to check the logs and find out what was going wrong. Guess what, the logs were useless. It logged at one or two places that an error had occurred, mostly close to the top level HTTP loop, but the actual location where the error was originated had been lost. Several layers in this app would convert error codes from one level of abstraction to another, also losing information. They shrugged, mystified. Just try things until you figure out what the issue is.
With exceptions this could not have happened. An exception has a stack trace. The developer needs do no work to get this valuable debugging aid, it's always there unless some bad code strips it somehow. Additionally, exceptions can wrap each other as causes, so code can work at high levels of abstraction whilst developers who are debugging can get precise error data from deep down the stack.
Another problem is the idea that Go developers never forget to propagate errors. Error handling in Go is tedious and there's no visible indication if you forget to do it or don't do it properly so sometimes it goes AWOL. The exceptional control flows still exist, just as they would if using exceptions, but now you have to write them manually instead of having the compiler write them for you.
A final problem is performance. Go has notoriously quite low performance, the people who say it's fast are usually comparing it to something like Python. Scattering hand written error handling code all over the place makes it harder for a compiler to move it out of the hot paths, because it's just a bunch of if statements. Exceptions by their nature tell the compiler that those error-handling edges won't execute very often, so they can be put out of the way in places that won't pollute the icache.
I agree with you on the stacktrace. The libraries that provide context via wrapping are good but woefully underused.
> Error handling in Go is tedious and there's no visible indication if you forget to do it or don't do it properly so sometimes it goes AWOL
This is also true, though I would expect teams to fail on linting so I don't share the concern. Is there an equivalent for unchecked exceptions (honest question)?
I'll take your word on the performance being better for exceptions, but I'm less convinced that Go's performance is a significant sticking point for the language.
Golang way is terribly flawed. As a matter of fact I've watched a video yesterday on golang best practices, and got very excited. And then I realized, this is still a language where you can't throw an exception and that made me very sad, as I realized golang is going nowhere. It will be exciting for a few more years, like ruby was once, and then everybody realizes what a PITA it is for large codebases, and it shall become another ruby.