A Rant On Reusability
Oh sweet reusability, the once-prized tenet amongst developers everywhere, how you have fallen.
“Why recreate the wheel”, they say - a time-saving, highly-valued guideline that in my mind -just common facade over laziness.
Sure, reusing code certainly saves time in the present, and since it’s likely worn down and battle-tested, shouldn’t that mean it’s safer than building something custom?
I say fuck no, and I’m hear to wave the white flag of surrender for reusability from this point going forward.
A rant against my past transgressions as a growing developer.
Now, don’t get me wrong, there are really two kinds of reusability existing in the world of software development and one of them is totally A-O-K with me.
That’s the reusability that happens at the macro scale - the libraries, utilities, and dev frameworks that make our lives levels of magnitude easier.
The JSON parsers, magic currency formatters, e-signature APIs, financial calculators, and the like. The Reacts, Angulars, Blazors, and
[insert other adored libraries here].
Things like extension methods and local utility classes are okay too. These are the “no-duh” kinds of reusability where you actually encountered duplicate logic and would be needlessly recreating the wheel were you to write your own.
The other kind of reusability… not so much.
The dark, putrid, and festering side I’m referring to is kind that arises from fiendish attempts to make every last function and class as generic as possible to satisfy some mystical ideal of perfect reuse. Not coincedently - the beginnings of a God Object. Especially when the use case doesn’t exist yet, i.e. “We might need this to support feature X ~in the future~”.
Further, it’s finding existing code that kinda does what you’re trying to do, so you bastardize it so it now does a half-assed confusing version of each.
It’s the “Hey, that method is close enough to what I need, let’s reuse it. Just need to add a parameter or two to the signature and a couple
if statements and… GREAT! look at that, I’ve saved some time. Check out my sweet REUSABILITY!”
Picture those wretched all-in-one As-Seen-On-TV kitchen gadgets that have 30 different parts to clean and aren’t particularly good at any one task. That is the fate of these decisions.
It’s the selfish, lazy frankenstein-ing of existing code to suite your one use case.
Instead what you’ve done is coupled two (or god forbid more) discrete business processes together so that next time a developer has to change one of them, they have to sniff around for an extra couple hours making sure their change is compatible with the all the other.
These seemingly innocuous day-to-day decisions are the axioms of your classic ball-of-mud architecture.
One coupling becomes many until you have something that resembles a monkey knot rather than a clear set of distinct features.
There’s a long lost essay I wrote that talked about spaghettification of code - That all starts from the accumulation of many sneaky micro-couplings that drip into codebases lacking a defined architecture or code review processes.
Sure, you can go a looong time building apps this way. It’ll grow into a festering old rat’s nest that you can piss and moan about at the water cooler, and you’ll spend days upon days fixing introduced bugs that you can blame on something vague - your manager won’t know the difference.
The code will do it’s job, it’ll just take 2-3 times as long to make a change, and it’ll be like working on an old truck - fix one thing and break two in the process, without any knowledge of what’s going to break next.
This is where my disdain for the term reusability has grown.
It simply does not belong within the walls of your business domain or service implementations.
Stop trying to save time this way.
Reuse is not a prized tenet outside of frameworks, libraries, or utilities.
Let go of the ideal of all-in-one.
Fine Japanese Chef’s Knives
Instead, aim to build your features each like a unique, hand-crafted, gormet Japanese chef’s knife.
Carefully designed for a specific culinary task.
Specific over generic.
Master-of-one, over Jack-of-all-trades.
Yes, this means more code upfront to maintain, however - when something breaks, it’s only going to require repairing one knife.
Need to add something new? No old code is changed or broken in the process. Just a nice new blade, hand-made specifically for this new feature, not constrained by poorly-suited decisions from yesteryear.
Need a viewmodel and you’ve found an existing one that has everything you need minus 5-10 more properties and methods (and has a few extra you don’t need)?
Don’t reuse it.
Make a new one specific to your use case. Yes, it’s more code and more time (now), but you’ve kept your feature separate so that changes can happen in isolation. Devs making changes in the future don’t have to think about anything other than the feature they are working on.
Got a method that does what you need except for some side effects you’ll have to skirt around carefully in order to make use of it?
Either refactor out the part you need into an unlikely to change utility, or better yet, make a new method entirely with a name that suits the use case.
This is why vertical slice architecture is gaining popularity as an antidote to the tangled monoliths we’ve been maintaining for the last decade.
When you build apps this way, they become a set of vertically-coupled features (Japanese knifes in a stained oak knife block) that change in isolation. New features come in as mostly new code rather than by further coupling what already exists. This means spending little time on regression over existing features now broken, hoping your tests can catch anything you’ve introduced.
Your architecture remains clear and intact.
So PLEASE - take great care and reservation when deciding whether or not to reuse code in your codebase or pre-emptively create reusable code when there is no obvious use case.