Exceptions: Friend or Foe

Nikasakana
6 min readApr 18, 2022

--

There are hundreds, if not thousands, of challenging and interesting topics in computer science and software engineering. Regular software engineer spends so much time learning new frameworks, languages and shiny libraries that the most of them forget about so called “boring” but at the same time critically important topics. There are so many many of these topics, that I have even invented the most creative title on earth:
The most ignored topics in software engineering”.

To become more aware about them, i have started series of articles with in depth explanations. This is the first article of the series, cheers :).
Here we’ll be explaining all the nitty-gritty details about “Exceptions” and it’s related topics like: “Exception handling”.

Before we start diving deep, i’ll mention that code samples are in Java, but the ideas discussed are language agnostic and might be helpful to any engineer using modern languages.

Theory and rationale behind “Exceptions”

First of all let’s revise what is the official definition of an “Exception”.

An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program’s instructions.

What are some concrete examples of these abnormal cases or events, that might cause these situations, you might ask.
The answer to this question varies a lot, but personally for me the easiest to grasp example is one with server side application communicating with a database ( See image below for the reference).

If your program communicates with a storage system ( like a database ) over a network, sudden outage of storage server, or network congestion will cause future application queries to fail right?
This is one very intuitive and simple example of the exact case of an Exceptional situations, that needs a special handling/treatment on an application side.
This way an application in a global context stays functional and responsive instead of stopping and logging an error about database problem.

To further understand the topic in depth let’s review more general example of an exceptional situation in modern day applications.

Let’s say you have several methods/functions that are invoked one after another, sequentially ( see the screenshot below ).
Each method’s invocation frames are at first pushed onto stack and later popped one by one if the program flow returns in a normal way.

Now let’s say that one of the methods throws an exception. Program will start in a normal flow. Upon reaching the point of raising exception, several things happen:
- normal code execution flow terminates
- all the details about the exception is wrapped in an Exception object and passed to the runtime system
- After a method throws an exception, the runtime system attempts to find something to handle it. The set of possible “somethings” to handle the exception is the ordered list of methods that had been called to get to the method where the error occurred.

Practical examples of exception handling

Now that we have general, theoretical picture of what Exceptions are all about let’s dive a bit deeper and look at some code samples and understand some of the real life use cases.
Below are listed examples with incremental levels of difficulty: 0–3

All the code snippets can be seen on Github at, you are more then recommended to clone repository and run examples locally ( maybe even contribute to it ;) ): https://github.com/nikasakandelidze/excpetion-handling-workshop

Level 0( Warm up / Observing normal flow)

For level 0 examples, we see a concrete code implementation of the normal ( exception-less ) flow discussed above.
No raised exceptions, no handlers, no edge cases, just a simple, sequential code.

Here, we see a normal flow, where no exceptional events happen.
Program completed gracefully.

Level 1 ( observing exceptions )

Here, we see code snippet with an exceptional event present, where the normal execution is suspended and an exception object propagates to the root of the program stack without handling it.
Program terminates ungracefully.

Level 2 ( Catching exceptions )

Here, we introduce Java implementation of exception handling mechanisms, aka try/catch blocks ( Still nothing too fancy ). Thrown exception in method 3 was propagated by system up to the call stack and catch block in the main method handled it gracefully.
Program completed gracefully.

Level 3 ( Catching and handling an exception in a correct manner )

Case studies above are more like theoretical, perfect, and not relevant to the real life use cases.
In production grade systems most of the time you have API-s that returns some useful data to the clients but at the same time they are full of potential exceptional situations, since they never work in isolation. They always communicate via network to other sub systems/components like: storage, message queue, etc.

While implementing general API, 2 general rules emerge for most of the methods:
- return some value following pre-defined API/Signature
- handle all the exceptional cases inside the implementation without introducing leaky abstraction layer and propagate message about exceptional event to the client in a correct way.

An interesting, more like a philosophical type of, question that arises in this case is:
What should the return value of our function be in an exceptional event?
or more correctly
How should we notify a user of our API that return value is not a result of a regular execution flow, but a some kind of exceptional situation, like database connectivity issue?

Let’s discuss this, more like a real world, scenario in an example shown below.

Client communicates with a service, that on it’s behalf communicates with a storage server via a potentially faulty network.
Whenever storage API throws a connection exception it’s a job of programmer to handle it gracefully on the service level and notify user of the service API in an abstract but informative manner about the faulty transaction.

Code for the Storage component:
Is a dummy implementation/imitation of faulty network call to database which results in connection exception.

Code for the Service component:
Main component of our system. Several important details to be noted:
- Introduces new “OurService” exception type, which will be public, known to the clients, and this exact mechanism will allow clients of our API to handle exceptional events gracefully too, using additional communication channel.

Code for the Client component:
Nothing too fancy here. Just a client code that uses a service API and handles exceptional cases gracefully using the same exact API published by service component.

Finalize

To sum everything up, In this article we tried to cover some of the biggest pillars on the way to understanding “Exception handling” and it’s related mechanisms.
I encourage you to look more resources up about this topic and by no means consider this article enough.
You and myself, we both, are more then recommended to dive deeper and to get even a better sense of this interesting but, at the same time, quite complex topic.

Thanks for reading ;)

--

--