PHP
Article

Feature Toggling Explained with Qandidate’s Toggle

By Reza Lavaryan

A frequently used development workflow in version control systems is feature branching. The idea is that we develop new features in branches other than the master one. After a feature is tested and ready to be released, it is merged back into the master branch or a release branch for deployment. This approach helps us develop new features without disturbing the main code base.

However, developing a feature branch might take much longer than a normal release cycle. This will make merging the branch more difficult since we would have to deal with possible merge, logic, or dependency conflicts.

Feature toggling

Feature Toggling

One of the techniques widely used as an alternative to feature branching is feature toggling. Feature toggles (or feature flippers) act like on/off switches. They let us continue development on the master branch while not exposing the partially developed and risky features to the public. We can make sure our features remain fully compatible with the existing functionality in our application.

We can temporarily hide a partially built or risky feature (release toggles) or limit finished stable features to a certain group of users (business toggles). These two types of toggles are implemented in the same way but for different purposes. With release toggles, we hide our unfinished features from users except for the development and QA team. These toggles retire when the feature becomes stable. They are usually managed at the code level.

By using business toggles, we can make a feature available to a certain group of users, or we can completely disable it due to some conditions – think webshop sale, Xmas theme on your site, etc. They often require an interface like a dashboard to view the status of the existing toggles with the ability to switch them off and on.

Feature toggling is used by many large websites including Flickr, Facebook, Disqus, Etsy, Reddit, Gmail and Netflix.

Martin Fowler has a good write up on feature toggling, covering the pros and cons and how to use them the right way.

In this tutorial, we’re going to lean how to create feature toggles using Toggle, a PHP library developed by Quandidate labs.

Qandidate Toggle

The basic idea is to activate and deactivate a feature based on some conditions at runtime. As an example, we could activate a feature for the users who registered their accounts in our system a week ago.

Toggle is composed of several components.

Toggle Manager

Toggle manager, as the name implies, is responsible for managing feature toggles including storing toggles, retrieving toggles, checking toggles’ statuses, etc.

The toggle manager can store the feature toggles in two different ways. One method is an in-memory collection, which is a class wrapping an array of toggles. This class exposes some methods for storing and retrieving the toggles inside the collection. The second storage approach uses Redis to store and retrieve the toggles.

The storage class is injected as a dependency into the toggle manager, at the time it is instantiated. In this tutorial, we will use the in-memory collection to store the toggles.

<?php
// ...
$manager = new ToggleManager(new InMemoryCollection());

Toggles

In Toggle, every feature toggle is an object with a name and a group of conditions. We evaluate runtime values against these conditions, to decide whether the feature toggle should be enabled or disabled.

<?php

// ...

$conditions = [$condition1, $condition2];
$toggle = new Toggle('newFeature', $conditions);

Operators

Operators are the building blocks of conditions of a toggle. If we compare them with the operators in programming languages, they’re like the logical expressions in if statements. The following operators are supported by Toggle out of the box:

  • GreaterThan
  • GreaterThanEqual
  • LessThan
  • LessThanEqual
  • InSet
  • Percentage

To create an operator:

<?php
// ...
$operator = new LessThan(1000)

In the preceding code, the operator applies to values less than 1000. Operators don’t do anything on their own. They are meant to be used with condition objects.

The idea may have become a little confusing by now, but it will be all clear when we put it all together in the following sections.

Conditions

As noted earlier, each toggle must meet one or more conditions, to be activated. What is a condition in Toggle?

A condition is an object which takes an arbitrary key and an operator object:

<?php

// ...

$operator = new LessThan(100);
$conditions = [];

// This condition is met when user_id is less or equal than 100
$conditions[] = new OperatorCondition('user_id', $operator);

$toggle = new Toggle('newFeature', $conditions);

Context

Operators, conditions and toggle objects create instructions for activating a feature toggle at runtime. So basically nothing happens until we check some values against the conditions.

Context is an object that allows us to assign values to toggle conditions. These values can be anything from ID numbers to any value based on our use case. We add values to a Context object using the set() method:

<?php

// ...

