- Published on
Exceptions; as I understand them
- Pramod Kumar
Exceptions; the fundamental feature of most of the programming languages available today. It looks like a very simple concept. When something goes bad, throw the exception, and catch to handle it. Pretty straightforward. But believe me, this is one of the concepts I request changes while reviewing peer code. I thought to share what I understood about exceptions and how I use them. So, let's dive in!
As programmers, while programming, we trust multiple underneath layers. For example, you assume that the required memory is available on the primary storage. You generally never check if memory is available before putting something in memory. But there are good chances that there will be no enough available memory. So, this is an exceptional case in this scenario.
Any failure that programmer thinks should not happen is an exception
I assume everyone who is reading this article knows how to use exceptions. You just wrap a function invocation inside a try block and put a catch statement to catch the exceptions. The implementation may change from language to language but this is the fundamental concept.
Let’s look at few scenarios where most of the programmers go wrong
You decided to throw an exception. But why do you throw a generic Exception type? I have seen it a lot (actually a lot!) of times. When you throw a generic exception you are not specifying what type of exception it is. For people who want to catch it, the only way to handle just this exception is to put a string matching logic which is utter bad. Every exception you raise should be as specific to the problem as possible. Use the available exception types already available. For example, python has exception types like ValueError, NotImplementedError, etc. So next time don’t be lazy and dare to throw “Exception”
Use custom Exceptions
We have seen why it is bad to throw bare Exceptions above. The problem does not stop there. It is a good idea to use built-in available exception types, but in some cases, you might need a new Exception type altogether. Let us say you are building a system that deals with face recognition. When someone passes some random image that does not contain a human face, instead of raising some in-built exception, create an exception type NotHumanPhoto or InvalidSelfie (if it has to parse only selfies), etc. Whether to throw an exception or not is a different story, but if you have to throw, you might as well create a type for it. Again, that does not mean you should bloat up your system with a lot of custom exception types, it should be balanced and reasoned.
It’s an exception, not null
I have seen a lot of people getting confused about returning null instead of raising an exception. It is important to understand that, exceptions are a powerful tool to tell that something has gone wrong beyond my assumptions.
def get_order_size(order_id): order_products = get_order_products(order_id) if len(order_products) > 10: return 'MASSIVE' if len(order_products) > 5: return 'MEDIUM' if len(order_products) > 0: return 'SMALL' return ValueError('Order with no products!')
The above function determines the size of the order. The last line of the function says it is an invalid order. The assumption is that every order contains at least one product. So, instead of returning null or None, it is better to throw an exception in this case.
Don’t catch exceptions if you can’t handle
Programmers don’t like their program to break. Which leads them to catch the exception and do something with it but not let the system break. If you are wrapping some code in a try block and putting something in a catch statement make sure you know what to do when it fails. Just logging the exception is actually not handling the exception.
def get_user_name(token): try: user = get_user(token) return user.name except InvalidUser as e: log.info('Invalid user') # better way of doing it def get_user_name(token): return get_user(token).name
The above function ideally should not have handled the exception. Whatever token has been passed should be valid, if it is not valid, let it just break, just pass the exception back.
Don’t catch the exceptions if you don’t know how to handle it
Don’t catch “Exception”
This is actually a continuation of the above point. Exceptions are implemented in a chain manner, one exception inherits another exception, “Exception” being the root. When you are catching an exception, you catch that particular type and its children.
Programmers as said above, don’t like their code to break. I have seen a lot of people wrapping the entire code to try and catching the root “Exception”. This is like promising someone, “No matter what, I will solve your problems”. This is as true as you saving a ship from sinking which is in the middle of the Pacific Ocean with a huge hole to its base. Never ever do such promises and so catching the “Exception”
Let the code break!
Not expecting your application breaks is just unreal. Your application should be ready to log everything about the unhandled exception, do the postmortem, and keep the application alive for the future when the unhandled exception hits the top layer. In case if it is a server, the controller layer would be the top layer. This might lead you to catch “Exception” but this is not handling the exception, you are just logging it, raising an alarm, or something.
So, believe the code you write breaks and build your system in such a way that it raises alarms when some unhandled exception occurs and keeps the system alive.
Exceptions are an amazing way of expressing that something has gone wrong beyond the function's control. Don’t be scared to raise exceptions, not to catch an exception if you don’t know how to handle it, have custom type exceptions, and finally to break the code you have written.
Happy programming. Cheers!