I'm a Floridian web engineer who planted his roots in Paris. Some of my favorite past-times are analyzing and optimizing development processes, building solid APIs, mentoring junior devs, and long walks on the beach.

Exceptional code

When I started programming, I never gave much thought to exceptions. I did not realize the benefit. If you write mainly interpreted scripts, they may seem like any other error that explodes during execution. Of course the difference, is that they are controlled explosions.

Some of the primary benefits are that they:

  • Allow you to expect some sort of known potential failure.
  • Allow you to know where the failure came from.
  • Allow you to fail gracefully.

The bad and the ugly

One of the worst things that I've seen is what follows:


def source
  begin
    risky_business
  rescue 
    nil
  end
end

The problem here, is that instead of handling failure, we just ignore it. There is no way to know what happens or why it happens. If risky_business normally returns nil, we may also think that function has passed when it really fails.

The next snippit is better, but still not without its problems.


def source
  raise "The source of all your problems"
end

def bad_exception_catcher
  begin
    source
  rescue Exception => e
    clean_up_the_stuffs
    raise e.message
  end
end

The problem here is a little more subtle. It is clear that we are taking advantage of being able to clean up problem code, however, there is one key problem. We are creating a new exception when we "raise e.message". This means that later in the logs, this call will be the source of the backtrace and not the original source function. If our source function is just one line, then there is no problem, but the more complicated that source function is, the more difficult it will be to pinpoint the problem.

A way to get around this problem, is to just reraise the original exception. In that way we will keep the original backtrace history.


def bad_exception_catcher
  begin
    source
  rescue Exception => e
    clean_up_the_stuffs
    raise e
  end
end

Testing

Typically, exceptions are something that you can and should test for. Also, if you are using ruby and something like rspec, exception testing is supported out of the box.


it "should raise error if user has no email" do
  user = OpenStruct.new(email: nil, name: 'A. Person', uid: 10244201)
  FbGraph2::User.stubs(:me).returns(user)
  expect { user = User.facebook_get_user('access_token') }.to raise_exception(
    LoginException, 'Please use an account with a valid email'
  )
end

In testing for specific exceptions, you can also assure that you don't get unexpected exceptions.