Proper Way to Implement Error Management in a Rails Application
Proper error management is important when it comes to Rails development. Being able to rescue errors is helpful, however it can also be dangerous when implemented poorly. In this guide we'll walk through how to properly integrate error management in Rails.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

Looking at pivotal tracker, you can see our next task is to talk about error management. What exactly is error management? I think one of the best ways of seeing how this works is to take a dead simple base case scenario. Let's extend our prior knowledge and start a pry Session.

I'm going to create a very basic method. I'm going to create a divide method and it's going to take two arguments, num1 and num2 then num1/num2.

If I try to use a method so if I say divide 5 by 5 this is going to return one. Everything works perfectly fine. If I say divide 5 by zero this is going to return an error, it doesn't return just a regular error it returns a zero division error because we divided by zero.

If you didn't want this to show up you could redefine your method and say def divide num1, num2. Inside of it instead of just putting your implementation you could say begin and inside of this block we can put the exact same code so num1 / num2. Now I can type in what happens whenever there is an error. I could say rescue => e. E is going to be to be our error variable. Now we can create our own custom error message and the e is going to allow it to be dynamic.

Now if I say puts "Error Occurred: {e}" I can type end twice. Now if we try to do divide 5, 0 you can see that it doesn't give us the same 0 division error. Actually, it doesn't even error out. All that happens is that our content here got replaced, it just simply gives the error message that we created where we said error occurred just like that.

Now technically it also is possible to implement something like this where I say

medium

If I try divide 5, 0 it doesn't even print anything out. It doesn't return a value obviously because you can't divide by zero. However, we don't have any kind of error message. One thing I want to be very clear on when we talk about error management we're not talking about simply hiding the fact that an error occurred. When we get into the rails part of the demo this is going to make more sense because what we just did there in the second example, this is actually incredibly dangerous. I'm going to show you exactly why now.

Let's clear out of here and I'm going to start up the rails server. Switching to sublime text, I will open up our user.rb file. I think this is a good example of how we want this to work. I'm going to open an incognito window and let's just go to the homepage. Right now, because no users logged in it just says hi guest but let's imagine that we didn't put this validation in for the presence of names.

I'm going to remove that so I can create a user without a name. I'm going to register and click sign up. This doesn't error out right here just because there's not going to be. So far so good even though we don't have a name we haven't caused any big bugs. Now let's say that we create some implementation code, say we add a new method and for the sake of demonstration purposes I'm going to say the method is called oops.

This just means that we misspelled a method we had some kind of syntax error. If I come back here now we are going to get an error and it says undefined method oops for nil class. How can we fix this? If we take a naive approach we could simply say oh no there's an error in our program and say begin and then put all of this inside of a rescue block. I can say rescue, and then just end again.

large

If I come back here you can see OK we're all good there is no error. That's actually a horrible practice. the reason is because errors are not bad. Errors even though you never like running into them, errors are there to inform you that there is a change in your program. In a perfect world, you would never have a scenario where you even need to use a begin and rescue block. The reason is that in a perfect world you'd have the perfect amount of data coming in and you wouldn't make syntax errors. That's not exactly reality though, that's the reason why this construct is here.

When is a good time? You've seen is that this is a very poor practice. Imagine that everything looks good and you have no issues. Then at some later date, you or some other developer comes in and adds a syntax error something that should throw an error or something you want to do because the error going to tell you what you need to do in order to fix it. You come back to the browser everything's still working and you think that your application is coded properly. In all reality, it's not.

That's the reason why this is so dangerous, I've had multiple times especially when I've taken on some older legacy rails applications where there were begin and rescue blocks all over the app and the developer simply implemented them in order to have errors not shown on the site. That definitely is a very big problem and it's a problem for exactly this reason where it hides the errors. That can lead to some very confusing behavior because you may have a process that you need to work and you expect it to work, then when the behavior isn't implemented and you also don't get an error, it's confusing. You expect to either get an error or you expect for the process to work. You don't expect for the process not to work and for no error to show up on the page.

That is exactly what can happen when you do something like this. What is a better way of doing it if this is considered a poor practice when would you do this?

I'm going to create a little bit of a convoluted example and one we can't even implement because honestly, our application is so basic right now that if we put error messages or we put a lot of these begin rescue blocks that would just be poor programming on our side because there is nothing in our application right now that should merit having this.

Let's pretend that we have a system that connects to an outside API. Let's say that we have something like get Facebook messages. This has a set of processes where it contacts Facebook and then it retrieves messages. Then it stores it inside of some Instance variable right here. Now, this is all well and good except whenever you're relying on a third-party service like Facebook as big as Facebook is and as great as their API is there are times when it goes down or when you have some delays. This is actually a much better time to implement these kinds of error handling system.

large

Imagine that you have this whole process and you don't have any error handling if this method right here throws an error such as the Facebook API being down for even a minute then it could error out your entire application even though you technically didn't have any code that was causing errors on your side.

Instead, you could say begin and then put all of this inside of a rescue block and here we could say rescue and put the exact message just like we did in our pry example. then you could just set a flash message and we'll get into it flash messages are in a while but this just could be something like this and say error occurred Contacting Facebook. And then we could also just print out the error. Just like this and then type end.

large