$operator = new LessThan(100);
$conditions = [];
$conditions[] = new OperatorCondition('user_id', $operator);

$toggle = new Toggle('newFeature', $conditions);

$context = new Context();
$context->set('user_id', 67);

user_id refers to our condition’s key in the above example.

Now that we have the conditions and runtime values, we can check the toggle’s status using the manager’s active() method:

<?php
$manager = new ToggleManager(new InMemoryCollection());

$operator = new LessThan(100);

$conditions = [];
$conditions[] = new OperatorCondition('user_id', $operator);

$toggle = new Toggle('newFeature', $conditions);

$context = new Context();
$context->set('user_id', 100);

$manager->add($toggle);

if ($manager->active('newFeature', $context)) {
    echo 'The newFeature toggle is enabled';
}

active accepts two parameters. The first parameter is the name of the toggle for which we want to check the status. The second parameter is the context object. active() checks if such a toggle exists inside the collection. Consequently, it evaluates the context values against the respective conditions and returns a boolean value.

Please do notice that we need to add the toggle to the manager using the add() method before the evaluation.

Instead of using the manager’s active() method, we can directly use activeFor() of the toggle object itself:

<?php

// ...

// Conditions here

$toggle = new Toggle('newFeature', $conditions);
if ($toggle->activeFor($context)) {
    echo 'The toggle is active';
    // Do something special here.
}
// ...

activeFor() takes a Context object as the argument.

Toggle in Action

We’ll use composer to install Toggle and its dependencies. To do this, first we create a directory in our web root directory and run the following command inside it:

composer require qandidate/toggle

Now, let’s create a file and name it ToggleConfig.php. We’ll keep all the toggle definitions inside this file. Finally, we will evaluate the toggle’s status and return it as an array.

For this example, let’s create a feature toggle which is enabled before 8 PM:

<?php
//ToggleConfig.php

use Qandidate\Toggle\Context;
use Qandidate\Toggle\Operator\LessThan;
use Qandidate\Toggle\OperatorCondition;
use Qandidate\Toggle\Toggle;
use Qandidate\Toggle\ToggleCollection\InMemoryCollection;
use Qandidate\Toggle\ToggleManager;

$manager = new ToggleManager(new InMemoryCollection());

//-------------------------------------------------
//   Toggle for featureOne
//-------------------------------------------------

$operator  = new LessThan(20); // < 20

$conditions = [];
$conditions[] = new OperatorCondition('time', $operator); // time < 20

$toggle = new Toggle('featureOne', $conditions);

// Adding the toggle to the collection
$manager->add($toggle);

$context = new Context();
$context->set('time', (int) date('G'));

//----------------------------------------------------------

// Return the status array

return array(   
    'featureOne' => $manager->active('featureOne', $context),
);

In the preceding code, first we imported all the necessary classes into the script. We instantiated ToggleManager and injected InMemoryCollection as the storage media. Then we added a toggle with one condition. The condition is met when the current hour is less than 20 (8 PM).

Finally, we evaluated the toggle status and returned it as an array.

To use the feature toggle, let’s create another file named index.php:

<?php

require_once 'vendor/autoload.php';

$toggles = require 'ToggleConfig.php';

if ($toggles['featureOne']) {
    echo 'The toggle is active';
    // Do something special here.
}

We assigned the output of ToggleConfig.php (which is an array of toggle’ statuses) to the $toggles variable.

Using Toggle with a Framework

When we’re deploying a partially developed feature into the wild, we have to hide access to it in UI components along with all the entry points leading to the functionality of said feature.

As an example, we’re going to use the toggling feature in a Laravel project to protect some UI components and a group of URLs. However, the idea applies to other frameworks as well.

Start up a Laravel skeleton project, then proceed.

First, let’s install Toggle. In our Laravel project’s root directory, we run:

composer require qandidate/toggle

Creating the Feature Toggles

The feature toggles can be defined anywhere as long as they are loaded when our application is bootstrapped. A good approach is to define all these in a middleware class.

To create a middleware, while in the terminal, we change directory to our Laravel installation directory and run the following command:

