Raising Exceptions Is Bad Practice

Pbvillaflores
6 min readOct 2, 2021

--

No, it certainly isn’t, particularly when it is for an exceptional situation. Properly raised exceptions actually make the life of developers much easier for exceptional situations, like when 1 is divided by 0 you get an exception. It gives immediate notice to an issue that can often be easily remedied. Exceptions should be just treated as normal part of good programming practice. But there are some developers that treat them like they were signs of poor programming practice. Nothing can be further from the truth than this claim.

I should mention my favored programming language at this point is python. In python, raising exceptions is very simple:

raise Exception(“the service is not available”)

But if you can use an existing exception type, better to use that instead like:

raise AttributeError(“The requested service is not available”)

Or making your own is simple:

class MyException(Exception):
"""Raise for my specific kind of exception"""

Exceptions make errors easier to find and handle

Let’s just imagine 1 divide by 0 didn’t raise an exception and instead some random number or 0 as a result. In a medium or large code project that can easily lead to some misleading results. And anyone looking for the problem can spend a long time looking for it. Some developers have to gone to great lengths because their programming tool doesn’t raise exceptions.

I am just as lazy as any programmer, and I don’t want to be troubled more than necessary. Not raising proper exceptions creates more work in debugging and understanding problems. Poorly written functions that don’t raise exceptions can create more work for people that use them, as they result in cryptic errors in logs and mask the real underlying problem. They can create an illusion of constant problem free performance, because failures aren’t raised.

Exceptions can enable lazy programming.

We hear of lazy evaluations in the context of various languages like python, haskell, javascript, R, and even in ML libraries like pytorch and tinygrad, we hear of the advantages of lazy evaluations. A similar thing is enabled in programming tools that have exceptions, simply because a programmer does not have to worry about exception handling, until they find that they have to. Essentially, a programmer does not have to write code to handle an out of range exception, or some other specific exception until that exception actually occurs, especially when the probability of that exception is rare or perhaps unexpected.

If the program does not fail or crash when subjected to robust testing, then essentially it could be said that there is no further work to be done in exception handling. Its certainly not true that one day, you cannot have a network failure (or some other exception situation) and the program might fail then and only then, under a condition not encountered during previous testing, but unless and until that failure occurs, the lazy programmer does not have to write any exception handling for the specific scenario.

There is definitely a balance to be struck here, but my view is that you write handling only for the exceptions that can be reasonably expected to occur, and you don’t when they are not.

And besides, if the program does fail, the exception traceback provides all the detail required to write the appropriate handling directed at specific root cause, should you wish to remediate. Hence, lazily write it out.

You can have a program with absolutely no exception handling within it, and that is perfectly fine. You don’t have to write any until practical circumstances or necessity calls for it.

Properly raised exceptions make for more meaningful errors

Yes, a raised/thrown exception can make a program fail, but in this case you would want it to because normally the program would likely fail already if the exception arose and the exception wasn’t thrown. In this case, it failed and gives a helpful error, with information as to what exactly went wrong, and then that means that can be actioned. Let’s say you have a useful function:

def getData(name):

and you normally use it like:

data = getData('John')

Let’s say the function returns some data list relevant to the given argument. If there wasn’t a match the function returns None. Now what happens if the service that getData relies on is unavailable? This warrants raising an appropriate exception.

Why not just print or log the error and return None?

Well, in that case the naive program that does not handle exceptions could receive None in the event that the data service is unavailable. It would behave no different to how it would behave had there been no matching result.

If your main objective was to simply keep programs running with a normal completion result, and not have them falling over, then that certainly meets that objective.

However, the result can be quite misleading. In this case, you could have gotten a None result, assumed that “John” had no data when in fact John did have data.

The consequences of that wrong result can be quite serious, and just depends on the business context. It can also mean hours of debugging an issue that gives rise to wrong results. In a large program, this “no exception” behavior can lead to much time spent reviewing and combing logs, and not knowing what to look for, it will be like finding an unknown needle in a haystack.

Can you overdo using exceptions?

Yes, it is possible. The general guidelines suggested by developers on StackOverflow that has received a large number of ticks of approval indicate that exceptions should be used when:

  • a fundamental assumption of the code is not met
  • exceptions for rare exceptional issues and events
  • for issues or events that should not be ignored

In a function, any thrown exceptions should be declared, so that the caller can anticipate them.

Raising/throwing exceptions makes for robust code with least effort

That’s because it makes error handling possible in the caller’s code. In the example getData function above, if it raises a ServiceUnavailable exception, a caller can catch that specific exception, and either exit with an appropriate result message or design some form of retry mechanism so that the caller’s process can re-attempt the same call at a later time.

Could you just write code ignoring the exception? Yes, absolutely.

These exception events will rarely happen, and in a program that is intended to be tactical or a quick fix solution, or narrow in its scope, then these programs likely won’t see them happen, and the developer often does not need to think of them. But if they do happen then the developer is better positioned with the exception raised rather than not, even if the program or problem is meant to be a short term tactical solution. That’s because there is immediate feedback as to the problem rather than creating a cryptic problem absent the exception.

An unchecked or unhandled exception, if it happens, simply causes a run time error, and it will display a reason identifying the cause. That reason or exception type will aid in resolution or root cause analysis, and ultimately can lead to additional fixes to code that can make it more robust.

When an exception occurs the program gives a traceback providing line number and code file where the exception occurred. That means you have exact problem root cause, and a direct appropriate remediation of the problem can be applied. Life could not be more simple, in terms of solutioning.

If you iterate the process of run, then remediate exception, re-run, then remediate again, you end up with code that is robust. Unbreakable under all practical circumstances it is subjected to.

Robust code can mean different things to different people, but here’s one practical definition: it is code that handles exceptions in the proper way, e.g., passes over the ones that can be ignored or retries the ones that should be. And this is precisely what exceptions enables.

Doesn’t all this additional error handling make for code that is harder to read?

Yes, it can and depending on the programming language the required syntax can be simple or can be quite verbose. In python, the required handling can often be written very simply.

try:
result = getData('Jonh')
except ServiceUnavailable:
print('getData returned service unavailable')
exit(1)

So, hopefully I have explained sufficiently that exceptions are part of good programming practice, and they’re easy to write and handle in python code. If you found this article because you thought it wasn’t, or wondered if it was, then this hopefully helps set things straight. If you are a junior or aspiring programmer or if you are an experienced developer but just hadn’t spent the time to learn more about exceptions, now is the right time for you to do so, for your favorite programming tool. For more about about the advantages of exceptions, there is more at this official document for the benefits of java’s exceptions. The discussion there is Java specific but the principles remain true of any other programming language. A great primer for python exceptions is here. And a good article exploring various approaches is here.

--

--