Hacker News new | ask | show | jobs
by stcredzero 2544 days ago
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.

1 comments

> 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.