One of the hardest things in software development is naming. Naming of products, of paradigms and of parts of your code. The reason naming is both hard and important is because it is an act of communication; without good names your code might as well be written in, well, code. A name is not simply a label: it informs and guides the reader’s mental model. Names can change the way the reader thinks. A good name is a sharing of minds; a poor name is a missed opportunity to learn and say what we mean.
We often adopt conventions to make some aspects of naming easier. Such consistency can be a good thing, but not all practices that are consistent are necessarily good. Having a common vocabulary of concepts is useful across a codebase, but the concepts named should qualify as useful information — consistent noise is just noise.
Homeopathic naming is one of the commonest habits programmers can fall into without realising: making names longer by adding more words with the goal of adding more meaning to code, but in practice diluting the meaning with every Factory/Manager/Object/Controller/Value/Service/… added. Diluting things dilutes them; it does not make them more potent. Affixing Lego-brick parts to an identifier does not amplify or enhance its meaning, and often serves to highlight there may have been little meaning there in the first place. A naming convention should not be used to prop up weak names. If a name doesn’t communicate well, we need to see that clearly so we can address it rather than smothering it, hiding it behind a wall of Lego.
One homeopathic naming ritual common to both the .NET and the Java worlds is adding the suffix Exception to a class to denote that its instances are exceptions. This affectation is imprinted on developers unaware that they have acquired it as a habit — an unexamined and unchanged factory setting absorbed from their environment.
Exceptions in these languages are privileged and prominent, meaning that, based on their context, classes and objects that are exceptions are visibly and syntactically distinct from classes and objects that are not. They have exclusive rights to appear in specific and compiler-enforced places in your code: in a throw, in a catch and, in the case of the Java, in a throws list (but see if you can avoid this). By definition, things that appear in these places can only be exceptions — the compiler knows they must be exceptions and so does the reader, so there’s nothing more to add.
One oft-quoted but oft-misunderstood guideline is the DRY principle from The Pragmatic Programmer:
In the first edition of this book we did a poor job of explaining what we meant by Don’t Repeat Yourself. Many people took it to refer to code only: they thought that DRY means “don’t copy-and-paste lines of source.”
That is part of DRY, but it’s a tiny fraction and a fairly trivial part.
DRY is about duplication of knowledge, of intent.
DRY applies to names, language constructs and metadata just as much as it applies to code duplication, documentation and manual repetition of automatable processes.
If your programming language denotes abstract classes with a keyword such as abstract, don’t repeat yourself by putting Abstract in the name or by telling the reader that its necessary use is as base class by naming it Base. If a class is a concrete class then, by definition, it is an implementation class, so don’t repeat yourself by putting Impl in the name. If your compiler and IDE can tell an integer from a string, don’t repeat yourself by encoding that detail in a variable name, as popularised by the once-popular Hungarian encryption scheme. If your testing framework has you mark your tests with a Test annotation, attribute or macro — and your test appears inside a class, file or folder named Test — don’t repeat yourself by including Test in the name of your test. Use the bandwidth of a name to tell the reader things they need to know rather than repeating what is already known. Use your bandwidth for signal rather than noise — and use less bandwidth.
We understand that a class and its instances represent exceptions by their syntactic position, but this is not the only clue. That a class represents an exception should also be obvious either by its parentage or by its name. The name should represent whatever the problem is, and should do so directly and specifically. To add Exception to the end is either redundant — so remove it — or an indication of a poor name — so rename it.
Consider the following Java exception classes:
Dropping the Exception suffix gives the following names:
These names are concise and descriptive. Even without seeing their context of use, there is little question that these are bearers of bad news. .NET exceptions use similarly negative phrasing, and other ominous words like Invalid and Bad. There is little room for misunderstanding.
OK, but what about the following, also from Java?
Dropping the Exception suffix results in the following:
Hmm, not so clear. But is that a problem with dropping the Exception suffix? Or is it a problem revealed by dropping it? Let’s try renaming these classes to the problems they actually signal:
These revised names are both accurate and precise, indicating the actual condition of the exception without being vague or resorting to noise words. Is security grounds for an exception? No, but a security violation is. The exception relates to security, but the name misses the opportunity to be specific. Similarly, NumberFormat is not the problem, but attempting to use a string with an InvalidNumberFormat is.
Is it exception-worthy that a pointer (umm, reference) is null? No, in a language with references that can be set to null, null is an acceptable value. The problem being signalled is either that a null has been dereferenced in the JVM, such as calling a method via a null reference, or that a null has been passed to code that considers null to be invalid as an argument. Either way, the null is unexpected.
Being more specific about names also helps highlights ambiguities and vagueness, as in this case: Java practice fails to make a clear distinction between language-signalled errors, such as dereferencing nulls, and code-signalled errors, such as rejecting a null argument as illegal. In .NET, the former case is expressed using NullReferenceException (i.e., NullDereferenced) and the latter using ArgumentNullException (i.e., InvalidNullArgument), which is an ArgumentException (i.e., InvalidArgument). As to whether Java code is written to throw NullPointerException or IllegalArgumentException is largely dependent on the codebase, the team, the individual, the phase of the moon, etc.
Just as you don’t tag your verbs with Verb or your nouns with Noun when you write or speak, there is little reason — and many reasons not to — tack Exception onto the end of an exception class’s name. It can be considered a code smell rather than a practice to follow, often depriving programmers of the opportunity to find a better name.
A possible exception to this guideline? The general class that indicates that its descendants are exceptions: Exception. But then again, that’s not a suffix: that’s the whole of its name and the concept that it represents, so perhaps there are few exceptions on Exception naming.