Slide 1

Slide 1 text

FLIPPING OUT WITH FEATURE FLAGS & TOGGLES PHP YORKSHIRE 2017 @MICHAELCULLUMUK

Slide 2

Slide 2 text

ME?

Slide 3

Slide 3 text

MICHAEL CULLUM @MICHAELCULLUMUK

Slide 4

Slide 4 text

BACK TO BASICS

Slide 5

Slide 5 text

CONDITIONALS

Slide 6

Slide 6 text

CONDITIONALS

Slide 7

Slide 7 text

FEATURE FLAG

Slide 8

Slide 8 text

WHAT’S IN A NAME?

Slide 9

Slide 9 text

SWIFT MAILER

Slide 10

Slide 10 text

SWIFT MAILER swiftmailer: disable_delivery: ~

Slide 11

Slide 11 text

SWIFT MAILER setParameter('swiftmailer.enabled', false); } else { $container->setParameter('swiftmailer.enabled', true); }

Slide 12

Slide 12 text

SWIFT MAILER setParameter('swiftmailer.enabled', false); } else { $container->setParameter('swiftmailer.enabled', true); }

Slide 13

Slide 13 text

ALTERNATIVE TO BRANCHING?

Slide 14

Slide 14 text

VARY BY: ENVIRONMENT USER

Slide 15

Slide 15 text

DIFFERENT SUBSET OF USERS

Slide 16

Slide 16 text

A/B TESTING

Slide 17

Slide 17 text

BOOKING.COM MODEL

Slide 18

Slide 18 text

TEST IN PRODUCTION

Slide 19

Slide 19 text

MONOLITHIC REPOSITORIES

Slide 20

Slide 20 text

TEST A MATRIX OF CHANGES

Slide 21

Slide 21 text

MANAGING YOUR FLAGS

Slide 22

Slide 22 text

RETRIEVING FEATURE FLAG VALUES

Slide 23

Slide 23 text

SPLIT IT OUT

Slide 24

Slide 24 text

HARDCODED

Slide 25

Slide 25 text

HARDCODED

Slide 26

Slide 26 text

PARAMETERS IN CONFIG FILES container->getParameter('swiftmailer.enabled', $flag); } elseif (isset($this->container->getParameter(sprintf('flag.%s', $flag)))) { return $this->container->getParameter(sprintf('flag.%s', $flag)); } return false; }

Slide 27

Slide 27 text

CACHE function isEnabled(string $flag): boolean { $cache = $this->cache; $flag = $cache->getItem(sprintf('flag.%s', $flag)); if (!$flag->isHit()) { $flag->get(); } return false; }

Slide 28

Slide 28 text

DATABASE

Slide 29

Slide 29 text

SETTING FEATURE FLAG VALUES

Slide 30

Slide 30 text

DATABASE

Slide 31

Slide 31 text

CODEBASE

Slide 32

Slide 32 text

USING THEM CLEANLY

Slide 33

Slide 33 text

TWIG {% if isFeatureEnabled('showCfpForm') %} {{ form(form) }} {% endif %}

Slide 34

Slide 34 text

TWIG

Slide 35

Slide 35 text

COMPLEXITY 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 } }

Slide 36

Slide 36 text

COMPLEXITY 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 } }

Slide 37

Slide 37 text

COMPLEXITY 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 } }

Slide 38

Slide 38 text

CONTROLLERS

Slide 39

Slide 39 text

CONTROLLERS - YAML profile: path: /profile/{user} defaults: _controller: DemoBundle:Profile:legacy _alt: DemoBundle:Default:new _flag: newProfile

Slide 40

Slide 40 text

CONTROLLERS - EVENT getRequest->attributes->get('_flag'); $alternate = $event->getRequest->attributes->get('_alt'); if (isEnabled($flag)) { $event->getRequest->attributes->set(‘controller’, $alternate); } }

Slide 41

Slide 41 text

CONTROLLERS - 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(); } }

Slide 42

Slide 42 text

CONTROLLERS - 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(); } }

Slide 43

Slide 43 text

SERVICE FACTORIES

Slide 44

Slide 44 text

NON-GENERIC FACTORY get(‘newProfile’) : $container->get(‘legacyProfile’); } }

Slide 45

Slide 45 text

GENERIC FLAGS FACTORY get($service) : $container->get($alternate); } }

Slide 46

Slide 46 text

GENERIC FLAGS FACTORY services: app.flagsFactory: class: AppBundle\FlagsFactory app.profileManager: class: AppBundle\Profile\ProfileManager factory: ['@app.flagsFactory', createService] arguments: - newProfile - legacyProfileManager - newProfileManager

Slide 47

Slide 47 text

REMOVE THEM LATER OR NOT?

Slide 48

Slide 48 text

ALLOWS YOU TO

Slide 49

Slide 49 text

BRANCHING WITHOUT CONFLICTS OR VCS

Slide 50

Slide 50 text

TEST IN PRODUCTION

Slide 51

Slide 51 text

SLOW ROLLOUT

Slide 52

Slide 52 text

FUNCTIONALITY ON DIFFERENT ENVIRONMENTS

Slide 53

Slide 53 text

A/B TESTING

Slide 54

Slide 54 text

CLEANLY

Slide 55

Slide 55 text

DATABASE OR CONFIGURATION FILES

Slide 56

Slide 56 text

AVOIDING LARGE CONDITIONALS

Slide 57

Slide 57 text

THANKS @MICHAELCULLUMUK

Slide 58

Slide 58 text

FLIPPING OUT WITH FEATURE FLAGS & TOGGLES PHP YORKSHIRE 2017 @MICHAELCULLUMUK