Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Flipping out with Feature Flags & Toggles Symfo...

Michael C.
September 16, 2016

Flipping out with Feature Flags & Toggles Symfony Live London 2016

Quite often it can be useful to enable new functionality for only certain users on your site, or perhaps to only enable it on a test version of your application. One method of doing this, with a single shared codebase, is feature flags, and is the method used by huge organisations with monolithic repositories like Flickr, but can also be used on much smaller scales when you want to test out new functionality in production or perform A/B testing.

This talk will guide you through why feature flags can be helpful to your development workflow, and how you can use them whilst avoiding too much added complexity to your application.

Michael C.

September 16, 2016
Tweet

More Decks by Michael C.

Other Decks in Technology

Transcript

  1. ME?

  2. HARDCODED <?php function isEnabled(string $flag): boolean { if ($flag ==

    'profile') { return true; } return false; } if (isEnabled('profile')) { newProfileMagic(); }
  3. HARDCODED <?php function isEnabled(string $flag): boolean { if ($flag ==

    'profile') { return true; } return false; } if (isEnabled('profile')) { newProfileMagic(); }
  4. PARAMETERS IN CONFIG FILES <?php function isEnabled(string $flag): boolean {

    if ($flag == 'mailDelivery') { return $this->container->getParameter('swiftmailer.enabled', $flag); } elseif (isset($this->container->getParameter(sprintf('flag.%s', $flag)))) { return $this->container->getParameter(sprintf('flag.%s', $flag)); } return false; }
  5. CACHE function isEnabled(string $flag): boolean { $cache = $this->cache; $flag

    = $cache->getItem(sprintf('flag.%s', $flag)); if (!$flag->isHit()) { $flag->get(); } return false; }
  6. TWIG <?php namespace AppBundle\Twig; class AppExtension extends \Twig_Extension { public

    function getFunctions() { return array( new \Twig_SimpleFunction('isEnabled', array($this, 'isEnabled')), ); } //.. }
  7. COMPLEXITY <?php if ($container->getParameter('profileFeature')) { if ($container->getParameter('newProfileStyle')) { if ($container->getParameter('newProfileStyleButtons'))

    { enableButtons(); } if ($container->getParameter('myNewCoolProfilePic')) { renderPicture(); if ($container->getParameter('newProfileRenderEngine')) { tryDifferentRenderEngine(); } } renderNewProfiler(); } else { // Render the legacy profile } }
  8. COMPLEXITY <?php if ($container->getParameter('profileFeature')) { if ($container->getParameter('newProfileStyle')) { if ($container->getParameter('newProfileStyleButtons'))

    { enableButtons(); } if ($container->getParameter('myNewCoolProfilePic')) { renderPicture(); if ($container->getParameter('newProfileRenderEngine')) { tryDifferentRenderEngine(); } } renderNewProfiler(); } else { // Render the legacy profile } }
  9. COMPLEXITY <?php if ($container->getParameter('profileFeature')) { if ($container->getParameter('newProfileStyle')) { if ($container->getParameter('newProfileStyleButtons'))

    { enableButtons(); } if ($container->getParameter('myNewCoolProfilePic')) { renderPicture(); if ($container->getParameter('newProfileRenderEngine')) { tryDifferentRenderEngine(); } } renderNewProfiler(); } else { // Render the legacy profile } }
  10. CONTROLLERS - EVENT <?php public function onKernelRequest($event) { $flag =

    $event->getRequest->attributes->get('_flag'); $alternate = $event->getRequest->attributes->get('_alt'); if (isEnabled($flag)) { $event->getRequest->attributes->set(‘controller’, $alternate); } }
  11. CONTROLLERS - EVENT <?php public function onKernelRequest($event) { $flag =

    $event->getRequest->attributes->get(‘_flag'); $alternate = $event->getRequest->attributes->get(‘_alt'); if (isEnabled($flag)) { $event->getRequest->attributes->set(‘_controller’, $alternate); } if (!isset($alternate) && isset($flag) && !isEnabled($flag)) { throw new NotFoundHttpException(); } }
  12. CONTROLLERS - EVENT <?php public function onKernelRequest($event) { $flag =

    $event->getRequest->attributes->get(‘_flag'); $alternate = $event->getRequest->attributes->get(‘_alt'); if (isEnabled($flag)) { $event->getRequest->attributes->set(‘_controller’, $alternate); } if (!isset($alternate) && isset($flag) && !isEnabled($flag)) { throw new NotFoundHttpException(); } }
  13. NON-GENERIC FACTORY <?php class ProfileFactory { public function create(): ProfileInterface

    { return isEnabled(‘newProfile’) ? $container->get(‘newProfile’) : $container->get(‘legacyProfile’); } }
  14. GENERIC FLAGS FACTORY <?php class FlagsFactory { public function createService(string

    $flag, $service, $alternate) { return isEnabled($flag) ? $container->get($service) : $container->get($alternate); } }
  15. GENERIC FLAGS FACTORY services: app.flagsFactory: class: AppBundle\FlagsFactory app.profileManager: class: AppBundle\Profile\ProfileManager

    factory: ['@app.flagsFactory', createService] arguments: - newProfile - legacyProfileManager - newProfileManager