Error Handling
What is an exception ?
Exceptions are Ruby’s way to deal with unexpected events.
If you’ve ever made a typo in your code causing your program to crash with a message like SyntaxError or NoMethodError, then you’ve seen exceptions in action.
When you raise an exception in Ruby, the world stops and your program starts to shut down. If nothing stops the process, your program will eventually exit with an error message.
Crashing programs tend to make our users angry. So we normally want to stop this shutdown process, and react to the error intelligently.
This is called “rescuing,” “handling,” or “catching” an exception.
The exception still happens, but it doesn’t cause the program to crash because it was “rescued.” Instead of exiting, Ruby runs the code in the rescue block, which prints out a message.
This is nice, but it has one big limitation. It tells us “something went wrong,” without letting us know what went wrong.
Rescuing All Exceptions (the bad way)
begin
# logic
rescue => e
# log error
end
This is considered a bad pattern when used by default.We rescue EVERYTHING which is a kind of safe guard but in the meantime:
- Prevent to enforce any path depending on which error has been raised
- Prevent to enforce thinking of the different scenario, global rescue handle anything
- Prevent to enforce separation of concern (http://rubyblog.pro/2017/05/solid-single-responsibility-principle-by-example) as errors from ActiveRecord, Api … are all mixed together.
Rescuing All Errors (The Right Way)
begin
user = User.find()
user.update!
rescue => ActiveRecord::NotFound
# logic if no user
rescue => ActiveRecord::RecordInvalid
# logic if can't update the user
end
As we’ve broken down the possible errors we can decide what to do, in most use case if we call “find” in a controller, we can let the default ActiveRecord::NotFound handle the exception, remain:
class ApplicationController < ActionController::Base
rescue_from :not_found, ActiveRecord::NotFound
endclass ExampleController < ApplicationController
def update
user = User.find()
if user.update
## happy path
else
## failure path
end
end
end
rescue is gone from the specific controller.
Most of the library provide hooks or error handling
- Controller => default rescue
- Job => failure hook
- …
The Let It Crash Philosophy
Unless your code require to be paranoid (eg: Payment process) allow it to crash if something wrong happen
The worst thing software can do when encountering a fault is to continue silently. Unexpected faults should result in verbose exceptions that get logged and reported to an error tracking system.
http://stratus3d.com/blog/2020/01/20/applying-the-let-it-crash-philosophy-outside-erlang/https://www.amberbit.com/blog/2019/7/26/the-misunderstanding-of-let-it-crash/
Conclusion
In the same tone tests are there to protect our code and help the your fellow developers to know what is happening in the code. Error handling allow them to understand what are the different scenarios and errors they should be aware of.
source:
https://www.honeybadger.io/blog/a-beginner-s-guide-to-exceptions-in-ruby/