Admit it: all your projects have this one slightly odd part. It has many different names: “helper” classes, “common” functions, “auxiliary” code (if you’re that erudite), “shared” subroutines… Or simply a utility module, which is how I will call it from here.
This is the logic outside of application’s core. The non-essential building blocks which are nevertheless used throughout the whole project. Heap of scraps, squirreled to plug in the holes of your language, framework or libraries.
Those flea markets are notoriously difficult to keep in check. If left to their own devices, they gradually expand, swallowing more and more of incoming logic – code that would otherwise go into more specific, adequate places. This is where you can carefully observe the second law of programming dynamics: without directed influence, entropy can never decrease.
You cannot do away with utility modules, though. There will always be code that exhibits their two characteristic properties:
- frequent usage throughout most of the project’s codebase
- loose coupling with the rest of the project
But neither is binary: they are more like a continuous spectrum. And to make it even more difficult, they are also constantly in flux, effected by any change that happens in the code. To remain minimal (and thus manageable), utility modules require probably the most frequent, extensive and aggressive refactoring.
So how exactly do you deal with this necessary menace? I think it’s a matter of reacting quickly and decisively when one of these properties change. For the most part, the usage and (inter)dependencies of your utility package will dictate which one of these four steps you should take:
- When certain entity (function, class) grows universal, up to a point when it’s used by more than one subpackage inside your app, it is usually time to put it into the utility module.
Although somewhat obvious, this point is really important for one particular reason: preventing code duplication. Without a designated place for helper/common/etc. code, you will have different implementations of the very same things spread like a vermin across the whole codebase.
And you really don’t want to have four distinct functions for turning a sequence
[X, Y, Z] into a string
"X, Y and Z", or something similar to that. This is way worse than even the biggest utility module you might end up dealing with.
- Symmetrically, whenever an entity stops being used by more than one part of the program, you may roll it back into that sole part which still requires it.
For most, this will be the trickiest part. I can tell you upfront that you shouldn’t actually do it every time. There are things basic enough that you don’t need at all to track usage of: the “join with ‘and'” example might be one such instance.
But there are times when it’s more reasonable to put some utility stuff back into more dedicated package, because it simply belongs there. If you need to have that “and-join” localized, for example, you will likely find it more sensible to place its modified version into internationalization package. On the other hand, if you learn that it’s used only to format log messages which are never seen by the user, you can put it directly into logging module or just scrap altogether. (After all, programmers are not easily offended by poorly punctuated sentences).
- When utility code evolves into subpackage on its own, extract it as such.
When your internationalization needs require not only a localized “and-join” but also a way to match numeric values with noun plurals (
"1 apple" vs
"3 apples"; my native language is especially complex here), this can be the reason for creating a full-blown i18n package.
It is quite natural process, for simple but useful snippets – ones that typically land in shared module – to grow more robust, functional and thus complex. At certain size threshold, it’s reasonable to promote them into next structural level: module or package.
- Loosening up already loose ties may call for creating an external library.
You probably don’t write code with the express intent of reusing it across different projects, regardless of whether you were convinced by Mythical Man Month that it costs three times as much to do so. But “accidents” happen, and you can sometimes find a non-trivial piece of utility code that appears to be floating, that doesn’t depend on anything else inside the parent project.
Should you make such a discovery, by all means make it into a library. It doesn’t have to be real, external library – much less an open source one. Releasing a project into the wild is serious and time-consuming task, so I won’t ask you to do it every time (even though the world would benefit greatly if you did).
Treat it as third-party code, though. Separate it into different root package, add a new build target for it, include it as a .jar or .egg rather than .java or .py files – and so on. This is a small investment that makes it much more likely for the code to increase its value threefold.
Reducing the size of that pesky utility module will be just an added bonus.
So help you helper classes and utilize your utility modules for shared, common good :)