#begin

Today we will be checking out chapter 10 of the book A Philosophy of Software Design by John R. Ousterhout. Todays chapter is one of the chapters where this book gets its popularity from I think. When people talk about this book they will often mention the practices from this chapter or they will mention Deep and shallow modules.

But let’s check out chapter 10, Define errors out of existence.

 

Define Errors Out Of Existence

I always thought this was a really interesting practice you don’t hear other people speaking about that much. Or well maybe they do, yet they do not articulate it as well as professor Ousterhout.

He says that one of the worst sources of complexity in software systems is exception handling. That’s a nice statement right? Aren’t exceptions supposed to be good? Well, we discussed this in-depth in one of the previous blogs about clean code. Uncle Bob has specific practices for error handling which we digested in a Unity3D context. We talked about how you should try to work around exceptions, since throwing them will always slow your game down. Remember that you should never throw exceptions in mission critical code!

And I think John agrees since he says that, and I quote: “Code that deals with special conditions is inherently harder to write than code that deals with normal cases, and developers often define exceptions without considering how they are handled”. Uncle bob has a similar idea remember? He says you should always define exceptions from the view of the caller of your code, so they make sense, and don’t forget to add constructive and informative error messages in exceptions.

In this chapter John is going to explain how we can change the semantics of operations so that exceptions have no place anymore. You simply don’t need them anymore. So the overall lesson of this chapter is to reduce the places where exceptions must be handed. Let’s have a look.

So first of all, what is an exception exactly? Well John defines an exception as “any uncommon condition that alters the normal flow of control in a program”. And many programming languages include a formal exception mechanism that allows exceptions to be thrown by lower-level code and caught by enclosing code. However, exceptions can occur in the normal flow of your code as well, for example the dreaded NullReferenceExeption or MissingReferenceException in Unity3D specifically.

John also gives us a number of reasons why exceptions might occur: 1) A caller may provide bad arguments or configuration information. 2)An invoked method may not be able to complete a requested operation. For example, an I/O operation may fail, or a required resource may not be
available. 3) In a distributed system, network packets may be lost or delayed, servers may
not respond in a timely fashion, or peers may communicate in unexpected ways. And 4) The code may detect bugs, internal inconsistencies, or situations it is not prepared to handle.

All systems we produce need to be fault tolerant and thus error handling can really be a significant fraction of all the code in a system. John says that code that deals with exceptions is inherently more difficult to write since you need to take care of all the known and unknown exceptional cases. A programmer can choose to do two things when errors happen; he can either choose to continue execution as if the exception has not occurred, or abort the execution and report the error. Although the latter case can result is some really awkward and complex situations. Because if you throw an exception you might need to restore the state of the system back to it’s state somewhere before the exception was thrown to keep everything consistent.

And even better, when you implement specific code that deals with exception handling, you introduce the potential for even more opportunities where exceptions can be thrown. Haha, this is pretty funny. How many of you have ran into the issue where your game does not work just because you Debug.Log something in the catch statement of a try-catch because it throws nullreference exceptions. I certainly have in the past. So, you really need to make sure to prevent a cascade of exceptions and find a way to deal with them without creating more.

John however also wants you to really consider that trying to handle each exception that can possibly be thrown is bad practice as well. This will lead into a over-defensive style where anything that looks even a bit suspicious is rejected with an exception, which will result in more and more complexity. He then says something really interesting which is: “It’s tempting to use exceptions to avoid dealing with difficult situations: rather than figuring out a clean way to handle it, just throw an exception and point the problem to the caller”. This way you simply pass the problem to someone else and it adds to the system complexity.

He also relates the concept of shallow and deep modules into this discussion about exceptions and he says: “classes with lots of exceptions have complex interfaces, and they are shallower than classes with fewer exceptions”. The complexity lies with handling exceptions, not throwing them. Throwing them is easy, but actually taking care of them is where things can become difficult. And thus, we want to reduce the number of places where exceptions have to be handled.

