I hate the word ‘utility’. It says absolutely nothing. And yet, I come across it in so often when programming. It can take different guises too: ‘common’, ‘shared’, ‘lib’, ‘pkg’, ‘tools’, etc. These names can appear both as filenames or as directory names and drive me insane when they do.
Why?
They can contain literally anything.
And yet any code that ends up in these places has a clear purpose. It must be useful, otherwise, why would we write this code? I mean, some people do enjoy writing useless code... but most of the time this isn’t the case. So why do utils crop up everywhere? How do we avoid this problem in our own code? Why does the name utils trigger me? Let me tell you my story.
The intent behind a bad name #
When I first started my career in software engineering, I worked on a large project for NHS Digital. The project involved a variety of products that all shared libraries and packages. The source for these products and packages was a single monorepo, supported by around 30 developers. One ticket on the backlog that I picked up was to refactor a database client shared across these products.
I began by researching the database clients the products currently used. I painstakingly mapped out an upgrade path for each product. I worked through all common scenarios for the database client. I deliberately designed the minimal interface this database client would need. In my eyes, it was going to be a work of art. Perfection.
I set about building the database client and after endless weeks of meticulous upgrades, I finally had the finished product. My masterpiece. The code ran like clockwork.
The downfall #
A year later I returned to work on the same codebase to find everything had become a mess. It felt like a rats nest to work in. There was no separation of concerns and every pull-request felt like you were wading through mud. I tried to figure out what the problem was. What had changed? Why had it all gone wrong? After a while, I realised it was entirely my fault.
When I introduced the database client to the codebase, I’d put it into a /shared/database/
directory. Sounds sensible, right? It was going to be shared across all the products, so that would be the logical place to put it. My flaw though was in creating that /shared/
directory.
This shared directory triggered a rapid descent into chaos. Any code that could be conceived as potentially reusable was immediately placed into the shared directory without any consideration. More and more of the logic of each product ended up here, and less and less in the product’s directory. Changes risked breaking everything. It wasn’t long before some of the shared code became product-specific, or became a spiral of self-consuming code with no clear interface.
Making a change to the shared code either meant changing every caller, overloading the behaviour, or making slightly different variations of the same code. Searching the codebase became near-impossible, duplication began to run rife and separation of concerns was non-existent. It was a nightmare.
So what did I learn from this? #
In creating a shared directory, I’d opened the floodgates. The problem with the name ‘shared’ is that it does not describe the code I had written. It only describes my intent for that code. Code being reused, or shared, is incredibly common. Utility code, shared code, library code, package code. These names are all to do with intent, rather than describing what the code is. I should have stuck to putting my code in a database/
directory. Forget the shared part.
So, what have we learned? We shouldn't call shared code shared, or utils, or common. It should just be code: named for what it is.
Looking for other ways to keep your code organised? Check out my article on writing IMMUTABLE code.
Enjoyed this post? Want to share your thoughts on the matter? Found this article helpful? Disagree with me? Let me know by messaging me on Twitter.
Top comments (13)
I have read multiple articles of this kind, where colleagues are blaming specific practices. I believe that the root problem here is that engineers do not work as owners of their projects. Every time a programmer touch a code base is an opportunity to improve code quality which is a hint for spotting junior developers.
A real senior developer should detect when the garbage is piling up and fix it. Even if management does not want to allocate time doing refactor. I would prefer to get fire for doing the correct things than blindly accepting all requests from management pretending that our codebase is healthy.
The rule of the boy scouts is: “Always leave the campground cleaner than you found it”.
Good luck with that if you have a family.
Be real, sometimes you will have time and sometimes no.
Part of the seniority is based on social skills that will convince management.
That is our duty as professionals. The best programmer I've ever worked with once told me every time product/management/client is taking the wrong decision and you let that happen, you are failing as a professional programmer.
Many colleagues do not think in this way but we should start to see this like a patient saying to the doctor they do not want this or that treatment because they do not like it. It is our responsibility to inform the people that pay our checks that they are going against their self-interests.
I think it's a nice ideal.
In practice, though, sometimes it takes too much time to achieve this.
Sometimes it's a battle that can't be won.
Sometimes you need the job more than you need to fight this fight.
And also, some people don't like having these kinds of conversations too, and that's fine.
Be a boy scout like Bob Martin
It is our code base, even tough we inherieted it
This is absolutely true. I just feel utils or shared code is a particular flavour of garbage I feel people are often blind to, or unaware of.
My question would be: say you hadn't added that single directory. Would the code have been in perfect shape a year later? From what you describe, I doubt it.
It absolutely wouldn't be. You're right. There's far more factors to it than I've put into this article - it's more than this one thing was an accelerant of sorts. A nasty trap I opened that all the engineers fell into.
Was it the only trap? No.
Will I be more aware of it in the future? Absolutely.
ahhh...yes... the util package. It's like the 'everything' drawer.
It really is. You look away for two minutes and it's full of flat batteries, pens that don't work and random scraps of paper.
Lovit !
I can almost see the shared floodgates
Here is my own opinion on "utils"
Code Smell 22 - Helpers
Maxi Contieri ・ Nov 12 ・ 1 min read
This is a great post. Thanks for sharing. Absolutely hits the same notes.
I've used the comparison to a standard library before when arguing against helpers or utils.
If you look at a programming languages standard library (e.g. golang.org/pkg/), everything has a clear point and purpose, there's no helpers or utils, and yet the code there is for the most reuse possible.
I don't see a problem with having a
utils
auxiliary package, as long as it's contents are properly organized and documented.