3 comments

[ 4.4 ms ] story [ 24.7 ms ] thread
I must be missing the point here, because this feels like bad advice. Calls like `panic` and `expect` and `assert` are there to uphold invariants, i.e. to deal with things that should never happen unless there is a logic error that invalidates the assumptions you hold about your program. It means "if you got here, I (the programmer) probably fucked up badly".

If an operation is fallible, it should return an error type and make it the job of the caller to deal with that. Silently returning default seems like a disaster waiting to happen, ala On Error Resume Next (which indeed was a disaster to deal with).

Now if this panic handler, instead of silently continuing in release mode, invoked some sort of "save your work and restart" handler such that the user doesn't lose anything and the program would restart in a known-good state, that seems like it would be a sensible way of doing things.

This approach is brittle to upstream code changes, and I don't think it should be used, but at least there is an effort to avoid catastrophic behavior (unlike Linus's philosophy for Linux). Both panicking and arbitrarily-nested error bubbling are more standard and recommended, but I think they are still suboptimal, mainly for code quality/maintainability, and should be phased out gradually. I think the viable methods for robust software are promptly handling errors or making errors unrepresentable. To promptly handle errors, the program should be structured such that error bubbling nests shallowly. Unrepresentable errors work within type systems or formal verification, such as the classic advice of "parse, don't validate"[0], giving no way to speak of an error state (so hopefully a cosmic ray never causes it!). These latter approaches are more ideal, but can be a pain to implement.

[0] https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...

Error handling patterns very much depend on the domain with possibly completely opposite recommendations, and any principles should specify in which domain it is a good approach.

Pure and deterministic algorithms can have the luxury of having much more strict and concrete error handling strategies.

On the other hand, "promptly handling errors" sounds naive for large distributed systems where almost every call can fail for reasons unknown. Experience has already shown that exactly the opposite approach - just panic and restart the "service" (process in Erlang terms, but it's got nothing to do with Unix processes and is far more granular) can build extremely reliable systems. In conventional programming languages, that roughly corresponds to adding top-mid-level error boundaries (exception/panic handlers) at critical junctures.

But perhaps that's what you mean by "error bubbling nests shallowly"? No idea, since it's not exactly clear.