Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Flipping out with Feature Flags & Toggles - PHP...
Search
Michael C.
April 08, 2017
Programming
180
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
180
Other Decks in Programming
See All in Programming
AIに既存システムを理解させる技術 ~レガシーを見捨てないハーネスエンジニアリング入門~
ochtum
0
170
仕様駆動開発へのトライを機に チームに適合する手法を模索し続けている話
freee
PRO
0
650
Pythonの実行はどこまで賢くなったのか? CPythonとPyPyから見る最適化のしくみ
curekoshimizu
3
2.2k
komatsuna「分散システムにおけるバグ分析手法」
komatsunaqa
0
280
S3 を使うアプリケーションをローカル完結で動かすことに全力を注いでみた / Running S3 Apps Offline
contour_gara
0
660
書籍「プロフェッショナルAI駆動開発」紹介スライド
juntaromatsumoto
0
780
AI Engineeringは、AIプロダクトだけのものか? 〜AIがソフトウェアを作る時代の新しい当たり前〜 / No AI in your product. AI Engineering in your development.
rkaga
5
550
FastAPI の並行処理モデルを完全に理解する
hoto17296
8
3.1k
AI駆動開発にグラフDBを重ねてみた
satoshi256kbyte
1
370
Hono + Inertia + React で LP を構築した話
oukayuka
2
180
Go 1.27 における memory allocation の高速化
andpad
0
340
Detecting Compromised CI with eBPF and Cilium Tetragon
lizrice
0
270
Featured
See All Featured
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
1.3k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
The SEO identity crisis: Don't let AI make you average
varn
0
540
The Pragmatic Product Professional
lauravandoore
37
7.4k
WENDY [Excerpt]
tessaabrams
12
39k
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.8k
For a Future-Friendly Web
brad_frost
183
10k
Designing for humans not robots
tammielis
254
26k
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
290
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
240
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
510
Bootstrapping a Software Product
garrettdimon
PRO
306
120k
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