The "let it crash" philosophy has a misleading name. It's not "don't handle errors", but it's "delegate the error handling to a component dedicated to this".
With OTP, you have what we call a "supervision tree". Your software is divided into components (erlang processes) and organized in a tree where the leafs are your components, and the nodes are the "supervisors" which will catch the error, and restart/retry the operation.
What's the result? Imagine you have a component that is polling data from the database and then sending a message to another component based on that data. For a split second:
- there is a network error
- you lose the connection to the database
- the component crashes
- its supervisor notice the crash, restart the process
If the network error was temporary, everything goes back to normal, and your system did not stop for a temporary network error. And your component does not need to have "retry code" (it is delegated to the parent supervisor).
If the network error was not temporary, the parent supervisor will notice that the component crashes far too often, and will decide to crash as a result. Letting the parent supervisor of the supervisor deal with the problem.
In Erlang/Elixir, processes can "monitor" other processes. They will receive a message when the target processes dies (normally, or abnormally). This allows you to delegate the error handling so yes, you write less defensive code, because that code is located in a dedicated component that will be notified automatically.