The helpless helper

From time to time, we all find ourselves with a small, essential, but unexciting piece of code in our hands. It’s necessary for our program to run, yet, for some reason, it feels like it doesn’t quite fit. Call it what you will: helper, utility, library, common, or something else, they all share a common fate in your project—a cluttered folder filled with ‘recurring’ functions that can be used throughout the codebase. And they all share the same unfortunate destiny: they are seldom, if ever, reused.

The forgotten ones

If you want something to be reusable, you need to make it either plainly visible or make it known. For example, when you have guests at your house, you probably expect them to behave in a certain manner. There are common rules in society, like using the restrooms instead of the plant pots, that are acquired by custom, and there’s no need to provide any special reminder for them (oh! although there are always exceptions). If you have specific habits that you want to enforce with your guests, you’re going to have to explicitly inform them: ‘Please use a coaster to place your glass on’.

Image: "Use a coaster or I'll punch you in the throat".
A little too much? [Image from Etsy]

Following the example, if you have civilized people in your house, they probably will try to avoid damaging your expensive furniture with their glasses by using any piece of cloth or paper available at the moment. We can say that they’re inclined to create their own helper function for this purpose. If you have some elegant or amusing coasters, you would likely prefer them to be used instead of dealing with a bunch of wet napkins scattered all over the place.

This is especially true when working with dates. As conscientious programmers, we all have a tendency to search for a function that returns dates in the expected formatted string. We typically search for no more than 15 seconds, after which we create our own helper function with the name and format we expect, thinking out loud: ‘I can’t believe these guys don’t have a helper for this!’ (spoiler: they almost always do).

function formatDate($date, $format = 'd-m-Y') {
    if ($date instanceof DateTime) {
        return $date->format($format);
    } else {
        return date($format, strtotime($date));
    }
}

Even when you’re working on a project alone, you can end up forgetting that you’ve already written a function for a particular task. It has happened to me all too often, especially when returning to a project after several months of working on something different and becoming accustomed to other coding conventions. There are a couple of tips we can follow to avoid ending up with three separate folders: ‘utils,’ ‘library,’ and ‘helpers’, avoiding using three different approaches to implement them: helper classes, facades, and global functions, and avoiding having at least three different versions of the same functionality: ‘formatDate,’ ‘dateFormat,’ ‘formatted_date,’ and so on.

  • Be consistent. If your code is predictable, people will naturally go where you want them to.
  • Follow conventions. If things are where and as people expect them to be, they will naturally adhere to the convention.
  • Make it visible. Make your code self-descriptive, and document all the nuances.

It’s just a helper

Time and again, we find ourselves working on an important piece of code—something magnificent, perfectly structured, and finely-grained, written with attention to the smallest detail. We’re mindful of choosing good, self-describing names for methods and properties. We use abstraction, encapsulation, inheritance, and composition, even employing dependency injection and all the other things with fancy names that we proudly claim to master in a job interview. We give our best, remaining focused, like a black belt martial artist executing punches and kicks with grace. We’re eager to witness this beauty in action.

Eventually, the time for peer review arrives, which is, of course, a mere formality. Our code is perfect; no human could find a single flaw in it. Well, no human except for Bob. What’s taking him so long to hit the approve button? We all know Bob; he’s not the quickest, and he usually struggles with simple tasks. But this one is as clear as a finely tuned instrument. Come on, Bob… come on!

Bob highlights the obvious: “This block could be reused elsewhere”.
Of course, Bob, practically anything could potentially be used in various contexts, but not this. Why do you do this to me? This feels like a waste of time, we think. But attempting to convince Bob that, in this particular case, reusability is clearly unattainable seems too challenging. It’s easier to kick out the code and create a small, reusable helper function. Where should I place this? How should I access the object properties… refactoring! We can’t think straight at this point; we’re probably far too exhausted, hungry, and frustrated with Bob (he’s a handful). We end up incorporating too many parameters, some nested conditionals, and a couple of returns

function calculateTax(
    $incomes, 
    $isMarried, 
    $hasChildren, 
    $isSelfEmployed, 
    $isSenior,
) {
    $totalTax = 0;

    foreach ($incomes as $income) {
        $tax = 0;

        if ($income > 50000) {
            $tax += $income * 0.2;
        } else {
            $tax += $income * 0.1;
        }

        if ($isMarried) {
            $tax -= 1000;
        }

        if ($hasChildren) {
            $tax -= 500;
        }

        if ($isSelfEmployed) {
            $tax += $income * 0.05;
        }

        if ($isSenior) {
            $tax -= 200;
        }

        if ($isMarried && $hasChildren) {
            $tax += $income * 0.05;
        }

        if ($tax <= 0) {
            continue; // Skip this block
        }

        if ($tax >= 1000) {
            return "High Tax";
        }

        if ($tax >= 500) {
            return "Moderate Tax";
        }

        $totalTax += $tax;
    }

    return $totalTax;
}

It’s ugly, but anyway it’s just a helper.

Poor judgment can lead us to write this kind of lousy code, even if we’re experienced, professional, and utterly committed to quality standards. Anyone can and eventually will have a moment of weakness. Whether tired, in a hurry, or moody, you name it, humans can falter. There’s no reason to be ashamed; we just need to be prepared to mitigate the damage. First, understand that we need to take even the simplest piece of code seriously, and then be willing to fix even the most trivial piece of code.

The cost of it

It may appear to be harmless, at least on a small scale. However, neglecting these types of issues is what leads projects to derail. Complexity increases, as does unmanaged technical debt (what we like to call poor-quality code), making the project harder to comprehend, more challenging to maintain, and nearly impossible to extend with new features.

Maybe I’m overreacting. After all, it’s just a helper! Or is it?

In my own experience the best approach is to think twice whenever we’re tempted to write a helper function:

  • Reinventing the wheel? If it’s something so general and reusable, it’s more than probable that the language, the framework, or some curated package has already covered it. Two minutes of investigation can save us a lot of headaches.
  • Deserves its own role? If it’s not already, maybe the functionality is too specific and does not fit a broader context.
  • Is it simple enough? If a ‘helper’ needs its own operation handbook or may require changes to adapt to different ‘users,’ it’s likely that it doesn’t qualify for the part.

In summary, helpers are the slackers choice. By taking the time to think, we’ll likely always find a better solution.


Posted

in

by

Tags:

Comments

Leave a Reply