php artisan make:middleware TogglesMiddleware

As a result, a middleware class named ToggleMiddleware.php will be created within our app/Http/Middleware directory. We need to define all the toggles inside the handle() method of this class.

For this example, let’s create a feature toggle to give access to a certain group of users. For example, users with the ID number under 100 – the “early adopters”.

To use the toggles’ statuses across our application, we will use Laravel’s Config service.

<?php

namespace App\Http\Middleware;

use Closure;
use Config;
use Qandidate\Toggle\Context;
use Qandidate\Toggle\Operator\LessThan;
use Qandidate\Toggle\OperatorCondition;
use Qandidate\Toggle\Toggle;
use Qandidate\Toggle\ToggleCollection\InMemoryCollection;
use Qandidate\ToggleManager;

class TogglesMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        
       /*
        * Toggle for NewFeature
        */
        $manager = new ToggleManager(new InMemoryCollection())
        
        $operator  = new LessThan(100); // < 100
        $condition = new OperatorCondition('uid', $operator);  // uid < 100
        
        $toggle = new Toggle('newFeature', [$condition]);

        $context = new Context();
        
        // Here we use the User model to get user information
        $context->set('uid', User::get()->getId());   
        
        // Storing the statuses in the Config service
        Config::set('toggles.newFeature', $manager->active($toggle, $context));
        
        return $next($request);
    }
}

In the next step, we need to register the middleware. Since we want to run the middleware during each HTTP request to our application, we register it as a global middleware. To do this, we add our middleware’s class name to the $middleware property of our app/Http/Kernel.php class:

<?php
/**
     * The application's global HTTP middleware stack.
     *
     * @var array
     */
    protected $middleware = [
        
        //...
        'App\Http\Middleware\TogglesMiddleware',
    ];

Hiding UI Components

To hide UI components related to our feature, we have two options. We can either access the Config service and check the toggle’s status right from the views, or we can do all the logic in our controller and then pass a flag to the view. The latter approach seems to be more appropriate according to best MVC practices.

So inside our controller:

<?php

// ...

// Our business logic here.

$showFeature = \Config::get('toggles.newFeature');
View::render('users.dashboard', array(
    'showNewFeature' => $showFeature,
));

// ...

And inside the view:

<div class="row">
    <div class="col-md-3">Feature one</div>
    <div class="col-md-3">Feature one</div>
    @if ($showFeature)
        <div class="col-md-3">Feature one</div>
    @endif

</div>

In the preceding code, we used blade’s @if statement to check if the flag is true or false. As a result, the last <div> element will be rendered only if showFeature is set to true.

Protecting the URLs

If our new feature contains an API or any controller that we should hide from the users, we need to protect them as well. To do this, we create a before middleware, but this time instead of registering it as a global middleware, we assign it to a certain group of routes. Inside the middleware’s handle method, we check the toggle’s status, and if it is disabled, we throw a 404 error.

Another approach is that we put a logic barrier at the beginning of the controller classes and throw a 404 error if the toggle is off. However, using this approach, the request is dispatched to our controller even though the toggle is off.

For this example, we go with the first approach. Let’s start off with creating the middleware:

php artisan make:middleware APIToggleMiddleware

As a result, an APIToggleMiddleware.php file is created within the app/Http/Middleware directory. Since global middlewares are run before the route-specific ones, we’re going to use the toggle that we created in our global middleware. The reason why we didn’t define the toggle inside a route-specific middleware in the first place is that we might need those toggles in all controllers.

<?php
namespace App\Http\Middleware;

use Config;
use Closure;

class APIToggleMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if (Config::get('toggles.newFeature')) {
            abort(404, 'Page Not Found!');
        }
        return $next($request);
    }
}

In the preceding code, we used the Config service to retrieve the status for the newFeature toggle. If it is false, we simply throw a 404 error.

To register this middleware, we add the middleware’s class name to the $routeMiddleware property inside app/Http/Kernel.php. $routeMiddleware is an associative array storing all the route specific middlewares, so, we’ll need to specify a key for our middleware as well. In our case: api-toggle:

