Live data from Hacker News

Error Handling in Node.js

joyent.com

41–50 of 96 posts

Re: Error Handling in Node.js

#41

Some of this looks like horrible advice, particularly the defeatist attitude towards what the article calls "programmer errors". Statements to the effect that you can never anticipate or handle a logic error sensibly so the only thing you should ever do is crash immediately are hard to take seriously in 2016. What about auto-saving recovery data first? Logging diagnostic information? Restarting essential services in…

The "let it crash" philosophy assumes that there is some external system monitoring & restarting the program that crashes. They mention this explicitly in the article, but it's worth repeating: you need this external system anyway. Your program may stop executing for all sorts of reasons other than a bug in your program, from bugs in your dependencies to uncaught errors to infinite loops to cosmic rays to someone tripping over the power cord to an earthquake destroying the entire U.S. west coast. Your distributed system needs to handle these as operational errors, and in extreme cases you might not even have power available for 1000 miles; there is no possible way that a single process could recover from that.

They also recommend configuring Node to dump core on programmer error, which includes (literally) all of the diagnostic information available on the server.

Re: Error Handling in Node.js

#42
post #19

I'm not really sure how to phrase this constructively, but this is horrible. Not the article, just the fact that humans expose themselves to this sort of stuff. Why would you choose to use a language that makes something as mundane as error handling this ridiculous and unpleasant?

There is nothing mundane about error handling, in fact it's one of the hardest things to get right in a programming language (see Rust's error handling saga for instance). There is no language I know of where error handling is both simple and not overbearing.

The most annoying thing about all this is that the central argument of this article "Separate recoverable errors from bugs" never made it to a widely used imperative language. C# had the opportunity but blew it.

Java mixed the two kinds of exceptions up completely and checked exceptions just added insult to that injury.

The best implementation I have seen for an imperative language is in Midori (The language used in Microsofts research OS with the same name).

http://joeduffyblog.com/2016/02/07/the-error-model/#bugs-are...

It's basically "C# done right". The blog post is well worth reading.

Re: Error Handling in Node.js

#43
post #8

My non-node specific suggestions: 1 - Don't catch errors unless you can actually handle them (and chances are, you can't handle them). Let them bubble up to a global handler, where you can have centralized logging. There's a fairly old discussion with Anders Hejlsberg that talks about this in the context of Java's miserable checked exception that I recommend [1]. This is also why, in my mind, Go gets it wrong. 2 - In…

If you can't handle an error, why would you allow it. It makes no sense. As a developer you have control of the errors/exceptions that get raised and raising an error that you can't deal with seems.. bad.

I'm not sure if I understand you correctly. What do you mean by "allow it"?

I would even define an error to be a situation the code can't handle properly, and those things are usually not under your control (if I rip out a harddisk while some program is running, the developer can't do anything against that - but I would hope that the program fails accordingly and states why it failed).

I'm obviously paraphrasing here, but things like this do happen (USB devices get disconnected, remote services go down etc).

Edit: Obviously, this applies to different code at different levels. Serialization code might fail due to input - but that also is not under the control of the dev writing the serialization logic. Thus, it should fail.

Re: Error Handling in Node.js

#44
post #21

Some of this looks like horrible advice, particularly the defeatist attitude towards what the article calls "programmer errors". Statements to the effect that you can never anticipate or handle a logic error sensibly so the only thing you should ever do is crash immediately are hard to take seriously in 2016. What about auto-saving recovery data first? Logging diagnostic information? Restarting essential services in…

What about auto-saving recovering data? It really depends upon the language and environment used. I work with C (almost legacy code at this point), and if the program generates a segfault, there is no way to safely store any data (for all I know, it could have been trying to auto-save recovery data when it happened). About the best I can hope for is that it shows itself during testing but hey, things slip into produc…

> I'm not a fan of defensive programming as it can hide an obvious bug for a long time (I consider it a Good Thing that the program crashed otherwise we might have gone months, or even years, with noticing the actual bug).

I've had segfaults "hidden" for a long time because my artist coworkers weren't reporting crashes in their tools. They assumed a 5 minute fix was something really complicated. Non-defensive programming is no panacea here. Worse, non-defensive programming often meant crashes well after the initial problem anyways, when all sane context was lost.

My takeaway here is that I need to automatically collect crashes - and other failures - instead of relying on end users to report the problem. This is entirely compatible with defensive programming - right now I'm looking at sentry.io and it's competitors (and what I might consider rolling myself) to hook up as a reporting back end for yet another assertion library (since none of them bother with C++ bindings.) On a previous codebase, we had an assert-ish macro:

  ..._CHECKFAIL( precondition, description, onPreconditionFailed );
Which let code like this (to invent a very bad example) not fatally crash:

  ..._CHECKFAIL( texture, "Corrupt or missing texture - failed to load [" 
Instead of giving me a crash deep in my rendering pipeline minutes after loading with no context as to what texture might be missing. Make it annoying as a crash in your internal builds and it will be triaged as a crash. Or even more severely, possibly, if simply hitting the assert automatically opens a bug in your DB and assigns your leads/managers to triage it and CCs QA, whoever committed last, and everyone who reviewed last commit ;)

> Logging is an art.

You're right, and it's hard. However. It's very easy to do better than not logging at all.

