John Mikael Lindbakk

View Original

Utility and helper classes - a code smell?

This is a very spicy & hot take of mine: I consider most utility or helper classes I’ve seen throughout my professional career to be a code smell and, generally, they are detrimental to the overall solution. Allow me to elaborate. 

What is a utility class?

Before we can actually talk about this, we must first agree on what a utility class actually is. The most broad definition would probably be something like this:

A utility class is a class with only static methods which holds no state and cannot be instantiated.

Everything that falls within that definition can be considered a utility class. Therefore we also must include what is known as “helper classes” as well.

This definition does indeed explain the traits of a utility class, but it does little to explain how to appropriately use one. This has led to a lot of confusion and misuse of utility classes which we will explore throughout this post. 

Why utility classes are problematic

Utility classes breaks SOLID

For the uninitiated, SOLID is a set of principles which we apply to help our code so that it can be flexible, maintainable and understandable. We want our code to be SOLID, yet using utility classes prevents this in several ways.

Dependency inversion principle

In regular OOP we like to inject our dependencies. This is great as we couple code to the contract (interface) rather than the actual implementation. Since utility classes cannot be instantiated we cannot inject it into anything. Therefore the dependency inversion principle is utterly broken when using utility classes. 

Open-close principle

Again we return to the problem of static methods. The nature of static code is that we cannot extend the code in any way. Therefore utility classes break the open-close principle. 

Single-responsibility principle

If our utility class deals with any business logic whatsoever we are forcing our code to share the responsibility of that business logic. Our business code cannot be completely responsible for its functionality as some of it can be found in a utility class, while the stateless nature of utility classes makes it impossible to take full responsibility either. This means that utility classes for the most part breaks the single-responsibility principle as well.

Utility classes complicates testing

Writing tests for static methods is pretty easy. After all one doesn’t even need to create an object to access them. What does become tricky is when we’re writing tests for methods which use these static methods. We want to test our classes independently from the others. This is a very basic principle for unit testing. What makes it possible for us to test things independently is by mocking the surrounding classes, however static methods make this difficult, though not impossible. There are two main approaches to deal with static methods and testing, but either approach needs special considerations when writing tests.

Mock static calls

One approach is to mock these calls. This does, however, require special very intrusive mock libraries. The problem is that the nature of static methods often require us to alter the binary code. This obviously depends on the language, but at least this is what these kinds of mocking frameworks do in Java. It is a way more involved and intrusive way of testing - especially considering that it can be avoided by simply not using static methods.

Don’t mock static calls

The other approach is to assume that the static functionality is just a part of the service. This way we don’t need to mock anything, but we will still have to consider that the code we’re running must, in fact, run through the static code as well. This can quickly lead to a lot more code in our tests to make sure that objects are in a correct state which the static method can handle. 

Say we have an object with 10 fields, where 8 is required. We only need to write a test which does something which only requires 1 of these fields. Yet, due to this static method we must include all 8 or else the call will fail. In short we must write worse tests to facilitate the static functionality.

Utility classes makes proxying impossible

In the world of Java it is very common to use AOP or proxying, especially in frameworks like Spring. The issue lies within that one cannot create proxy objects for static classes as there are no instances to replace. This complicates the extendability of our code as. Logging, session management, error handling etc are all things which we often use AOP for which is massively complicated by static functionality. In utility classes we have to concede that surrounding code must be responsible for doing these things, or we will have to re-write how we do things to now work with static classes. Either approach is bad.

It is hard to stop a growing utility class

When we have first accepted utility classes into our code they are notoriously difficult to remove. Often these utility classes are ambiguous and therefore it is easy to add semi-related logic to them. This often leads to massive utility classes and the more logic added the harder it is to break up later. Bigger the class is, the bigger the rewrite will become.

Since most utility classes are so poorly defined it makes it really difficult to stop one that grows. It is easy to add functionality to an ill defined utility class while being very difficult to justify stopping someone from adding to it. This is why we often see very large utility classes which slowly consumes a domain one method at a time.

The problems a utility class brings are, however, not confined to the utility class. Due to the system being tightly coupled to the utility class the problems will spread out to the system as well. After a while we will see the system’s code bend to the utility class as that is easier than actually changing the utility class (I’ve seen it happen). 

The other problem which arises is that when a utility class reaches a critical mass it will become very difficult to avoid it. At some point it will grow so large, become so important to a domain and has so many roots within the code base that it will start rotting the overall code. All new functionality must then, at some point, touch the utility class.

Identify smelly utility classes

I’m not about to make the claim that utility classes are inherently wrong, but as with most things in programming we should always be able to identify when they make our code smell. 

Only one class depends on the utility class

A good utility class provides some generic functionality used multiple times throughout an application. We are simplifying these generic operations by making them static and putting them in a place where other pieces of code can easily reach them. In situations where only one class depends on the utility class it is a clear indication that the logic isn’t really generic enough to warrant a utility class. In this scenario we should really consider why the utility class exists in the first place.

The utility class emulates other patterns

A common fault is to use utility classes rather than actual  design patterns. I’ve seen, and sadly written, plenty of utility classes which deal with validation, object creation and to provide an object with default values. This is pointless as we already have well known design patterns that do these things. We already have factories, builders, adapters, facades and so forth. We know what these things mean, and by forcing such logic into a utility class makes the solution as a whole harder to grasp. Another issue is that this often introduces business logic into our utility class which is a big no-no.

The scope of the utility class is too large or undefined

The world of programming doesn’t like huge scripts. Massive chunks of code are prone to bugs and difficult to manage. Utility classes are not any different in this regard. The issue is that we often name our utility class based on our application’s models. That’s why we get classes like “PatientUtil” or “AppointmentHelper” and so forth. This means that the scope is limited to everything that has to do with that domain. Does new functionality belong in the “PatientService” or “PatientUtil”? We simply don’t know, as both of these classes are responsible for the overall functionality for the “Patient” domain. 