<?php
/**
 * The application's route middleware.
 *
 * @var array
 */
 protected $routeMiddleware = [
     'auth' => \App\Http\Middleware\Authenticate::class,
     'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
     'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
     'api-toggle' => '\App\Http\MiddleWare\APIToggleMiddleware',
 ];

Now, we can use the middleware inside our app/http/routes.php file as a before middleware:

<?php

Route::group('new-feature/api', ['middleware' => 'api-toggle'], function(){
     Route::get('new-feature/', function(){...});
     Route::get('new-feature/create', function(){...});
     Route::put('new-feature/{id}', function(){...});
});

As a result, all the routes under new-feature/api will be only available to users with ID numbers below 100.

Toggling Strategies

By default, a feature toggle is active when at least one of the conditions are met, but this can be adjusted using the toggling strategies that Toggle provides out of the box.

To do this, we need to specify the desired strategy when creating a toggle object:

<?php

// ...
$toggle = new Toggle('newFeature', $conditions, Toggle::STRATEGY_AFFIRMATIVE);
// ...

Affirmative

This is the default strategy that Toggle uses for checking toggle conditions. In this case, at least one condition must be met for a toggle to be active.

<?php

// ...

$manager = new ToggleManager(new InMemoryCollection());

$conditions   = [];
$conditions[] = new OperatorCondition('user_id', new LessThan(100));
$conditions[] = new OperatorCondition('age',     new GreaterThan(21));

$toggle = new Toggle('newFeature', $conditions, Toggle::STRATEGY_AFFIRMATIVE);

$manager->add($toggle);

$context = new Context();
$context->set('user_id', 100)
        ->set('age', 20);

if ($toggle->activeFor($context)) {
    echo 'The toggle is active';
    // Do something here
}

The above toggle will be enabled because one of the conditions (user_id) is met.

Majority

In the Majority strategy, the majority of conditions must be met. For example, if we have three conditions in a toggle, at least two of them must be met.

<?php

// ...

$manager = new ToggleManager(new InMemoryCollection());

$conditions   = [];
$conditions[] = new OperatorCondition('user_id', new LessThan(42));
$conditions[] = new OperatorCondition('age',     new GreaterThan(24));
$conditions[] = new OperatorCondition('height',  new GreaterThan(5.7));

$toggle = new Toggle('newFeature', $conditions, Toggle::STRATEGY_MAJORITY);

$manager->add($toggle);

$context = new Context();
$context->set('user_id', 41);
        ->set('age', 25);
        ->set('height', 5.6);

if ($toggle->activeFor($context)) {
    echo 'The toggle is active.'
    // Do something special here
}

In the preceding code, the toggle will be enabled, because two (user_id and age) of the three conditions are met.

Unanimous

In a toggle with a Unanimous strategy, all conditions must be met:

<?php

// ...

$manager = new ToggleManager(new InMemoryCollection());

$conditions   = [];
$conditions[] = new OperatorCondition('user_id', new LessThan(42));
$conditions[] = new OperatorCondition('age',     new GreaterThan(24));
$conditions[] = new OperatorCondition('height',  new GreaterThan(5.7));

$toggle = new Toggle('newFeature', $conditions, Toggle::STRATEGY_MAJORITY);

$manager->add($toggle);

$context = new Context();
$context->set('user_id', 41);
        ->set('age', 25);
        ->set('height', 5.6);

if ($toggle->activeFor($context)) {
    echo 'The toggle is active.'
    // Do something special here
}

In the above example, the toggle will be disabled because the third condition (height) is not met based on the context’s value.

Toggle Statuses

Each feature toggle can have three states: active, inactive and conditionally active. When a toggle is manually set to active, it will be always active regardless of the conditions. If a toggle is set as inactive, it will always be inactive. The third state is conditionally active, where the state of the toggle will be dependent on the conditions. This is the default state.

To change the state of a toggle manually, we use the activate() method of the toggle object. This method takes a parameter which is the state of the toggle and can be one of the following constants:

  • Toggle::CONDITIONALLY_ACTIVE (default state)
  • Toggle::ACTIVE
  • Toggle::INACTIVE
