Live data from Hacker News

How “let it fail” leads to simpler code

yiming.dev

81–90 of 165 posts

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

#81
post #78

Earlier quoted context omitted.

I'd imagine retrying/reconnecting is compatible with the general "let it fail" approach. If you just sent a message/request to an actor/server and it still hasn't responded after 5 seconds, you can send another. It wouldn't matter whether that actor/server died from a regular error or a "let it fail" error, the retrying would still work the same.

> If you just sent a message/request to an actor/server and it still hasn't responded after 5 seconds, you can send another. This depends on the message. I hope amazon doesn't just send another message if my transaction didn't complete in 5 seconds. I think like all pieces of wisdom, sometimes it's OK to let it fail, and sometimes its OK to handle the errors. If anyone ever tells me to always do X or never do X, it's…

> I hope amazon doesn't just send another message if my transaction didn't complete in 5 seconds.

I do, but only while also associating a stable nonce to the transaction.

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

#82

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 error. If you don't, then exceptions are the best approximation of this pattern - much better than result types or error codes, which litter your code with irrelevant error handling details.

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

#83
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…

JS's typing is weaker than Python's, but Python still has a relatively weak type system - particularly in old-style classes as GP was showing (class foo(object)-style classes fix those problems, or at least some of them) EDIT: I was wrong about new-style classes fixing this. Other dynamic languages are stronger than both - for example Common Lisp.

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

#84
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…

> 1 + "1"

Did someone say PHP?

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

#85
post #37
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 was on a team for a short while (Java programmers) and their frontend code was really overly "careful". For example, they would always check if a method existed, before calling it. var o = new SomeObject(); if (o.computeSomething != null && o.computeSomething != undefined) { o.computeSomething(...); } Their reasoning was that in JavaScript (with the old syntax) you just add functions to the prototype, so you could…

they did indent by 3 spaces

Probably a compromise between 2 and 4?

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

#86

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…

JS's typing is weaker than Python's, but Python still has a relatively weak type system - particularly in old-style classes as GP was showing (class foo(object)-style classes fix those problems, or at least some of them) EDIT: I was wrong about new-style classes fixing this. Other dynamic languages are stronger than both - for example Common Lisp.

There are no old-style classes in Python 3.

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

#87
post #86

Earlier quoted context omitted.

JS's typing is weaker than Python's, but Python still has a relatively weak type system - particularly in old-style classes as GP was showing (class foo(object)-style classes fix those problems, or at least some of them) EDIT: I was wrong about new-style classes fixing this. Other dynamic languages are stronger than both - for example Common Lisp.

There are no old-style classes in Python 3.

Oops, you're right, and the problem isn't fixed by new-style classes. I must have misremembered something.

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

#88

A corollary or generalized interpretation of this approach (and someone please specify if there’s a formal term for this) is: “fail locally, and immediately.” What I mean is that once something unexpected happens your code should ideally fail in that step itself. The simplest most common example I’ve seen with python programmers is when they pass around dicts as arguments in complex code bases. Methods expect various…

In general, I think using real classes with a single central definition instead of raw manually created dicts is the solution.

A thorough test suite is also needed, of course.

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

#89
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 is fine to resort to a general error catch when you really don't know every possible error (and let's be honest, if you have enough libraries in the mix, some surprises will happen) and you want the other 99.999% of the data to go through. Yes, this one little thing didn't load, and let's log that, let's examine that and figure out how to prevent that going forward, but overall, the rest of the program must continue.

How did it get so tanklike? Every time a little bit failed and it got logged, I figured out what went wrong, fixed it, and then tried to generalize a class of similar errors. After a while, I got into Things I Was Told Would Never Happen in the data we ingested and programmed for when never happened. Reader, never came a little sooner than expected.

Anyway, I largely agree with the idea but there are places where you want the exact opposite, and I think it is important to look for those places lest this heuristic become so stiff it can lose utility.

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

#90
post #35

The article doesn’t seem to look at how resources are cleaned up when a BEAM process crashes. https://elixirforum.com/t/understanding-the-advantages-of-le... says “All resources are owned by a process in Erlang, and the VM guarantees clean-up of resources once the process dies”. My Google-fu failed me when I searched for more details about Erlang process cleanup of resources, or how to register cleanup actions (e.g.…

I am not sure what you mean by that. Which resources do you mean? Everything happening in your program is running inside the Erlang VM, even file writing... And each process inside the VM does their own garbage collection. The only way I could see the VM itself crashing is if it can't allocate memory, then it all crashes.

Let’s assume we are using Linux.

Firstly, an Erlang VM process is not a Linux child process: “Erlang processes are lightweight, operate in (memory) isolation from other processes, and are scheduled by Erlang's Virtual Machine (VM). The creation time of process is very low, the memory footprint of a just spawned process is very small, and a single Erlang VM can have millions of processes running.”.

An Erlang program can use kernel resources. Objects related to file descriptors, filesystem data, the process namespace, and signal handlers. Kernel timers, semaphores, memory allocations (e.g. for FFI), sockets, DMA, etcetera.

I presume the Erlang library code tracks resources (such as file access function tracking file descriptors per Erlang process), so they can be cleaned up if we “let-it-fail”. I also presume there is a canonical way to register to clean up resources - the equivalent to atexit()[1].

[1] https://man7.org/linux/man-pages/man3/atexit.3.html

Post reply on HN