I think the strength of nondefensive programming is being overstated. For starters, I would much rather take the compiler telling me what errors to handle over Erlang’s style. But let’s take it that we are in Erlang anyways. The Let It Crash philosophy needs a border of sanity around it. The errors are useless to an end user. And if you aren’t careful, your errors become almost impossible to translate to meaningful errors for your end users. For example, not being able to open a file will be a badmatch error in his example. How do you, up several layers, turn that into a error your user can understand? Are you building a web server and couldn’t open the file they are requesting or is it an auxiliary file that failed to open?
So yes, Let It Crash has a lot of benefits, but none of them involve the user experience, so make sure you can still provide a good user experience.
have you used erlang’s type system and dialyzer much? I find that dialyzer tends to find essentially all of the bugs that I would have expected to find with every other statically typed language except for haskell.
as for the user experience – if you mean the system operator, I wholeheartedly agree that erlang can do better (and I believe elixir is working on it) at providing actionable information on crashes. Tupletraces, badmatch and function_call are tough even for erlang developers. If you mean users, hopefully you are catching exceptions and providing something better than stack traces.
If you mean users, hopefully you are catching exceptions and providing something better than stack traces.
Yes, but my point is what meaningful error do you turn badmatch exception into?
The same thing you turn segmentation fault or null pointer exception into: guru meditations…
I imagine a non-useful error message would be better user experience than a crash.
Well it gets worse when you can possibly have confidential information in there. For example, Erlang stacktraces include parameters to functions being called. What if you crash on connecting to your database? How do you know that’s a bad error message to show your users?
To be fair, he does say you should check the results for cases where there’s a legitimate reason to suspect something might go both ways, such as a file on a web server.
the elephant in the room for let-it-crash is process state. If your process exits and has to be restarted, then you have lost any state that you had, and perhaps have to rebuild or re-obtain it from a persistent store. And, to ameliorate that, you might have to proactively defend yourself by checking your state in somewhere periodically. And at that point you have an infinite turtle generator.
And this also gets worse when your process is busy, because perhaps it had a message queue that was non-zero. That’s state too. Everything waiting for that process to come back with an answer is now either itself restarting in a resonant supervisor cascade or might be dealing with an unexpected situation that will soon cause it to restart.
For flighty, stateless gen_servers which are just doing routing or peeling protocol onions, fuck yeah, let it crash. For state-y servers that would be a pain in the ass to reload from, say, a sql database across a network, the philosophy gets a little more muddy. But that’s why jlouis says there are exceptions to every rule I suppose. :)
I have seen this before, but it is a good one, and what the author is talking about is one of the better parts of Erlang. When I first started programming in Erlang, my code was littered with error handling. Figuring out that process supervision could replace some of that was a long process, filled with spaghetti code and refactorings.
As someone who has never used Erlang, but am incredibly interested: can you or anyone else recommend a gentle introduction to process supervision? Most of the articles I read basically say “don’t over-handle errors; crash and let the supervisor take care of things” but then never get around to talking about how the supervisor actually takes care of things.
http://learnyousomeerlang.com/supervisors might be a good intro
Couldn’t agree more. I went through this 3 or 4 times, picking up more and more each time.
I use a similar technique in my C++ programs. I have a supervisor program that restarts if there is a crash. I then use design by contract to check conditions. If a contract fails, the application crashes and then gets restarted by the supervisor.
However, i still do the traditional defensive programming for the outer shell that deals with IO, only because I want better error messages.
a similar philosophy from the ruby world: http://www.confidentruby.com/ (not “let it crash” but “program confidently rather than scattering defensive code around every statement”)