Live data from Hacker News

How “let it fail” leads to simpler code

yiming.dev

121–130 of 165 posts

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

#121
post #114

I struggle to find the correct descriptor for a counter-example, wherein You Really Want Success for the process as a whole, but it is acceptable for a sliver of it to fail, in the the context of ETL. I have an ETL I am told (I switched jobs) that is still working, from 2008. It was built to be a tank, and I also did another forbidden thing: Pokemon Exception Handling. It's a guideline, not a law of physics, and it i…

Funny thing, I had same experience. I also built robust ETL. It was for ingesting and manipulating financial data from 30+ different banks and I also did Pokemon Exception Handling to make it robust. In general my philosophy is: I don't want to wake up at 4am unless it's urgent. What can I do to gracefully handle failures to achieve that goal?

Exactly, thank you. At the time I was transitioning from a jack of all trades person to strictly a programmer. I despise getting called in the middle of the night.

My feeling is that, if you have a foundational business process like this, it should be designed to be maintained if there is a serious problem, and it ought to keep working. I know, haha, "the Internet is a series of tubes" but I really wanted this thing to be like a chunk of very uninteresting ductwork that just moves air from one place to another: it should just do its job with as much fanfare.

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

#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, and a couple other missing elements). My code was wrangling the whole animation; doing all the stuff which our at-the-time-primitive animation system couldn't do itself (animating texture coordinates and etc). And my code was just silently handling all the errors it ran into so that we never even noticed that anything was wrong.

The difference was subtle enough that in the twelve years since the game was released, nobody but the original animator has ever noticed and mentioned it to me (and that, years after release). But that one experience and knowing how much worse it could have been was enough to convince me that "crashes early and crashes loudly with as much detail as possible" is by far the better strategy. At least for entertainment products. And doubly so for entertainment products which can't be patched after release.

(for clarity, this screw-up was 100% my fault. The animators had made the final changes to the cutscene data files in plenty of time for inclusion in the final build, I just somehow didn't import the changed data files into the game when I made the matching changes to the code side, and then my code didn't throw any errors to tell me or anyone else on the project that anything was wrong.)

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

#123

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…

Funny that you mention cleanup. Our service doesn't clean up in any circumstance. Originally we had a cleanup operation in case of errors. But then we found those could fail as well. As it turns out, we need a catch-all way to clean up resources if anything else fails. The solution is simple. The main service never cleans up, and a secondary service (I like to call it The Reaper) just cleans up orphaned resources. Th…

Garbage collector?

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

#124
post #93

Earlier quoted context omitted.

My guess would be add assertions everywhere instead of throwing exceptions.

Why not throw exceptions, and just never use try/catch? That way, all exceptions are uncaught and should terminate the program, in a way that takes advantage of the programming language's native error reporting facilities.

assertion failures terminate the program immediately.

exceptions usually trigger cleanup code.

If your cleanup code is mostly closing files and clearing memory, then it's useless because the OS will do that for your crashed program anyway.

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

#125
post #95

Earlier quoted context omitted.

Totally agree. Now, I’ve actually found functional programming - specifically, Either - to be of great use in helping me focus only on the happy path.

Yes, with some kind of monadic-like control flow (either actual monads or even Rust's ? macro), those can be also achieve this workflow pretty well. Edit to add: I still think exceptions are better in practice, as you also get a stack trace when the failure happens, whereas Either and ? don't really help track down the error unless you add code to create a manual "stack trace".

In rust, you can use the excellent anyhow crate https://docs.rs/anyhow/latest/anyhow/ . It has various ways to add context to an error, and will automatically attach a stack trace with the backtrace feature.

Explicit or implicit panic of course also attaches a backtrace. It can also be caught, although that is a can of worms. So panicing is the closest thing rust has to exceptions - somewhat similar to java.lang.Error on the jvm. https://docs.oracle.com/javase/7/docs/api/java/lang/Error.ht...

With anyhow, error handling in rust really is quite pleasant.

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

#126

One of the pieces of software I'm most proud of is a service to manage the dynamic part of our infrastructure. It uses control theory and let it fail to great effect. The service reads the state of the system, and applies change to converge to a configured policy. If it encounter an error, it doesn't try to handle or fix it, it just fails and logs a fine grained metric, plus a general error metric. The system fails a…

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 a program, including by making hard demands on the toolchain, hardware and OS.

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

#127
post #57

Earlier quoted context omitted.

Can you guess what this code does? class foo: pass obj = foo() obj.bar = "I thought Python was strongly typed?" print(obj.bar) And even better: class foo: a = 42 obj = foo() print(obj.a) del foo.a print(obj.a) Whatever your opinion on what the imprecise sentence "strongly typed language" should mean, these are definitely not features of one.

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

The second operation is not defined such in Java, but in theory you could have a universal toNumber protocol and define the addition of a non-integer and an integer as converting the non-number to a number then adding.

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

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

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

#129
post #7
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. :-)

I'm a big fan of the "crash early" strategy. I write in Swift primarily, and if I suspect a state is impossible to reach, I'll add a fatalError() so that in development, if it turns out I'm wrong, I spot it right away. (Something I learned from another dev I worked with, who was very productive.) Unfortunately, a lot of other devs hate to see that your code may actually crash and start asking questions about what sce…

Same here.. esp. in server based code it makes no sense to not fail early, even on the slightest issues. If you have proper logging / notifications, you'll code be more robust.

Had to deal with the same issue as you.. other devs and managers don't like those errors.. but it makes things fragile and more difficult to troubleshoot.

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

#130
Dataflow offers an interesting variation of not polluting the happy path with error handling: just do nothing.

How so? Well, the happy path passes data to the next filter. In the case of an error, simply do not do that, and the next filter will be none-the-wiser.

Optionally, log the error, possibly with enough information that the "logger" could retry the operation if appropriate. But as others have pointed out, there often isn't much that can be done.

Post reply on HN