What’s the best way to handle errors? Things won’t always go the way you expect and you’re going to need to plan how to handle errors. Should you use return codes or exceptions? Plus, this episode will help you in other ways. You’ll be glad you listened.
This episode explores this question with five different ways to handle errors. The error handling strategies are first described with a real life example and then again as they relate to programming. You learn how to:
- Ignore the error. While it might seem like you’re writing resilient code because you can recover on your own, this is really not a good choice.
- Hide the error. This is even worse than ignoring an error. You’ll have enough bugs already and don’t need to purposefully add more.
- Return an error code. Sometimes this really is the right approach. It’s simple and direct. The only problem is that the caller can decide to ignore the return value.
- Throw an exception. This is my preferred approach and ensures that the error cannot be silently ignored. Sure, the caller can always catch an exception and then decide to do nothing about it. But there’s only so much a single method can do.
- Make an assertion. This allows your method to verify anything it wants and to stop the entire program if anything doesn’t look right.
You can listen to the full episode or read the full transcript below.
Let’s say you’re working in a car factory on the assembly line and your job is to install the seat belts. A new car rolls up to your station and you discover that only one of the four bolt holes have been prepared. You need four bolts to securely fasten the seat belt to the car and you only have one. How do you handle this? Here’s five different ways:
◦ 1. You could just skip this car and let somebody else deal with it. This approach just IGNORES the problem and hopes it goes away. You know the car won’t actually get sold like this but nobody will be able to figure out that it was you who ignored it. Your conscience is clear and you avoid some extra work.
◦ 2. You could just use one bolt and forget about it. Hey you tugged on it and it seemed just fine. Plus a few more cars like this and you might even save some money for the company by not using so many bolts. This approach HIDES the problem. It may not even be discovered unless the car is in an accident. You feel guilty but soon forget all about it as you struggle to keep up with the line of other cars.
◦ 3. You could use one bolt, make it look as good as possible, and then fill out some report that you RETURN each day. You know these reports sometimes get lost but is that your concern? You did the right thing, right? You followed the rules and reported the problem.
◦ 4. You could attach a bright orange sticker to the front of the car that will cause the car to be removed from the line and sent back for rework. The car will bypass several stations on its way to rework but you know that nobody will ignore the sticker. You feel great knowing that nothing gets past you and wish you had the strength to pick up the car with your own hands and THROW it back to the earlier station.
◦ 5. You could pull the cord that stops the entire assembly line. You get nervous flutters as you hope nobody gets upset with you for stopping production. You know it will take hours to get things going again. In the long run, this has to be the best because the delay will get the attention of the engineers who will have to make some changes around here. This is just unacceptable and you decide to ASSERT your authority.
Each time I think about programming, I’m amazed at how similar it is to real life. Each of the previous five choices is available to you as a programmer. It’s the same thing really. And some of the terminology that I used when describing the assembly line is the exact terminology used when programming.
Let’s say you’re writing a method that needs to calculate the health of your adventure hero character. You need to use a formula that adds and multiplies several factors to arrive at the health. The factors involved are the character’s age, race, recent battle damage, sleep, and hunger. The exact formula doesn’t matter for this discussion and you might need more or fewer factors in your method.
But here’s the problem. You need to calculate the health but find that for some unknown reason, the character’s age is zero. This is a bug because characters should never have zero for the age. So what do you do? Here’s the same 5 choices in programming terms:
◦ 1. You could ignore it and just assign full health. Maybe this is 100 in your game.
◦ 2. You could hide the problem and work around it by picking one of the other values to use for the age. You decide to use the hunger value for both hunger and age. It seems to work and you get a health value that’s not unreasonable. It’s completely nonsense but maybe nobody will notice.
◦ 3. You could always set the health to a full 100 and return false from the method to signal that something went wrong.
◦ 4. You could throw an exception. You get to choose the type of the exception and a message explaining the problem. Some other code will catch this exception because that’s how exceptions work. If your code doesn’t catch the type of exception thrown, then the program will terminate.
◦ 5. You could assert that all the values must be valid. Anytime one of your assertions fails, the program ends right then and there. The QA team will definitely notice this.
What’s the best strategy? How should you handle programming errors? It’s really going to depend on the situation and you might even use a combination of these approaches.
I’ll advise you to never ignore or hide a problem of any kind. This just doesn’t end well.
And returning an error code is also something that I tend to avoid whenever possible.
There are times when an error code is appropriate and that’s usually when an exception just can’t be thrown or when the error code is more of a return code for a normal and expected event. Maybe this is a network call to a different computer. You need return codes for that.
Exceptions are my normal recommendation. Just make sure the situation really is exceptional. Missing or invalid values definitely qualify. But other more common situations such as maybe failing to remove an object from a collection because it wasn’t in the collection in the first place are probably better handled with the simple return value.
And finally asserts are a great thing to add to your debug builds. I recommend removing them from your release builds and letting any errors be caught and logged so you can figure out what went wrong. If a customer hits an assert, that kills the whole application and you’re not going to get any logs of the error.