And I think something similar applies to defensive programming. You want null to crash your program? Do so explicitly, maybe with an error message describing what assumption was violated, preferably in release too instead of adding a possible security vulnerability to your codebase: http://blog.llvm.org/2011/05/what-every-c-programmer-should-... . Basically, always enabled fatal asserts.

This might even be a bit easier than logging - it's hard to pack too much information into a fatal assert. After all, there's only going to be one of them per run.

Re: Error Handling in Node.js

#45

Why the suggestion to use an error's name rather then instaneof and the error's class?

Error.prototype.toString() reads e.name, not e.prototype.constructor.name, so you can't rely on everyone to have subclassed Error.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

Re: Error Handling in Node.js

#46
post #22
post #8

My non-node specific suggestions: 1 - Don't catch errors unless you can actually handle them (and chances are, you can't handle them). Let them bubble up to a global handler, where you can have centralized logging. There's a fairly old discussion with Anders Hejlsberg that talks about this in the context of Java's miserable checked exception that I recommend [1]. This is also why, in my mind, Go gets it wrong. 2 - In…

> Only log actionable items Easy to say but much harder to implement. For example, if you communicate with another service, a few network errors are usually not actionable and you'd have some fallback mechanism in you code. But tons of network errors (e.g. > 20%) is a problem that needs to be fixed now. So would you log the network error or not?

Set a threshold, and log only once you hit that threshold.

Re: Error Handling in Node.js

#47
post #20
post #8

My non-node specific suggestions: 1 - Don't catch errors unless you can actually handle them (and chances are, you can't handle them). Let them bubble up to a global handler, where you can have centralized logging. There's a fairly old discussion with Anders Hejlsberg that talks about this in the context of Java's miserable checked exception that I recommend [1]. This is also why, in my mind, Go gets it wrong. 2 - In…

Monad transformers offer a more disciplined and pleasant alternative to your #1. You should handle errors at the value level, not with some magic error passing system provided by your language. You can catch the errors at whatever level you like, and you are forced to deal with them properly by construction.

Are you talking about things like EitherT? IMO here isn't so much of a difference to exception handling. Both approaches make it hard to see at a glance where most code could fail, and you can (but are not encouraged to) transform errors explicitly.

Add Java's checked exceptions, now the practical differences are quite subtle. Of course it's nice to be able to be able to do transformations with higher level functions.

Re: Error Handling in Node.js

#48
post #33
post #14

Earlier quoted context omitted.

> This is also why, in my mind, Go gets it wrong. Considering the amount of places I see c# code catch errors and continue as though nothing happened I have to wonder how many wild go and c programmers simply ignore error codes?

Once I got used to it I found I kind of like the Go paradigm of checking for errors every time something could go wrong, and (usually) passing the first one up the chain with some additional context info. However, the fact that "ignore error" is an easy and built-in paradigm that even shows up in the official docs: fragileThing, _ := scary.MightNotWork() that fills me with dread.

Could you elaborate?

Assuming that scary.MightNotWork() is some kind of ancillary function that is non-essential, why would I want to let it impact the main program. The example that comes to mind would be logging. If I have set up my own "Write logs into network share" call, I'd never ever expect it to throw errors that took down the app. Share down? Don't care. logfile locked/corrupt. Don't care. Try and log, if you can't, fail silently without impacting the main purpose of the application/service/whatever.

Re: Error Handling in Node.js

#49
post #33
post #14

Earlier quoted context omitted.

> This is also why, in my mind, Go gets it wrong. Considering the amount of places I see c# code catch errors and continue as though nothing happened I have to wonder how many wild go and c programmers simply ignore error codes?

Once I got used to it I found I kind of like the Go paradigm of checking for errors every time something could go wrong, and (usually) passing the first one up the chain with some additional context info. However, the fact that "ignore error" is an easy and built-in paradigm that even shows up in the official docs: fragileThing, _ := scary.MightNotWork() that fills me with dread.

I think that's in the docs mostly for conciseness. I don't think anyone thinks that it's a good practice in real code.

Re: Error Handling in Node.js

#50
post #20

Earlier quoted context omitted.

Monad transformers offer a more disciplined and pleasant alternative to your #1. You should handle errors at the value level, not with some magic error passing system provided by your language. You can catch the errors at whatever level you like, and you are forced to deal with them properly by construction.

Are you talking about things like EitherT? IMO here isn't so much of a difference to exception handling. Both approaches make it hard to see at a glance where most code could fail, and you can (but are not encouraged to) transform errors explicitly. Add Java's checked exceptions, now the practical differences are quite subtle. Of course it's nice to be able to be able to do transformations with higher level functions…

Standard "canned" reply: Java doesn't force you to annotate exceptions or even to handle them all. Typed returns do. And as to the "where", well, in the originating function.

But that gets us to some real criticism here: You run the danger of getting an Either Monad out of about every function in your code-base... So maybe throwing exceptions isn't the worst thing after all. (/me ducks all the stuff being thrown my way from hardcore [EDIT:] ~Haskell~ functional programming fans :-))

EDIT: s/Haskell/functional programming/ (because its unfair - Haskell can throw stuff: http://www.randomhacks.net/2007/03/10/haskell-8-ways-to-repo...)

Post reply on HN