Programming Pro Tip 1 – Function Purity

Function Purity Pro Tip

This is the first tip in my Programming Pro Tips series, which I hope will help you improve the overall quality and maintainability of your code. This first tip is particularly pertinent to developers creating web applications in PHP but could easily apply to other sectors. This tip focuses on the subject of control flow and function tasks in applications. In order to explain, two types of functions need to be defined: Utility Function This is A function which handles a single task such as uploading an image, sending an email, or creating a file. This type of function should NOT control logic decisions it’s self. It simply performs a single task and does it well. Control Function Although care should be taken to keep functions as simple as possible, ideally focusing on a single purpose, sometimes we need to break this rule to create functions which deal with control flow. Think MVC controller. Control functions bring together control statements, utility functions and other linear code to create a logical execution flow and they are often hard to break up into smaller chunks. Control functions are often responsible for instantiating objects, creating variables which will be passed down, and are often filled with control statements such as If, Thens. In my opinion control functions should also be responsible for error/user feedback creation, state manipulation and in most cases, throwing exceptions. So with those definitions in mind, here’s the programming tip: “Don’t mix control logic into utility functions and vice versa. Keep control separate from actual tasks as much as you can.”

Why?

  1. Your functions will be more focused and easier to mentally digest.
  2. Function naming will be easier and clearer.
  3. Unit testing will become much easier.
  4. Control flow will be easier to find and follow.
  5. Functions will be more portable and easier to use elsewhere.
  6. Refactoring, removing code, replacing code will become easier.
Example Please look at the simple example below written in PHP. This example explores saving an associated image and sending a notification when an entity is saved (entity such as a product, page or post).
class SomeEntityController extends AwesomeControllerClass
{
    /**
     * Setup stuff
     * @returns boolean
     **/
    public function __construct()
    {
        parent::__construct();

        $this->registerEventHandler('afterSave', $this, 'saveImages');
        $this->registerEventHandler('afterSave', $this, 'notifyPeopleWhoLikeMyStuff');
    }

    /**
     * Save the entity main image
     * @returns image string
     **/
    public function saveImage(ControllerContextObject $context)
    {
        if($context->status == ControllerContextObject::STATUS_SAVED && $context->data->image_source) {

            // Some mystical stuff happened here to format image data from the context into $image_data array
            $image_data = array();
            $path = '/some/path/that/probably/wouldnt/be/defined/here';

            $result = \Vendor\Image\Uploader::upload($image_data, $path);

            return $result;

        }

        return '';
    }

    /**
     * Save the entity main image
     * @returns boolean
     **/
    public function notifyPeopleWhoLikeMyStuff(ControllerContextObject $context)
    {
        $result = false;

        if($context->status == ControllerContextObject::STATUS_SAVED) {

            // Some mystical stuff happened here to data and get content for an email and put it in $mail_data
            $mail_data = array();

            try {

                $result = \Vendor\Email\Mailer::mailer($mail_data);

            }catch(\Vendor\Email\Mailer\Exception $exception) {

                $this->raiseError($exception->getMessage());

            }
        }

        return $result;

    }
}
In the example, I have ruined the purity of the saveImage and notify people functions by putting control logic in them. Let’s refactor this by creating a control function and remove the control logic from these function.
class SomeEntityController extends AwesomeControllerClass
{
    /**
     * Setup stuff
     * @returns boolean
     **/
    public function __construct()
    {
        $this->registerEventHandler('afterSave', $this, 'onAfterSave');

        return parent::__construct();
    }

    /**
     * Runs after every save
     * @return boolean
     **/
    public function onAfterSave(ControllerContextObject $result)
    {
        if($result->status == ControllerContextObject::STATUS_SAVED)
        {
            if($result->data->image_source) $this->saveImage($result);
        }
    
        return $this->notifyPeopleWhoLikeMyStuff($result);

    }

    /**
     * Save the entity main image
     * @returns image string
     **/
    private function saveImage(ControllerContextObject $result)
    {
        // Some mystical stuff happened here to format image data from the context into $image_data array
        $image_data = array();
        $path = '/some/path/that/probably/wouldnt/be/defined/here';

        $result = new \Vendor\Image\Uploader::upload($image_data, $path);

        return $result;
    }

    /**
     * Save the entity main image
     * @returns boolean
     **/
    private function notifyPeopleWhoLikeMyStuff(ControllerContextObject $result)
    {
        
        // Some mystical stuff happened here to data and get content for an email and put it in $mail_data
        $mail_data = array();       

        try {

            $result = \Vendor\Email\Mailer::mailer($mail_data);

        } catch(\Vendor\Email\Mailer\Exception $exception) {
        
            $this->raiseError($exception->getMessage());
        
            $result = false;
        
        }

        return $result;
    }
}
As you can see this simple change had made the imageUpload and notifyPeopleWhoLikeMyStuff functions way more focused, easy to maintain, easier to unit test and more portable. I strongly suggest thinking about adopting this technique in your programming if you haven’t already thought about it. Bonus Tip You may have also noticed that I have ensured each of my functions return the same data type regardless of what happens internally (no mixed function returns). This is an idea adopted from “Functional Programming” which I have found to be a great tool for making code easier to follow. Therefore, bonus tip: “Make sure your functions always return the same data type.” Thanks for reading, I hope this tip will inspire you to think about how you lay your code out in terms of control and tasks. Please accept my apologies for the poor ‘Pretty Print’ of the code examples, I’m working on a better solution for displaying code.