7 ms·
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
by latch 10y ago
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 the context of error handling (and system quality), logging and monitoring are the most important thing you can do. Period. Only log actionable items, else you'll start to ignore your logs. Make sure your errors come accompanied by a date (this can be done in your central error handler, or at ingestion time (via logstash or what have you)
3 - Display generic/canned errors to users...errors can contain sensitive information.
4 - Turn errors you run into and fix into test cases.
[1] - http://www.artima.com/intv/handcuffs.html http://www.artima.com/intv/handcuffs.html
- flukus 10y ago> 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?
- biztos 10y agoOnce 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.
- woah 10y agoI think that's in the docs mostly for conciseness. I don't think anyone thinks that it's a good practice in real code.
- aikah 10y agoSo you admit Go error as value is verbose. Because they are. The best system ihmo is Java's. If a method throws then IT should be part of the method signature and dealing with the error (try/catch) should be mandatory. The irony is that Go has some form of (inferior) try/catch with panic/recover. So it has both but still pretends exceptions are bad.
- jerf 10y agoThe problem with checked exceptions is nothing more and nothing less than they didn't work. Yes, in theory, or at least some theories, checked exceptions ought to be awesome. But they aren't in practice. Go's error handling in practice works better than Java checked exceptions. Where fact and theory conflict, fact wins. (I emphasize "checked" because there is a much more robust and interesting discussion about exceptions in general, and then of course a number of sidecar discussions about other error handling mechanisms like Either/Option, etc. I'm only making this claim about checked exceptions. Which is kinda shooting fish in a barrel; arguably C's error handling worked better than Java's checked exceptions and I think C's "error handling" isn't even worthy of the term.)
- Ntrails 10y agoCould 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.
- iotscale 10y ago> Try and log, if you can't, fail silently without impacting the main purpose of the application/service/whatever. Are you being serious? I agree that there can be "some kind of ancillary function that is non-essential" but in the case of failed logging you should try sending an email / showing some warning if you have a GUI / try other outputs / crash with a meaningful error especially if you are running under some sort of supervisor... etc. Of course that doesn't invalidate your main point.
- biztos 10y agoSure. If your function does things internally that might return errors, the normal thing to do is have your function also potentially return an error, namely the first error it finds. If you call a function the error of which isn't a big deal to your function, you'd normally check the return value to make sure it meets your expectations. If it does, fine -- proceed accordingly. If it doesn't, then send the error up the chain. So -- totally bogus example -- say you want to return the mod time of a file or, if the file doesn't exist, the epoch. The file not existing is an error, but not one you'd abort on; other file errors though would be problematic: // ModOrEpoch returns the modification time of the file at path, or the epoch // time if there is no such file. Unexpected file conditions are returned // as errors. func ModOrEpoch(path string) (time.Time, error) { epoch := time.Unix(0, 0) info, err := os.Stat(path) if err != nil { if os.IsNotExist(err) { return epoch, nil } return time.Now(), fmt.Errorf("File error for %s: %s", path, err.Error()) } if info.IsDir() { return time.Now(), fmt.Errorf("File is a directory: %s", path) } return info.ModTime(), nil } https://play.golang.org/p/AMSJMV3tks https://play.golang.org/p/AMSJMV3tks I suppose it's possible you might really, really not care about an error, but that would be extremely un-idiomatic in Go. And, I would argue, a very bad habit in any language where the error could possibly be other than what you expect; sort of like try { foobar(); } catch(e) { /* global thermonuclear war? */ } The thing that bugs me about seeing the errors ignored in official docs is that the Go world puts a lot of emphasis on writing idiomatic code, and the Go "idiom" is much less flexible than, say, Perl's. You might reasonably look to the official documentation to learn what is and isn't idiomatic. And you would hopefully figure out soon enough that ignoring errors isn't, but then again you might not.
- btschaegg 10y agoWhen it comes to 1.), a better way to state things is may be that you shouldn't ignore errors unless you were able to completely handle them. Catching exceptions to throw exceptions with better messages is something I would stronly suggest, since almost no exceptions are useful without contextual information. I.e. which file was not found? The config, not the input or output. Things like this. This is especially useful in C++, where you don't get stack traces, but also in other languages you'll want to present non-technical (i.e. non-dev) users with meaningful messages. Stack traces will just frighten them off. Your comment on sensitive information also plays into this. I'll agree that just swallowing errors and going on is a recipe for disaster. This is something that regularly bugged me in most of the C code I've encountered so far.
- pjc50 10y agoYou can sometimes get stack traces in C++, but it's platform and compiler specific. Gcc's system is pretty good. (I have some code for WinCE that walks stack traces in conjunction with SEH so that crashes in production - segfault etc - get logged in a useful manner. It does rely on parsing and decoding instructions ...)
- narag 10y agoCatching exceptions to throw exceptions with better messages is something I would stronly suggest Also not every user in the world understand English. I catch exceptions in two levels. One is the user initiated action. I seldom see this specified, but it seems obvious to me: if the user decides to do X, either X is done or a clear and meaningful message is shown, explaining what and why could not be done. There is another finer grained location to do what you say (collecting details), logging and then re-raising up to the other level. Swallowing errors is evil and no, not limited to C code.
- btschaegg 10y agoInteresting point. I have to confess that I've never seen logging done usable in a i18n sense. Of course, for UI applications, you're absolutely right. When it comes to logging details, you have a point. But I still think a clear final error message is something to aspire to - especally if you're writing very busy and potentially multithreaded (or - even worse - async) services. On this note, filtering logs according to thread IDs can be very helpful here. I wonder if that is also easily possible with "fibers". > Swallowing errors is evil and no, not limited to C code. Of course, nothing ever is - it's just where I've seen this thing the most (highly anecdotal evidence, I know :-) ). Unfortunately, the nature of many C APIs also makes it very non-obvious if you're skipping through the code, whereas an empty catch statement stands out somewhat.
- wyager 10y agoMonad 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.
- jstimpfle 10y agoAre 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.
- fnl 10y agoStandard "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-report-errors/ http://www.randomhacks.net/2007/03/10/haskell-8-ways-to-repo...)
- jstimpfle 10y agoSource? I was under the impression that it does (apart from things like NullPointerException, which of course has Haskell equivalents). And I've had to do a significant project in Java.
- BoorishBears 10y agoYou mean Unchecked Exceptions (that extend from RuntimeException) vs Checked Exceptions?
- gbog 10y ago> 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?
- lmm 10y agoSet a threshold, and log only once you hit that threshold.
- softawre 10y agoAnd keep track of that state across 20 different instances? What we do is just log the failure and have a system like New Relic monitoring everything so that it can alert us when we hit 20% network failure.
- lmm 10y agoSure - but then the developer-facing "log" is the New Relic interface, and your instances transmit failure information to it via some API (I mean I suppose you could have one program output a plain-text log file and then another program or service parse that to figure out how many errors were happening, but you wouldn't do that for any other kind of inter-system communication).
- koonsolo 10y agoFor point nr 4, at our company we tracked how many bugs that were fixed, came back afterwards (project with 80 developers for more than a decade old code base). We saw it's very rare that the same bug (after the fix) will pop up twice, and therefore decided not to add or write regression tests on them, and focus our testing efforts where it mattered more.
- hvidgaard 10y agoIt depends on what kind of error it is. If it is an off by one error, there isn't much you can do to test it, but if it is an input edge case you didn't handle correctly, then a test make a lot of sense, if for nothing else to verify the fix.
- lisivka 10y agoOf course, it is rare that same bug will pop up twice, but bug demonstrates areas which are not covered by tests, so new test(s) will cover these areas and will catch an other bug(s).
- deleted 10y ago[deleted]
- eikenberry 10y agoIf 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.
- barrkel 10y agoConcurrency means you can't prevent errors. Every time you open a file, it could have been deleted out from underneath you, in a race with some other process. Most files a program opens are not as a result of user action: configuration, libraries, resources, etc. And usually it doesn't make sense to catch these errors at the point of occurrence, because they'll be all over the codebase. And there's very little you can do in response to them.
- btschaegg 10y agoI'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.
- eikenberry 10y agoYou "allow" it by doing something that can potentially give an error without handling it there. I'm referring to the parent's recommendation "Don't catch errors unless you can actually handle them" .. and why you wouldn't handle them. You should always handle them. To put it another way, I disagree with the design decision of depending a top level/global exception handler.