Hacker News new | past | comments | ask | show | jobs | submit login

The problem with explicit error handling is that it's all too easy to get it wrong (by forgetting to check the return value) and when it does go wrong, it goes wrong silently, introducing a risk of leaving you with corrupt data. In production.

The beauty of exceptions, on the other hand, is that the default option is the safe one. Sure, forgetting to add error handling may leave you presenting a user with a stack trace, but at least you're not billing them for something that never gets delivered.




>The beauty of exceptions, on the other hand, is that the default option is the safe one

Except, when it's not. Exceptions tend not to solve the problem, only make it subtly worse. The biggest wart on exceptions is the fact it introduces non-local control flow. All of the sudden any function you call can cause you jump out of your current function. In any situation where an unhandled error will corrupt your state, exceptions have that problem as well, on top of the fact that they are invisible.

An `err` aliased to `_` or shadowed can be found by a linter or a human reading the code. A function `foo()` causing your stack to blow up and possible corrupt any IO you are doing is worse. Therefore, the guys in Java-land discovered CheckedExceptions which were even more controversial, and arguably led to languages like Go and Rust dropping exceptions in general.


The problem with explicit error handling is that it's all too easy to get it wrong (by forgetting to check the return value) and when it does go wrong, it goes wrong silently, introducing a risk of leaving you with corrupt data. In production.

The problem with exception error handling is that it's all too easy to get it wrong (by forgetting to complete the handle code) and when it does go wrong, it goes wrong silently, introducing a risk of leaving you with corrupt data. In production.

In either case, error handling needs to be code reviewed. The best thing to do, is to make the right thing the easiest, minimal friction thing to do. Unfortunately, getting off the "happy path" is often a messy business. My suggestion is to explicitly implement a standard "developer scaffold" to be used when filling in the error handling during development, with penalties for not using it. This makes it easier to find where error handling needs to be fully fleshed out.

The beauty of exceptions, on the other hand, is that the default option is the safe one. Sure, forgetting to add error handling may leave you presenting a user with a stack trace, but at least you're not billing them for something that never gets delivered.

The entire reason why Unit Testing and Test First made it into Extreme Programming and Agile methods, is that it was way too easy for end-users to see error notifiers and stack traces in production in Smalltalk.


> The problem with exception error handling is that it's all too easy to get it wrong (by forgetting to complete the handle code) and when it does go wrong, it goes wrong silently, introducing a risk of leaving you with corrupt data. In production.

It's much more difficult to "forget completing the handling code" than it is to overwrite a golang error value and not handle it (which does happen in code bases). The default behavior of unhandled exceptions is to bubble up, unlike golang errors that can get silently dropped quite easily, and I've seen this is in large code bases. Unless you're writing

  try { ... } catch (..) { /* do nothing */ }
in which case you explicitly opt into doing nothing, and for which there are linters that catch this sort of behavior automatically.

On the other hand, even the simple

    func main() {
        fmt.Println("")
    }
doesn't handle errors.


in which case you explicitly opt into doing nothing, and for which there are linters that catch this sort of behavior automatically.

There are linters for golang. No reason why those sorts of tools and community norms shouldn't squash those sorts of behaviors. (As has happened for race conditions in golang!)


I used such linters, and they fail at certain things. The `fmt.Println("")` example still holds. Another example:

  a, err := foo()
  b, err := bar()
  c, err := baz()
err doesn't get handled in the first two cases, and the linter doesn't complain.

And race conditions in golang are very much possible, and cause issues in prod.


I used such linters, and they fail at certain things.

Of course. They're just a tool.

For the amount of concurrency done, golang is doing pretty good. I know of no other programming community which has such community standards on using race condition checking to the same extent as linting.


> Of course. They're just a tool.

Which is why it is strictly superior to use a language feature which does not have this issue to begin with (e.g. exceptions or actual compiler enforced error handling like Rust).

> I know of no other programming community which has such community standards on using race condition checking

I assume you're referring to the golang race detector. It's better than nothing, but then again, it's a tool and is not guaranteed to find all issues that may arise. Compare (again) with Rust, or with languages with proper immutable data structures like Scala and Java (e.g. https://immutables.github.io), not to mention https://openjdk.java.net/jeps/8208520 or https://clang.llvm.org/docs/ThreadSanitizer.html


actual compiler enforced error handling like Rust

Or Java?

Compare (again) with Rust, or with languages with proper immutable data structures like Scala and Java

To paraphrase you from earlier: As far as I know, race conditions happen and leak out into production with Scala and Java.

The lesson of golang, and really the lesson of programming in the large for the past 30 years, is that it's not enough to have mathematical or methodological power in guaranteeing correctness in a program. If that were the case, formal methods would have won decades ago, and we'd all be using such environments. It's not about having the most rigorous widget. It's getting programmers to do the right thing, month after month, year after year, in large numbers, across different management teams. Arguing the strength of constructs within programming languages is just an idle meta-anatomic measurement exercise.

That said, I like lots of things about Java, Scala, and Rust. I just happen to really like golang for a different set of reasons. Golang is concurrency+programming-in-the-large-human-factors "Blub," in a way that refutes PG's essay about Blub.


> Or Java?

Yes - that was implied in the same sentence where I wrote "(e.g. exceptions or actual compiler enforced error handling like Rust)." :-)

> As far as I know, race conditions happen and leak out into production with Scala and Java.

They do. But there are more techniques in those languages (such as immutable collections, as their type systems are actually able to model and implement them) to mitigate the issue.

> and really the lesson of programming in the large for the past 30 years, is that it's not enough to have mathematical or methodological power in guaranteeing correctness in a program.

I agree there. I actually don't abide by the ideas that using a language like Haskell or Idris or what have you will automagically grant you superior or error-free software. I actually like what talks like these have to say:

* https://www.youtube.com/watch?v=dWdy_AngDp0

* https://www.youtube.com/watch?v=449j7oKQVkc

However, that does not mean we disregard established practices, only to try to reinvent them in a bad way as we see golang trying to do (no generics - even though many other languages implemented them "correctly", bad error handling, no sum types, null pointers, and so on). They purposely disregarded these works without good arguments at all, and now they're struggling to find workarounds, or continue to dismiss them as non-issues, even though they clearly are.

The way I see it, the moment Java gets fibers and value types implemented, and GraalVM's native compilation is mature enough for use, golang's appeal as a devops langauge where it ended up (however low it is currently - as it's mainly over hyped) becomes even less. C#/.NET is the other alternative making very good progress, and they already have async, value types, and NGEN (though I don't know how mature the latter is for production).

> programming-in-the-large-human-factors

This seems to continually get mentioned without anything to back it up. Sorry, but from what I've seen, Java and C# are strictly superior for "programming in the large". Things like "fast compile times" aren't even a factor in front of Java and C# with their incremental compilation. And the fact that the language is "simple" just means that you will end up with more verbose and complex code, with more places for things to go wrong, and yes I've seen it in larger golang code bases.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: