FeatureFlagBundle
A bundle to manage feature flags & A/B tests, (*1)
this bundle is not production-ready - some features have not been fully developed or tested, (*2)
Dependencies
Feature flags
Features are defined in the application configuration. Features can be enabled/disabled :
* using the provided admin controller (global level),
* by your application (session level),
* using the provided front-end controller (session level),
* by adding a request parameter to the page url (page level)., (*3)
A feature can have the following default state :
* disabled-always : the feature is disabled and cannot be enabled,
* disabled-hidden : the feature is disabled, and cannot be globally enabled.
It can be enabled for development/testing purposes using the front-end session controller or the request parameter,
* disabled : the feature is disabled and can be enabled,
* enabled : the feature is enabled and can be disabled,
* enabled-always : the feature is always enabled, and cannot de disabled., (*4)
A/B Testing
A feature can have multiple variations. The list of variations is defined in your configuration file,
and it is your responsibility to modify your templates according to those variations., (*5)
Variations are automatically chosen by the bundle, but can be changed :
* by your application (session level),
* using the provided front-end controller (session level),
* by adding a request parameter to the page url (page level)., (*6)
The service provides a simple success/failure couting mechanism. You can also use your favorite external analytics service., (*7)
Configuration
The basic configuration is :, (*8)
socloz_feature_flag:
options:
redis:
host: "host:port,host:port"
features:
feature_name:
state: [enabled|enabled-always|disabled|disabled-hidden|disabled-always]
description: used for the admin page
variations: list of variation names
Other options :, (*9)
-
socloz_feature_flag.options.redis.prefix
: prefix for redis keys (default : socloz_feature_flag
)
Using feature flags
Testing if a feature is enabled for the current user :, (*10)
PHP, (*11)
if ($this->get('socloz_feature_flag.feature')->isEnabled('feature_name')) { [...] }
Twig, (*12)
{% if feature_is_enabled('feature_name') %}...{% endif %}
Enabling/disabling a feature for the current user :, (*13)
PHP, (*14)
$this->get('socloz_feature_flag.feature')->enableForUser('feature_name');
$this->get('socloz_feature_flag.feature')->disableForUser('feature_name');
Using A/B Testing
Getting the feature variation for the current user :, (*15)
PHP, (*16)
if ($this->get('socloz_feature_flag.abtesting')->getFeatureVariation('feature_name') == 'A') { [...] }
Twig, (*17)
{% if ab_variation('feature_name') == 'A' %}...{% endif %}
Couting transactions :, (*18)
PHP, (*19)
$this->get('socloz_feature_flag.abtesting')->begin('feature_name');
$this->get('socloz_feature_flag.abtesting')->success('feature_name');
$this->get('socloz_feature_flag.abtesting')->failure('feature_name');
Analytics call :, (*20)
Twig, (*21)
{{ ab_tracking('feature_name') }}
Admin interface
The admin interface is located at /feature-flag/admin/features
., (*22)
To use the admin interface, you must load the admin routes in routing.yml
:, (*23)
socloz_feature_flag_feature_admin:
resource: "@SoclozFeatureFlagBundle/Resources/config/routing/admin.xml"
prefix: /feature-flag/admin
The twig template use Twitter Bootstrap CSS classes, but does not include Twitter Bootstrap., (*24)
You can override the layout by creating a app/Resources/SoclozFeatureBundle/views/layout-admin.html.twig
file, such as :, (*25)
{% extends "AcmeAdminBundle::layout.html.twig" %}
{% block my_block %}
{% block socloz_feature_flag %}
{% endblock socloz_feature_flag %}
{% endblock %}
You have to secure the admin route in your security.yml
file :, (*26)
access_control:
- { path: ^/feature-flag/admin/, role: IS_AUTHENTICATED_FULLY }
Use whatever role is appropriate..., (*27)
Front-end controllers
If you want to be able to switch features or variations for the current session, load the corresponding routes :, (*28)
socloz_feature_flag_feature:
resource: "@SoclozFeatureFlagBundle/Resources/config/routing/feature.xml"
prefix: /feature-flag
socloz_feature_flag_ab:
resource: "@SoclozFeatureFlagBundle/Resources/config/routing/ab_testing.xml"
prefix: /feature-flag
You can toggle a service for the current session using /feature-flag/{feature name}/enable
and /feature-flag/{feature name}/disable
., (*29)
You can change variation for the current session using /feature-flag/{feature name}/change_variation/{variation}
., (*30)
The controllers redirect to the route configured as socloz_feature_flag.options.base.redirect
(use route names)., (*31)
You can display the feature state or variation using /feature-flag/{feature name}/status
or /feature-flag/{feature name}/ab_status
, (*32)
Page-level toggles
You can change variation or toggle features using query string parameters :, (*33)
-
socloz_feature_flag_{feature name}_enabled
(0 to disable, 1 to enable)
-
socloz_feature_flag_{feature name}_variation
(add the variation name)
Google Analytics
The Google Analytics implementation expects that you use the async Google Analytics tag. If your tag looks like this :, (*34)
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-XXXXX-X']);
_gaq.push(['_trackPageview']);
[...]
everything is fine. If it does not look like it, analytics won't work., (*35)
A single custom variable (session level) is used for all features. Therefore, you have to use the same variation set for all your features,
and use the socloz_feature_flag.splitter.shared_random
splitter (see below)., (*36)
The custom variable can be configured using :
* socloz_feature_flag.google_analytics.variable_id
(id of the variable, default 1)
* socloz_feature_flag.google_analytics.variable_name
(name of the variable, default socloz_feature_flag_variation), (*37)
Advanced configuration
All internal services can be overloaded :, (*38)
Splitter service, (*39)
Configuration : socloz_feature_flag.services.splitter
, (*40)
The splitter service chooses a variation for the current user.
The available splitters are :
* socloz_feature_flag.splitter.random
(default) : chooses a random variation
* socloz_feature_flag.splitter.shared_random
: tries to use the same variation for feature having the same variation set (useful for Google Analytics), (*41)
A custom splitter can be written. It must implement Socloz\FeatureFlagBundle\ABTesting\Splitter\SplitterInterface
., (*42)
Transaction service, (*43)
Configuration : socloz_feature_flag.services.transaction
, (*44)
The transaction service counts successful transactions., (*45)
There is only one transaction implemented : socloz_feature_flag.transaction.session
., (*46)
A custom transaction service can be written. It must implement Socloz\FeatureFlagBundle\ABTesting\Transaction\TransactionInterface
., (*47)
State services, (*48)
Configuration : socloz_feature_flag.services.state
, (*49)
The features states for the current user are stored/recalled from a list of state services. State is written to all services, and is read from the first one., (*50)
The default is : [socloz_feature_flag.state.request, socloz_feature_flag.state.session]
., (*51)
-
socloz_feature_flag.state.request
reads the query string and does not write anywhere,
-
socloz_feature_flag.state.session
reads/writes the session.
A custom state service can be written. It must implement Socloz\FeatureFlagBundle\State\StateInterface
., (*52)
Storage service, (*53)
Configuration : socloz_feature_flag.services.storage
, (*54)
The storage service stores global data (feature states, counts, ...), (*55)
The default is : socloz_feature_flag.storage.redis
., (*56)
A custom storage service can be written. It must implement Socloz\FeatureFlagBundle\Storage\StorageInterface
., (*57)
Analytics service, (*58)
Configuration : socloz_feature_flag.services.analytics
, (*59)
The storage service generates the HTML code used to call the external analytics service., (*60)
The default is : socloz_feature_flag.analytics.google_analytics
., (*61)
A custom analytics service can be written. It must implement Socloz\FeatureFlagBundle\Analytics\AnalyticsInterface
., (*62)
License
This bundle is released under the MIT license (see LICENSE)., (*63)