The utility class exists to hide code

This is what I would consider the number one reason why utility classes are created. As applications grow, both in terms of size and complexity, we realize that some of our classes are growing too large. To deal with it we simply make some methods static and put them in a utility class. In this situation we correctly identified the problem, but applied a terrible solution. On the surface the solution might seem to work. Both classes are now smaller after all, but we haven’t solved the underlying issue. The code is still highly coupled and could as well have just been in the same class, we simply tried to hide it in the most naive way possible. We still require the huge test suite for that class. We still require to follow each function when reading the code, but now we have two poorly defined classes which we must switch between to understand the solution.

An appropriate solution to such a problem would be to identify the various subdomains within class we want to split up. This does take time and can be very complicated, but the end result is far better. Moving code into utility classes just for the sake of it only causes more problems.

The utility class is mutable and stateful

A stateful utility class isn’t a utility class. It might pretend to be one, but that is a filthy lie. This is not an indication that something is wrong, rather it simply is wrong on so many levels. Same goes for utility classes which alters the variables passed to the methods. A utility method’s only effect should be the return value and nothing else.

Rules for working with utility classes

I feel that many utility classes exist because the developer didn’t see any better way of approaching a given problem. While the goal shouldn’t be to simply avoid utility classes for the sake of it, we should at least discuss how to approach issues when considering using a utility class.

Rule 1: Ban meaningless words

Let’s look at the definition of the word “Utility”:

“A program or routine designed to perform or facilitate especially routine operations (such as copying files or editing text) on a computer” [source]

Now let’s look at the definition for helper:

“A person or thing that helps or gives assistance, support, etc.” [source]

What does both of these definitions have in common? They are both meaningless in the context of naming a class. These terms do nothing to indicate what a class actually does. Nor does it help to limit the scope of a class. Yet most utility classes I’ve encountered has had such names:

  • PatientHelper

  • BonusUtil

  • GroupHelper

  • AppointmentUtil

Developers are always valuing proper naming, yet we constantly drop the ball when it comes to utility classes. Therefore we should drop words like “helper” and “utility” from our vocabulary all together. They do not make our code more readable, nor do they provide the reader with important information. In my experience the simple act of not using meaningless terms forces us to actually consider what something is, and often reveal the true nature of something.

Rule 2: Business logic isn’t allowed to be in a utility class

Business logic doesn't belong in utility classes, yet it often ends up in them. Utility classes are supposed to be these helper classes which do some generic operations for us. They should not know, nor care about any business logic of a given system. Having this rule forces us to consider other patterns and solutions before we resort to utility classes.

I know it sounds strange. Most of our code does deal with business logic after all. This is what I think many people misunderstand about the purpose of utility classes. Take a look at Java’s Math class. It is a utility class dealing with basic numeric operations. None of these operations has anything to do with any business requirement in the systems that use them. These operations are completely generic and whole on their own. This should also be the goal of any internal static methods - they should be generic and tied to no specific business logic or model.

Rule 3: Stop and think - consider other design patterns

Plenty of times I’ve seen utility classes substituting other patterns. I’ve seen utility classes creating objects rather than making a factory, builder or prototype patterns. There have been plenty of instances where an adapter, facade or some other structural pattern would be more suitable. Lastly and more egregious is when the utility class should simply be a service with its own interface.

Often we see that utility classes spring up around poorly defined domains as a way to break it up without actually breaking it up. It is quick to do and easy to manage, but as we have previously discussed it doesn’t solve the underlying issue. What one should do is to carefully consider how to meaningfully break up the domain into multiple subdomains which are all responsible for some functionality. 

It's quite rare that a utility class is actually warranted, so when we do write one that should be one of those moments where we take our hands off the keyboard and truly consider what we’re doing”. This is not the time to move forward, rather it is a time to reflect on the core issue that we are trying to solve. 

Rule 4: Never add to a smelly utility class

Most of us already have existing systems which we must maintain, and many of these systems will include smelly utility classes. Throughout this post we have discussed the problems of a growing utility class, therefore we should refrain from adding to one which smells. Adding code to such a class only adds to the problem. If one really feels that a utility class is warranted then a new one should be created. 

I would go so far as to say that such classes should be locked the smelly class from being edited entirely. Only unlocked in cases where we have to fix a critical bug, but never to simply add code to it. This might sound harsh, but stopping a spiraling utility class is extremely difficult, especially in a team with multiple developers.

The truly insidious part of utility classes is that the system is so tightly coupled with it. Therefore a smelly utility class which grows will set deep roots in the application. The problems are not confined to the utility class, rather it will start rotting the code surrounding it. This is why the team should get together and plan to remove smelly utility classes. While this is often a huge task it is also something which should happen sooner rather than later, as later will always make the task bigger and more complicated.

Are utility classes a code smell?

I don’t consider utility classes to automatically diminish the quality of a system. The main issue with utility classes is that they are very easy to introduce while the disadvantages are not being obvious. Utility classes are very difficult to get right, yet so easy to create. This is why we often see problematic utility classes in the first place. Done right utility classes can provide immense benefit, and many frameworks and languages use them appropriately. I am not at a place where I can comfortably say that all utility classes can be considered a code smell.

Conclusion

When utility classes are warranted they can be a great addition, but in most instances I’ve come across they are not warranted. Many instances are born out of laziness or lack of understanding the problems static functionality can bring - not always, but often. The goal of this post was not to ban all utility classes, rather to inform what problems such classes tend to bring, how to identify problematic classes and setting some ground rules which hopefully prevents us from writing utility classes that's detrimental to the health of our systems.