Live data from Hacker News

How “let it fail” leads to simpler code

yiming.dev

131–140 of 165 posts

Re: How “let it fail” leads to simpler code

#131
post #14

For all the hate that Java tends to get, the language natively supports this distinction between: * Expected errors - Checked Exceptions * Unexpected errors - Unchecked Exceptions Idiomatic Java also makes heavy use of asserts, e.g. using the Guava Preconditions library.

Checked exceptions require the implementation to distinguish between expected and unexpected errors. But as pointed out in the article, whether an error is expected or unexpected is more a function of the use case than the implementation.

Re: How “let it fail” leads to simpler code

#132
One of the issues I take with this mentality is that companies are always looking for excuses to sell bad software. As an engineer, I really don't enjoy maintaining crappy SaaS, particularly if they are built with weakly typed, dynamic languages.

If something is obviously prone to fail you should empower people to do something about it, because usually "letting it fail" will lead to your users switching to your competitor's offering.

Re: How “let it fail” leads to simpler code

#133
post #122

I was a believer in the "my code should never crash, no matter what" school of thought until I shipped a Dreamcast game with an out-of-date opening cutscene. It was an in-engine opening cutscene which was very nearly final; the file we shipped was about two or three weeks out of date compared against the version that should have gone on the disc (It had one missing shape key on a character's face at the end of a shot…

Or even better, it should be the latter during development and the former in the released version. You don't want your released game to crash in level 11 if the player happens to look behind the wrong lightpole because a texture is missing, but you do want to notice that in development.

I remember back when I messed with D3D (around 8?) I was surprised to learn this is how the Debug build mode of it works. In Debug you can completely screw up your pipeline and scene handling and...everything will work just fine. Or appear to. Switch to a Release build and all those failures are suddenly very obvious - and this is almost exactly what you wouldn't want.

Re: How “let it fail” leads to simpler code

#134
post #8
post #5

I've seen a lot of new developers shocked by this approach, which surprises me a little. They seem to think that it's up to the application to handle all errors, even those of the programmer(s). This, of course, is unreasonable since it would essentially require knowing all the bugs in advance. :-)

Well, there's software that can cause some degree of harm. For example through servos controlling something physical. While you still probably can't catch all of the issues, you damn better try as hard as you can within reason. I'd also wish for similar rigor from people developing whatever filesystens my data is on. :-) Fail fast is generally a good idea, if you can do it safely.

"Litter the code with aborts and test the ever-loving hell out of it" is more or less the strategy we use with flight software.

Re: How “let it fail” leads to simpler code

#135

Earlier quoted context omitted.

Yes, I can guess what the code does. But can you guess what this code will do? 1 + "1" Contrast Python (a strongly typed language): >>> 1 + "1" Traceback (most recent call last): File " ", line 1, in TypeError: unsupported operand type(s) for +: 'int' and 'str' >>> [] + 1 Traceback (most recent call last): File " ", line 1, in TypeError: can only concatenate list (not "int") to list With Javascript (a weakly typed la…

> With Javascript (a weakly typed language): I'm always wary of these, because you can define that as strongly typed if it's the operation which is defined to perform the conversion internally, which IIRC is how it works in javascript. For instance the first example will do the exact same thing in Java, because addition between a string and a non-string is defined as converting the non-string to a string then concate…

> which IIRC is how it works in javascript.

Yes, when objects are involved it's internally translated to:

    ([]).toString() + 1
This can be shown by changing the default implementation:

    > Array.prototype.toString = function() { return 'Boo!'; }
    > [] + 1;
    "Boo!1" 
Changing the prototype for Number doesn't work so I assume there's something slightly different going on there.

Re: How “let it fail” leads to simpler code

#136
I've heard this expressed as "write brittle code", and I'm a strong advocate for it. Looking up a user by id, and get no results? Rather than either passing null up the stack, or wrapping null with an optional.empty, throw an exception! It's the client code's problem if it somehow got hold of an id that doesn't exist. (Yes, ymmv depending on the system, e.g. if you're dealing with eventual consistency then maybe do something different.)

As the article says, this of course doesn't mean that you shouldn't handle user errors, or even known system errors.

Re: How “let it fail” leads to simpler code

#137

Earlier quoted context omitted.

Yes, I can guess what the code does. But can you guess what this code will do? 1 + "1" Contrast Python (a strongly typed language): >>> 1 + "1" Traceback (most recent call last): File " ", line 1, in TypeError: unsupported operand type(s) for +: 'int' and 'str' >>> [] + 1 Traceback (most recent call last): File " ", line 1, in TypeError: can only concatenate list (not "int") to list With Javascript (a weakly typed la…

> 1 + "1" Did someone say PHP?

PHP has a strict mode.

Re: How “let it fail” leads to simpler code

#138

In a new language, I'd like to see exceptions being allowed in pure code, but prohibited in non-pure code. (Non-pure here meaning code with side effects.) In pure code, an exception could essentially be passed up, and transformed into an error return value at the point where it's called by non-pure code.

So then you make a pure function `throw_exception` and now you can throw from impure code.

Re: How “let it fail” leads to simpler code

#139
post #126

Earlier quoted context omitted.

This is exactly why I think all the discussions about the importance of error handling paths (and the aversion drive have to exceptions) are usually overblown. The most successful, and common, error handling strategy is to log and abandon the whole operation, cleaning up everything the operation left around. If you have one process per operation, this is often very well captured by doing exit() at the place of the er…

These discussions happen when people don’t give consideration to the fact that reliability is an architectural concern and error handling is part of that. There’s certainly a minimum of error handling that has to be done in order for code to be considered generally correct, but a lot also depends on the reliability requirements. Sometimes it’s just inappropriate to abort and this may deeply change the architecture of…

Completely agree. Even with "fail fast", there are many levels where this makes sense - for example, you may not want to let an entire server application crash just because one request couldn't be successfully served; but you may still design it to crash and restart for other conditions, rather than trying to recover from more serious errors.

Re: How “let it fail” leads to simpler code

#140
The article mentions Erlang, a functional language - which gives an interesting contrast - it is a functional language which are all about mapping out all behavior so that no undefined behavior can exist, and basically force you to consider every possibility (of course that doesn't account for stuff like network errors).

Wouldn't the same scheme be better suited for a procedural language, with deliberately dirty code full of gotchas?

I write a ton of hacky scripts, like last time I needed to rewrite 1000s of xml-s I wrote a crude regex replace for it. It worked 99% of the time, and I fixed up the rest manually. This sort of thinking - that a subprocess might fail due to whatever reason, including sloppy code - but the whole process will keep trucking on would be perfect for the this paradigm.

Additionally this would open the path for stuff like trivial hot code replacement - since in this system, a subprocess that crashes every time - like an invalid program would be handled by the system.

Post reply on HN