Every RuntimeException Is a Bug

Properly written Java software uses checked exceptions to indicate environmental problems external to the program such as an I/O error and uses runtime exceptions to indicate problems inside the program itself, in other words bugs. Every time a run-time exception is thrown there’s a bug somewhere. Sometimes the bug is inside the method that throws the exception. Sometimes the bug is in the method that invokes the method that throws the exception. Sometimes the bug is in a third-party library. Sometimes the bug is that someone is trying to do something in a place where they’re not allowed to do it, for instance open files inside a compare method. But make no mistake: if a run-time exception is thrown, there’s a bug somewhere.

We have not yet learned how to write perfectly bug free software. I suspect we never will. Defense in depth suggests that it’s a good idea to have a trycatch block near the top of your application — for instance in the main method or in the run method — that will catch any runtime exceptions you weren’t expecting, log them, and perform any cleanup it reasonably can. However, this is not enough. It is also necessary that someone carefully read the logs and fix the bugs that caused the runtime exceptions in the first place. Otherwise, you might as well not be logging them in the first place.

A disturbing number of third-party libraries have started using runtime exceptions where they shouldn’t be. In particular, they’re using runtime exceptions to report errors communicating with external services such as databases and DNS servers. (Yes I’m talking to you Hibernate). If you’re using such a library — although I recommend you don’t — then you will find a lot of these exceptions in your logs. In this case, the logs are warning you that you’re missing a catch block deeper inside the code. It’s annoying that you find this out at run time rather than at compile time as you could have if the library had used the right kind of exception in the first place, but better late than never.

One thing you should not do is include checked exceptions in the same log as the runtime exceptions. They mean different things and they require different responses. Most checked exceptions indicate environmental problems that take care of themselves. Database connection errors are one frequent example. In fact, any network service is simply going to fail some of the time. That’s just how TCP/IP works. Unless it’s failing all the time or very frequently, it’s not worth worrying about. You don’t want to clutter up your logs with a lot of detail you’re not going to do anything about. Limit the error logs to the problems you actually need to resolve, and every runtime exception that is thrown is a real problem that you actually need to resolve.

Here’s another way of thinking about it: a problem you’re just going to catch and ignore should be a checked exception. No runtime exception should ever be ignored. Runtime exceptions should be subjected to postmortems and fixed in the code so they cannot reoccur. Architect your code so that runtime exceptions are logically impossible.

Of course, not all checked exceptions should be ignored. Many of them indicate serious problems that require handlers to deal with. But if an exception can sometimes be ignored, then that exception is a checked exception. Leaving a code path that is known to throw runtime exceptions in your program is leaving bugs in your program. Every uncaught runtime exception, every exception that bubbles up into your main loop unexpectedly is a bug, and it should be fixed.

Another Java question LLMs can’t answer

This time I asked Gemini, “How does Maven version comparison differ from semver version comparison?”, and while this time its answer didn’t say anything patently false, it did fail to find the most important distinction. It’s operating at the level of an undergraduate with better than average writing skills but who still doesn’t really understand the subject matter. It didn’t find anything new, although in this case there was something new to be found. (I found it myself a few weeks ago, but haven’t gotten around to publishing it on the Web yet.)

Read the rest of this entry »

Gemini Doesn’t Understand Namespaces Either

Last week Gemini gave me a very wrong answer about exception handling in Java. This week let’s see if it can handle an even less controversial but often misunderstood point about XML namespaces. (Spoiler: it can’t.)

The question is:

Should XML namespace URLs include a version number?

The correct answer is no.1, 2 Gemini’s answer is the opposite, and the reasons it gives show why it’s wrong.

Read the rest of this entry »

Gemini doesn’t understand exceptions. Then again neither do you.

Some days I feel like code review is an exercise in educating developers. Nowhere is that clearer than in Java exception handling. Almost nobody understands this. When I’m interviewing software engineers, I pretty much never ask about exceptions because it’s an almost 100% guaranteed fail. If I’m being interviewed and I’m asked about exceptions, I have to play the meta game where I assume that my interviewer believes things that aren’t true, and I tailor my response to fit their misconceptions. Mostly I do that by focusing on the syntax and behavior without covering the semantics and proper use of exceptions. Effective Java comes closer than most to correctly explaining which exceptions you should use when, but even Bloch doesn’t quite stick the landing.

Today after yet another round of back-and-forth code review comments where I once again had to explain to an otherwise experienced developer how exception handling is supposed to work, I got the bright idea to ask Gemini, Google’s machine learning chatbot, what it thought. It gave me a better answer to the question than most programmers do; but it still made two fundamental mistakes, one of commission (saying something that isn’t true) and one of omission (leaving out an absolutely critical point). See if you can spot the mistakes.

Read the rest of this entry »

Bleeding

For many centuries, bleeding patients was a standard treatment for many diseases. Cancer? Bleed the patient. Headache? Bleed the patient. Fever? Bleed the patient. Pneumonia? Bleed the patient. Bleeding was accepted medical wisdom.

Perhaps surprisingly to modern patients, bleeding worked, at least some of the time. The patient would get better. Of course, a lot of the time if the doctor did nothing, the patient still got better. No one bothered to ask whether it was the bleeding that caused the patient to get better or not. Few people even knew how to phrase the question.

Read the rest of this entry »