This is the type of error handling that the begin and rescue blocks were created for they weren't created to just help hide syntax errors or problems in your own code. If your if your code is completely self-contained it's not talking to any other API or anything like that. Then you see that you're starting to use these begin rescue blocks there's a chance that you're simply creating a poor implementation. That's something to keep in mind, Whenever I start to type one of these in, I always stop myself and I say does this really need to be handled in this way. Do I really need to protect the application from throwing an error or are there other things that I could put in the code that could help protect against this.

A great example would be that's a reason why we use validations. If we didn't use validations we'd have to have these begin rescue blocks all over the place that said hey an error occurred because you didn't give your first name. But we don't have to do that because we have a validates presence of name and we can be confident that we're always going to have a name.

Now let's go back to our API example because I want to show one other thing that is very important. Right now we're technically rescuing all of the errors that are out there in standard error. I'm going to pop open the standard error exception documentation right here. This is in Ruby and this shows, if you scroll down, all of the errors that you're going to see. This is the built-in subclasses of exception are you have no memory error. Keep on going all the way down. You remember when we had these zero division error that is the exact error. And this is the class that that is so you can see there are a number of errors. You have argument errors for when you put in too few or too many or the wrong type of arguments. You have IOError, you have some more advanced ones such as FiberError there are all kinds of things like that.

Now the very cool thing and this is definitely considered a best practice is even this implementation right here. This is still not a good way to manage errors. Yes we are capturing and we're protecting against Facebook going down. But one thing we're still not doing is we're still not protecting against a syntax error because what happens if we come here and we say oops then we have an error. And if we see this our error message thinks OK well it's not a problem on our side, we just can't contact Facebook. Then a few days go by and then all of a sudden you realize Facebook has not been down for a few days and the problem the entire time was that you were capturing all of the errors. This is definitely still not a great practice. What you'll usually see in a real-life application is for you to find the specific set of errors that you're going to run into and then you can list them off right here.

Opening up the documentation you can see that say that we've narrowed the Facebook connection error down to something such as an IOError, we can just copy this error and place an IOError right here. Then it's only rescuing IOErrors, it's not going to rescue some type of syntax error.

I'm going to open up Google Chrome and I'm going to go to repl.it the reason is because I want you to be able to see this code side by side. I'm going to say

def divide num1, num2
  begin
    num1 / num2
  rescue => e
   puts "Error: #{e}"
  end
end

divide 5, 0

Now if we run this you can see it simply prints out our nice little divided by zero error. But the problem with this is watch what happens if we come here and we just type in some jibberish. this is mimicking having some type of syntax there.

def divide num1, num2
  begin
    num1 / num2
    asdfasdfs
  rescue => e
   puts "Error: #{e}"
  end
end

divide 5, 0

If I run it's going to say error divided by zero but that's a little bit misleading. Yes, there is this error but it really is more than that. And you now watch what happens if I say five by five hit run. Now it says error undefined local variable or method for the main object. Now, this seems like it's throwing an error but if you actually look at what's returned it's still processing the whole method. this is not a good thing because yes it's printing out an error message but we're for some reason rescuing bad code which you don't want to rescue bad code you only want to be very targeted and very specific with what you're going to rescue.

if we come to our list right here and we look at all of our examples I'm going to pull out this zero division error. And now I can say rescue 0 division error.

def divide num1, num2
  begin
    num1 / num2
  rescue => ZeroDivisionError => e
   puts "Error: #{e}"
  end
end

divide 5, 0

And if I run now you can see this gives us an error message and this is actually good because now if we have a syntax error the process is going to halt right here. Before it printed out a nice error which may look better but that's actually a bad thing. Imagine this was a production application and you had this process and everything still ran even with syntax errors. It would be really bad. Here, you want to be very targeted.

And this goes into Ruby and into Rails best practices is to be very targeted when it comes to using begin and rescue blocks. You want to know the errors that are going to occur. And if you don't know the errors are going to occur. Or if you don't have some idea on what the errors are then you may need to rethink your code because maybe you don't understand what your method does.

If you're preparing to be a professional rails developer this is a very important thing that's going to come up consistently. And if you're writing code and handing it off to a senior developer who's going to review it if you have your code just filtered with begin rescue blocks they are not going to like that because if you're going to have to refactor it go back and redo it all because this in a production app would throw lot of errors.

We're saying we understand in having a divide method that the one error that we may come across is a zero division error. This right here makes perfect sense. Or going back to our Facebook connection API method. In that case we would find the method that occurs when Facebook is having issues and then we would just say I only want you to rescue that specific error and all the other ones such as syntax errors or argument errors or anything like that. We want you to let us know so that we can go fix it.

That is how you can implement error management and implement it properly inside of a Rails application.

Great job, I know that this section of the course is a little bit different because we didn't really build a ton of functionality but I wanted this to be something that we covered because I would be doing you a disservice if I said I was giving you a comprehensive rails course and then didn't walk through the principles that you're going to need to use when building out professional rails applications. Being able to use debugging byebug tools like pry and then implementing an error management is going to be a big part of that.

Great job if you went through that. We're going to finish all this off we're going to deliver it and everything is good in the next section. We're going to go through a really fun one and we're going to go into ruby gems including building your very first ever Ruby gem.

Resources