I know I said I wouldn’t put up a post until I got the videos done, but this has been nagging at me. As for an update on the videos, I’ve been partially lazy, partially busy, but I’m ready to record the first episode the first chance I get, and I don’t expect to need a lot of editing. Anyway, on with the topic of the day.
The Takipi Blog has recently posted two articles about the top 10 most thrown exceptions (that are logged). These 10 exceptions account for 97% of the exceptions in those logs. I’d like to list off these exceptions in order from most common to least and give a short commentary about them being in the list.
It’s likely not a huge surprise to a lot of you that NPEs are on this list, or even so high on the list, but this really shouldn’t be the case! These exceptions are highly avoidable; there are two relatively well known ways to avoid already available in Java – Null Object pattern and the Optional type – and if you switch to a language with null safety (such as Kotlin, which is perfectly compatible with Java), you barely even have to think about avoiding NPEs.
We should all be working hard to prevent these exceptions, either by putting in good preventative practices (and I don’t consider normal null checks to be the best practice unless that potential null is coming to you from a 3rd party) or switching to a language that helps prevent NPEs.
NumberFormatException and ParseException
ParseException is number 9 in the list, but it’s closely related to NumberFormatException, and I find it truly disturbing that both of these are in the top 10, and one of them is #2. Both of these are related to parsing String data into something else, and I can’t help but think that, with such a high prevalence of these exceptions, developers aren’t fully using the type system to their advantage, that they’re doing “stringly-typed” programming.
Now, a good chunk of these exceptions are likely due to bad user input or some sort of serialization issue, but there are probably also far too many “stringly-typed” objects out there. The ones caused by bad user input should probably be checked before getting run through the parser, right as the very beginning as part of the initial input validation.
What I’m saying is probably a little harsher that I really mean. These can be difficult exceptions to avoid in many circumstances, and it’s not especially surprising to seem them in the top 10.
NoSuchMethodException and InvocationTargetException
Both of these only happen in reflection, and I may not say it much, but I have a strong dislike for reflection. There’s almost always another way. That way may not be as easy, but I believe it is almost always better nonetheless, since things become more explicit and understandable, and you gain more compiler help with mistakes.
There are definitely some exceptions to avoiding reflection (such as JUnit using reflection to run all the tests), but I’m certain that a majority of these exceptions have been thrown for no good reason, that using normal programming techniques could have avoided issues.
To see a really good talk about avoiding “magic” like reflection check out “8 Lines of Code” (following along is difficult because the video only looks at the speaker, and he jumps back in forth in the slides a fair bit, so it can even be difficult to follow along with the slides yourself). This is also one of the reasons I prefer Java Spark over Spring.
I actually don’t mind this one showing up; it means that a good chunk of people are checking their inputs and making sure they don’t screw everything up. There are some ways of reducing the count a little, such as using really strong typing, such as what Object Calisthenics suggests (rules 3 and 4), but that’s a purposely overzealous set of rules to help you learn, and following it will mostly just push the IllegalArgumentException further up and into one place.
RuntimeException and Exception
Really? REALLY?! That’s just not helpful, especially Exception. I can understand making an exception into a runtime exception, but just using RuntimeException isn’t the best way. Make a custom exception for that, even if it’s as generic as WrappedException.
Again, good OO design can work around this, but coming up with those designs is really tough, sometimes. I’ll go easy on this one.
I don’t know how to feel about this one. I’m afraid of how much of it might be caused by reflection or by using raw types when generics were available. In both of those cases, it needs to stop. But I also know that there are plenty of places where it’s really hard to avoid, so I won’t harp on it.
Most of what worries me in that list of exceptions is the amount of NPEs and reflection problems that are apparently prevalent in Java code. The many of the others have to do with weak OO design, but I know there are plenty of developers out there that are new or inexperienced or that their problems make it difficult and/or not worthwhile to develop more deeply. I have to let those cases go.
Please, readers, if you learn anything from this, learn that you could probably save yourself tons of headaches in the future by learning how to work around null (either by a null-safe language or by using Optional or the Null Object pattern) and try and see how much reflection and annotations you can take out of your code.