John will explain about four ways to do this in the remain part of this chapter. We will look at practice he calls “defining errors out of existence”, masking exceptions, exception aggragation, and last but not least, why not “just crash”. Sometimes crashing might be the most viable option. But let’s first dive into the first practice; defining error out of existence.

He says that the best way to eliminate exceptions from your code is to define your API’s so there are no exceptions that need to be handled. A simple example he gives is when you try to delete a file from the file-system, yet it is not there, you should not throw an exception because of it already being deleted. Or another example that’s in the book; the substring method in Java. If you pass out of range arguments to that function is Java, and I think in C# too, an exception will be thrown. There are other languages, like Python he says, where the values are simply clamped to the min and max of the string length. This is a safer way to do substring implementation. Plus, you just defined errors out of existence.

And to me this sounds perfectly reasonable but you have to be honest that not all functionality can be adapted this way to not throw exceptions. I mean, there are cases where you simply need data to conform to some per-conditions you set for some particular functionality. Because, without this data conformity you simply cannot continue. But, John has three practices left so let’s see if he explains how he would solve this 🙂

So next up is the concept of exception masking. And this is a technique where you detect and handle an exception in a low level system, so that higher level systems of the software do not need to be aware of the condition. This seems so, straight forward or common sense to me… I mean, isn’t this how you would treat most exceptions anyway? I think you must always do your best effort not to let exceptions bubble up to the surface. But, yeah in some cases you won’t be able to do that.
John gives a simple example where, for example on some webapplication an exception occurs, sometimes it will be better to just do nothing at all, end let things timeout. This will result in timed-out requests but you do not have to handle all kinds of exceptions.

To me this sounds like a weird solution for a problem, because you will probably not be able to communicate the fault situation to your users or consumers. But he’s right that if you handle all these situations your code will most likely become a bit more complex and difficult to reason with. But he says that you should try and mask these kinds of situations inside the module they occur, which is an example of pulling complexity downward.

The third technique for reducing complexity related to exceptions is exception aggregation. The idea here is that you handle many exceptions within a single piece of code rather than having distinct handlers for many individual exceptions. I think, if you have worked with C# async tasks you have encountered this pattern of aggregate exceptions. So, in C# you could reuse the aggregate exception type to model this behaviour. So exception aggregation replaces several special purpose mechanisms, each tailored for a particular situation, with a single general-purpose mechanism that can handle multiple situations. This provides another illustration of the benefits of general-purpose mechanisms. I think this is a great idea, although have not used this pattern outside async tasks. But I might give it a try in the future. What do you think about this? Would you throw aggregate exceptions in non async code and handle multiple exceptions that way? Let me know in the comments if you like.

The fourth technique John wants to discuss is called; Just crash? He says that you might want to crash the application when errors occur that you just cannot handle. All applications have errors like these but they are often very very rare. And when you have them, you crash and print out some diagnostics information. You will most definitely have seen such crashes and logs if you have worked with Unity3D before. Not to say Unity is bad but these things happen and all programs suffer from these kinds of problems from time to time. A simple example is an Out Of Memory exception. If you have done any apps for iOS, I bet you have suffered from this, haha.

So if you load to many assets for your game on iOS it will most definitely crash at some point. You really have to unload them and make sure you free up some memory. Also make sure you destroy any unused, AudioClips, AnimationClips, Spites and textures. These will clog up your memory big time.

But in cases like, out of memory, you simply crash the application and make the user restart it because it is very difficult, if not impossible to recover from such errors. But on the other hand, if you are able to recover gracefully, you might as well just to it, right?

But to wrap this chapter up John says that special cases in any form make code harder to understand increase the likelihood of bugs. Exceptions exceptionally introduce special cases into your code, that’s exactly what they were designed for in the first place. The best way to reduce these kinds of special cases is to define them out of existence. For exceptions that cannot be defined away make sure you either mask them, and handle them at low levels of your system, or aggregate them to handle all of them in a since place. And if that does not work, just crash if that makes sense.

Thanks for reading!

#end

01010010 01110101 01100010 01100101 01101110.

Hey, sorry to bother you but you can subscribe to my blog here.

Never miss a blog post!

You have Successfully Subscribed!