Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Flipping out with Feature Flags & Toggles - PHP...
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Michael C.
April 08, 2017
Programming
190
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Flipping out with Feature Flags & Toggles - PHP Yorkshire 2017
Michael C.
April 08, 2017
More Decks by Michael C.
See All by Michael C.
OOP & Design Patterns (Part 1 + Part 2)
michaelcullum
0
680
Building a first class REST API with Symfony
michaelcullum
3
2.1k
Trend Analysis and Machine Learning in PHP - PHP South Africa
michaelcullum
0
240
Hadoop & PHP - PHP South Africa
michaelcullum
0
190
Machine Learning and Trend Analysis in PHP - Cascadia PHP
michaelcullum
0
190
Trend Analysis & Machine Learning in PHP - PHP SW
michaelcullum
0
180
Machine Learning and Trend Analysis in PHP - DPC 18
michaelcullum
0
330
Trend Analysis & Machine Learning in PHP - PHP Serbia
michaelcullum
1
410
Machine Learning and Trend Analysis in PHP - DevDays Vilnius
michaelcullum
1
190
Other Decks in Programming
See All in Programming
App Intentsのビルドプロセスを支える技術
kntkymt
0
390
更なる可用性を求めて、5年間運用したKotlinのアプリケーションをGoでリプレイスする話
ken_tunc
0
290
海上で動くGoサーバー: goroutineとchannelでさばく航行データストリーム
atsuki_seo
0
730
UPDATE をやめる — EF Core でマスタをバージョン管理する
panda728
PRO
0
160
cdk deploy JawsSonic #MARATHONしながらAWSリソースをデプロイしてみよう
akihisaikeda
2
140
AWS DevOps Agentで インシデント対応をAIに任せたい
honmarkhunt
7
3k
Family mrubyの進捗
kishima
1
130
thread_parallel_with_free-threaded_Python_and_NumPy.pdf
riku_sakamoto
0
340
20260914 AIエージェント時代のPlatform Engineering LLM基盤とプロダクトの責務境界線
kanfab1
7
2.1k
{ Android | Kotlin } Gradle Plugin in 2026
ryunen344
1
320
LL言語やWebフレームワークのPostgreSQL対応 〜DBの機能がユーザーに届くまで〜
kentaroutakeda
1
150
Vue Fes Japan 2026 タイムテーブル徹底解説
448jp
1
250
Featured
See All Featured
Rails Girls Zürich Keynote
gr2m
96
14k
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
330
Accessibility Awareness
sabderemane
1
210
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
390
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
460
sira's awesome portfolio website redesign presentation
elsirapls
0
420
Measuring & Analyzing Core Web Vitals
bluesmoon
9
1k
The SEO Collaboration Effect
kristinabergwall1
1
570
YesSQL, Process and Tooling at Scale
rocio
174
15k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
6.2k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.6k
Context Engineering - Making Every Token Count
addyosmani
9
1.1k
Transcript
FLIPPING OUT WITH FEATURE FLAGS & TOGGLES PHP YORKSHIRE 2017
@MICHAELCULLUMUK
ME?
MICHAEL CULLUM @MICHAELCULLUMUK
BACK TO BASICS
CONDITIONALS <?php if (true) { oneAction(); } else { alternativeAction();
}
CONDITIONALS <?php if ($enabled) { oneAction(); } else { alternativeAction();
}
FEATURE FLAG <?php if (featureEnabled('twitter_seen')) { oneAction(); } else {
alternativeAction(); }
WHAT’S IN A NAME?
SWIFT MAILER
SWIFT MAILER swiftmailer: disable_delivery: ~
SWIFT MAILER <?php if (isset($mailer[‘disable_delivery'])) { $transport = 'null'; $container->setParameter('swiftmailer.enabled',
false); } else { $container->setParameter('swiftmailer.enabled', true); }
SWIFT MAILER <?php if (isset($mailer[‘disable_delivery'])) { $transport = 'null'; $container->setParameter('swiftmailer.enabled',
false); } else { $container->setParameter('swiftmailer.enabled', true); }
ALTERNATIVE TO BRANCHING?
VARY BY: ENVIRONMENT USER
DIFFERENT SUBSET OF USERS
A/B TESTING
BOOKING.COM MODEL
TEST IN PRODUCTION
MONOLITHIC REPOSITORIES
TEST A MATRIX OF CHANGES
MANAGING YOUR FLAGS
RETRIEVING FEATURE FLAG VALUES
SPLIT IT OUT <?php interface FeatureFlags { function isEnabled(string $flag):
boolean }
HARDCODED <?php function isEnabled(string $flag): boolean { if ($flag ==
'profile') { return true; } return false; } if (isEnabled('profile')) { newProfileMagic(); }
HARDCODED <?php function isEnabled(string $flag): boolean { if ($flag ==
'profile') { return true; } return false; } if (isEnabled('profile')) { newProfileMagic(); }
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; }
CACHE function isEnabled(string $flag): boolean { $cache = $this->cache; $flag
= $cache->getItem(sprintf('flag.%s', $flag)); if (!$flag->isHit()) { $flag->get(); } return false; }
DATABASE
SETTING FEATURE FLAG VALUES
DATABASE
CODEBASE
USING THEM CLEANLY
TWIG {% if isFeatureEnabled('showCfpForm') %} {{ form(form) }} {% endif
%}
TWIG <?php namespace AppBundle\Twig; class AppExtension extends \Twig_Extension { public
function getFunctions() { return array( new \Twig_SimpleFunction('isEnabled', array($this, 'isEnabled')), ); } //.. }
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 } }
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 } }
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 } }
CONTROLLERS
CONTROLLERS - YAML profile: path: /profile/{user} defaults: _controller: DemoBundle:Profile:legacy _alt:
DemoBundle:Default:new _flag: newProfile
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); } }
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(); } }
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(); } }
SERVICE FACTORIES
NON-GENERIC FACTORY <?php class ProfileFactory { public function create(): ProfileInterface
{ return isEnabled(‘newProfile’) ? $container->get(‘newProfile’) : $container->get(‘legacyProfile’); } }
GENERIC FLAGS FACTORY <?php class FlagsFactory { public function createService(string
$flag, $service, $alternate) { return isEnabled($flag) ? $container->get($service) : $container->get($alternate); } }
GENERIC FLAGS FACTORY services: app.flagsFactory: class: AppBundle\FlagsFactory app.profileManager: class: AppBundle\Profile\ProfileManager
factory: ['@app.flagsFactory', createService] arguments: - newProfile - legacyProfileManager - newProfileManager
REMOVE THEM LATER OR NOT?
ALLOWS YOU TO
BRANCHING WITHOUT CONFLICTS OR VCS
TEST IN PRODUCTION
SLOW ROLLOUT
FUNCTIONALITY ON DIFFERENT ENVIRONMENTS
A/B TESTING
CLEANLY
DATABASE OR CONFIGURATION FILES
AVOIDING LARGE CONDITIONALS
THANKS @MICHAELCULLUMUK
FLIPPING OUT WITH FEATURE FLAGS & TOGGLES PHP YORKSHIRE 2017
@MICHAELCULLUMUK