Exception handling would be better than what we're seeing here.
The problem is that any non-trivial software is composition, and encapsulation means most errors aren't recoverable.
We just need easy ways to propagate exceptions out to the appropriate reliability boundary, ie. the transaction/ request/ config loading, and fail it sensibly, with an easily diagnosable message and without crashing the whole process.
C# or unchecked Java exceptions are actually fairly close to ideal for this.
The correct paradigm is "prefer throw to catch" -- requiring devs to check every ret-val just created thousands of opportunities for mistakes to be made.
By contrast, a reliable C# or Java version might have just 3 catch clauses and handle errors arising below sensibly without any developer effort.
Interesting to see Rust error handling flunk out in practice.
It may be that forcing handling at every call tends to makes code verbose, and devs insensitized to bad practice. And the diagnostic Rust provided seems pretty garbage.
There is bad practice here too -- config failure manifesting as request failure, lack of failing to safe, unsafe rollout, lack of observability.
Back to language design & error handling. My informed view is that robustness is best when only major reliability boundaries need to be coded.
This the "throw, don't catch" principle with the addition of catches on key reliability boundaries -- typically high-level interactions where you can meaningfully answer a failure.
For example, this system could have a total of three catch clauses "Error Loading Config" which fails to safe, "Error Handling Request" which answers 5xx, and "Socket Error" which closes the HTTP connection.
> It may be that forcing handling at every call tends to makes code verbose
Rust has a lot of helpers to make it less verbose, even that error they demonstrate could've been written in some form `...code()?` with `?` helper that would have propagated the error forwards.
However I do acknowledge that writing Error types is boring sometimes so people don't bother to change their error types and just unwrap. But even my dinghy little apps for my personal use I do simple serach `unwrap` and make sure I have as few as possible.
I don't understand how your takeaway is that this is a language flaw other than to assume that you have some underlying disdain for Rust. That's fine, but state it clearly please.
The end result would've been the exact same if they "handled" the error: a bunch of 500s. The language being used doesn't matter if an invariant in your system is broken.
Checked exceptions were a reasonable idea, but the Java library implementation & use of these was totally wrong.
Checked exceptions work well for occasional major "expectable" failures -- opening a file, making a network connection.
They work extremely poorly when required for ongoing access or use of IO/ network resources, since this forces failures which are rare & impossible to usefully recover from to be explicitly declared/ caught/ rethrown with great verbosity and negative value added.
All non-trivial software is composition, so the idea of calling code "recovering" from a failure is at odds with encapsulation. What we end up with is business logic which can fail anywhere, can't recover anything, yet all middle layers -- not just the outer transaction boundary -- are forced to catch or declare these exceptions.
Requiring these "technical exceptions" to be pervasively handled is thus not just substantially invalid & pointless, but actually leads to serious rates of faulty error-handling. Measured experience in at least a couple of large codebases is that about 5-10% of catch clauses have fucked implementations either losing the cause or (worse) continue execution with null or erroneous results.
The examples at the start seem confused & poor. A type that "can return either one result, many results or an error" is trying to fit two different cardinalities into a single API.
APIs should either be typed to be unary (possibly with optionality/ error), or plural (allowing 0..many).
I've dealt with similar woolly design before. Introducing clear distinction between cardinalities gave a major improvement in logical clarity.
Interesting. The corruption was in a math.pow() calculation, representing a compressed filesize prior to a file decompression step.
Compressing data, with the increased information density & greater number of CPU instructions involved, seems obviously to increase the exposure to corruption/ bitflips.
What I did wonder was why compress the filesize as an exponent? One would imagine that representing as a floating-point exponent would take lots of cycles, pretty much as many bits, and have nasty precision inaccuracies at larger sizes.
This article is very interesting. I have previously noticed the contrast between strict state representations, and the messier practicalities of user- and externally-sourced data.
I like the insight of splitting Intent and State. There's a lot to the article & I'm not sure I fully understand the latter half yet, but I'm filing this one away for further exploration.
Same here, and I am disappointed to not see much discussion here about the article.
Last year I was working on a multiplayer web game for the first time and I found it very difficult to devise a solution accounting for the fact that player 1 has different state to player 2, which has different state to the backend. I arrived at a solution which entailed sending sequenced commands, but having read this article I now realize that it was the explicit notion of intent that I was searching for.
I don't know, I've met a lot of devs who've found the "true religion" of strict data integrity and have been reluctant to allow for the messier practicalities of user- and externally-sourced data.
None of them called Stanley, but there's definitely a fairly common pattern I recognise.
Yes, there's this amazing thing called "documentation".
Confluence works well if you KISS - write just what the audience needs to know, in terms they can read & find.
Avoid writing grand tracts of wanna-be architecture, policy, theory or planning; just write useful actionable material on topics your audience needs. You will find this works.
Source: I led 400 devs and this was the only way that could scale.
The problem is that any non-trivial software is composition, and encapsulation means most errors aren't recoverable.
We just need easy ways to propagate exceptions out to the appropriate reliability boundary, ie. the transaction/ request/ config loading, and fail it sensibly, with an easily diagnosable message and without crashing the whole process.
C# or unchecked Java exceptions are actually fairly close to ideal for this.
The correct paradigm is "prefer throw to catch" -- requiring devs to check every ret-val just created thousands of opportunities for mistakes to be made.
By contrast, a reliable C# or Java version might have just 3 catch clauses and handle errors arising below sensibly without any developer effort.
https://literatejava.com/exceptions/ten-practices-for-perfec...