<?php

// Conditions here

$toggle->activate(Toggle::CONDITIONALLY_ACTIVE);
$manager->add($toggle);

$context = new Context();
$context->set('user_id', 41);
        ->set('age', 25);
        ->set('height', 5.6);

if ($toggle->activeFor($context)) {
        echo 'The toggle is active.'
        // Do something special here
}

Using Arrays or YAML to Create the Toggles

So far, we have been instantiating different objects (operators, conditions, and toggles) to create feature toggles. We can also create feature toggles using arrays and YAML files, if we prefer configuration over code. In this approach, we define all the conditions and operators as a multidimensional associative array or as YAML objects. Consequently, we use the InMemoryCollectionSerializer service (which is part of the Toggle library) to create all the necessary operators, conditions and toggles automatically.

To define toggles using an array:

<?php

// ...

// Array value
$data = [
    'some-feature' => [
        'name' => 'newFeatureToggle',
        'conditions' => [
            [
                'name' => 'operator-condition',
                'key'  => 'user_id',
                'operator' => ['name' => 'greater-than', 'value' => 41],
            ],
        ],
        'status' => 'conditionally-active',
    ],
];

As we can see, we have an array of toggles. Inside each array, we have two elements: name and condition. name is the toggle’s name and the condition is an array of conditions. In the conditions sub-array, we need to specify the condition’s class (OperatorCondition) but in kebab-case format: operator-condition. The same rule applies for other class names that we use in the configuration array. We also need to define the condition key (in this case user_id) and the operator.

To create the toggles, we deserialize the above array using InMemoryCollectionSerializer like so:

<?php

use Qandidate\Toggle\Context;
use Qandidate\Toggle\ToggleManager;
use Qandidate\Toggle\Serializer\InMemoryCollectionSerializer;

// $data array ( 
// ...
// );

$serializer = new InMemoryCollectionSerializer();
$collection = $serializer->deserialize($data);
$manager    = new ToggleManager($collection);

$context = new Context();
$context->set('user_id', 42);

if ($manager->active('some-feature', $context)) {
    echo 'The toggle is Active';
    // Do something special here
}

Yaml mode is almost identical, but requires the Symfony/yaml package. For more information, refer to the examples.

Wrapping up

Feature toggles let us develop directly on the master branch without having to deal with merge conflicts. They also help us conceal unfinished and risky features from end users, and we can always roll back in case something goes wrong with the code. However, if we don’t use them cautiously, they can become a nightmare in the long term.

We need to make sure that we really need a feature toggle. If we use feature toggles whenever we can, even though they are not needed, at the end of the day we’ll have a pile of barely traceable toggles scattered throughout our code base. This will increase the chances of us exposing buggy features to the public without even noticing it.

While feature toggling is fantastic for one-off easily deletable switches like Xmas themes, special promotions, temporary profile features etc, they should be considered a second option when developing complicated features. Instead, we should see if we can develop and deploy our upgrades in small commits in each release cycle.

Likewise, we should always remove the feature toggles as soon as they are no longer needed. This includes removing the middleware classes and configuration files in which we define them.

That said, Qandidate Toggle is a PHP library that makes managing feature toggling much easier than before. We hope you enjoyed this tutorial, in which we started with a basic example and expanded it into a Laravel environment.

How do you do feature toggling? Let us know! And if you have any questions or comments, please leave them below!

  • http://cashpath20.com JudyJMena

    .❝my neighbor’s mother is making $98 HOURLY on the
    internet❞….

    A few days ago new McLaren F1 subsequent after earning 18,512$,,,this was my
    previous month’s paycheck ,and-a little over, $17k Last month ..3-5 h/r of work a day ..with extra
    open doors & weekly paychecks.. it’s realy the
    easiest work I have ever Do.. I Joined This 7 months ago and now making over
    $87, p/h.

    Learn More right Here….website on my PrroFile
    +fsddfd

  • http://www.d3.lt/ Zy

    Simply clever! Thanks!

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in PHP, once a week, for free.