Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
FEATURE FLAGS ARE
FLAWED
LET'S MAKE THEM BETTER
Created by Stephen Young / @young_steveo
FEATURE FLAG?
I've seen a lot of names for this concept tossed
around: feature bits, ags, ippers, switches
and the like. T...
SIMPLE TOGGLE
class AmazingRecipe
{
public function getSauce()
{
$useNewRecipe = false;
// $useNewRecipe = true;
if ($useN...
TRADITIONAL
FEATURE FLAGS ARE…
FLAWED
...ACTUALLY PRETTY USEFUL AND NOT REALLY FLAWED PER SE, BUT THEY COME WITH A FEW
CHA...
SIMPLE TOGGLE
class AmazingRecipe
{
public function getSauce()
{
$useNewRecipe = false;
// $useNewRecipe = true;
if ($useN...
SIMPLE TOGGLE V2
class AmazingRecipe
{
public function getSauce()
{
if ($this->useNewRecipe()) {
// new formula here
} els...
MOCK TOGGLE
/**
* @dataProvider newFormulaProvider
*/
public function testGetSauce($useNew, $sauce)
{
$sut = $this->getMoc...
SIMPLE TOGGLE V2
class AmazingRecipe
{
public function getSauce()
{
if ($this->useNewRecipe()) {
// new formula here
} els...
FEATURE ROUTER
class AmazingRecipe
{
public function getSauce()
{
if ($this->useNewRecipe()) {
// new formula here
} else ...
FEATURE ROUTER
final class Flags
{
protected static $map = [];
public static function enabled($key)
{
if (empty(static::$m...
TRADITIONAL FEATURE FLAG SYSTEM
Curb Long-Lived Feature Branches
Easy To Use (If This Then That)
Testable
TONS OF APPLICATIONS
Release Toggles - change frequently, short lived
Timed Releases
Operations Toggles - sometimes long l...
WHAT'S NOT TO LIKE?
CYCLOMATIC COMPLEXITY
public function getSauce()
{
if ($this->useNewRecipe()) {
if ($this->testSpicyVersion()) {
// add sp...
ALL OR NOTHING
CANARY DEPLOYMENTS
COHORTS
[…] a cohort is a group of subjects who have
shared a particular event together during a
particular time span.
— W...
MORE CONDITIONS?
if (
Flags::enabled('SomeFeature') &&
$user->canSeeFeature('SomeFeature')
) {
// execute feature code
}
c...
INTRODUCING SWIVEL
Swivel can enable features for a subset of users.
No more complex control ow; Swivel takes care of
dete...
BASIC CONCEPTS
1. Segment users into 10 cohorts. Swivel calls these Buckets.
2. Associate a Feature to a number of active ...
SEGMENT USERS INTO BUCKETS
id user_id bucket_id
1 160 6
2 956 2
3 189 7
4 412 2
ASSOCIATE FEATURES TO BUCKETS
id slug buckets
1 "AwesomeSauce" "[ ]"
2 "AwesomeSauce.Spicy" "[1,2]"
3 "AwesomeSauce.Saucy"...
BOOTSTRAP
$bucket = 5; // From Session or DB
$map = [
'AwesomeSauce' => [1,2,3,4],
'AwesomeSauce.Spicy' => [1,2],
'Awesome...
TOGGLE EXAMPLE
class AmazingRecipe
{
public function __construct(ZumbaSwivelManager $swivel)
{
$this->swivel = $swivel;
}
...
SHORTHAND EXAMPLE
class AmazingRecipe
{
public function __construct(ZumbaSwivelManager $swivel)
{
$this->swivel = $swivel;...
METRICS FOR CANARY DEPLOYMENTS
SWIVEL LOGGING
PSR-3 LOGGER AWARE
$config = new ZumbaSwivelConfig($map, $bucket, $psr3Logger);
// or
$config->setLogger($p...
SWIVEL METRICS
STATSD STYLE METRICS INTERFACE
interface MetricsInterface {
public function count($context, $source, $value...
METRICS FOR A/B TESTING
MORE APPLICATIONS
Release Toggles
Timed Releases
Operations Toggles
Experiments
Permissions?
COMPLEXITY
TRADITIONAL FEATURE FLAGS
PROS
Eliminate Long Lived Branches
Disable Problematic Code
CONS
Complexity Bump
All Or Nothing
COHORT FEATURE FLAGS
PROS
Eliminate Long Lived Branches
Disable Problematic Code
Roll Out Features
A/B Testing
CONS
Comple...
SWIVEL FEATURE FLAGS
PROS
Eliminate Long Lived Branches
Disable Problematic Code
Roll Out Features
A/B Testing
Built In Lo...
SWIVELJS
swivel
.forFeature('AwesomeSauce')
.addBehavior('Spicy', this.getSpicyFormula)
.addBehavior('Saucy', this.getSauc...
COMMUNITY PACKAGES
ZF2 module: https://github.com/mrferos/mrf-swivel
Yii extension: https://github.com/DanaLuther/yii-swiv...
ROADMAP
Bucket Ranges
Con gurable Granularity
CakePHP 3.0 Plugin
QUESTIONS?
GO FORTH AND TOGGLE!
Twitter: @young_steveo
Swivel: https://github.com/zumba/swivel
Joindin: https://legacy.joind.in/17583...
Feature Flags Are Flawed: Let's Make Them Better - DPC
Upcoming SlideShare
Loading in …5
×

Feature Flags Are Flawed: Let's Make Them Better - DPC

282 views

Published on

Traditional Feature Flags v.s. Cohort Feature Flags and the Swivel Library

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Feature Flags Are Flawed: Let's Make Them Better - DPC

  1. 1. FEATURE FLAGS ARE FLAWED LET'S MAKE THEM BETTER Created by Stephen Young / @young_steveo
  2. 2. FEATURE FLAG? I've seen a lot of names for this concept tossed around: feature bits, ags, ippers, switches and the like. There seems no generally accepted name yet. — Martin Fowler
  3. 3. SIMPLE TOGGLE class AmazingRecipe { public function getSauce() { $useNewRecipe = false; // $useNewRecipe = true; if ($useNewRecipe) { // new formula here } else { // original code here } } }
  4. 4. TRADITIONAL FEATURE FLAGS ARE… FLAWED ...ACTUALLY PRETTY USEFUL AND NOT REALLY FLAWED PER SE, BUT THEY COME WITH A FEW CHALLENGES AND I THINK WE CAN IMPROVE THE MODEL, SO LET'S MAKE THEM BETTER
  5. 5. SIMPLE TOGGLE class AmazingRecipe { public function getSauce() { $useNewRecipe = false; // $useNewRecipe = true; if ($useNewRecipe) { // new formula here } else { // original code here } } } ❌ ❌ Not Testable
  6. 6. SIMPLE TOGGLE V2 class AmazingRecipe { public function getSauce() { if ($this->useNewRecipe()) { // new formula here } else { // original code here } } public function useNewRecipe() { return false; // true } } ← ← Can Be Stubbed
  7. 7. MOCK TOGGLE /** * @dataProvider newFormulaProvider */ public function testGetSauce($useNew, $sauce) { $sut = $this->getMock('AmazingRecipe', ['useNewRecipe']); $sut->expects($this->once()) ->method('useNewRecipe') ->will($this->returnValue($useNew)); $this->assertEquals($sauce, $sut->getSauce()); } public function newFormulaProvider() { return [ [ false, 'old Recipe' ], [ true, 'new Recipe' ] ]; }
  8. 8. SIMPLE TOGGLE V2 class AmazingRecipe { public function getSauce() { if ($this->useNewRecipe()) { // new formula here } else { // original code here } } public function useNewRecipe() { return false; // true } } ❌❌❌ Not Maintainable Not Con gurable Not My Concern
  9. 9. FEATURE ROUTER class AmazingRecipe { public function getSauce() { if ($this->useNewRecipe()) { // new formula here } else { // original code here } } public function useNewRecipe() { return Flags::enabled('AmazingRecipie.NewSauceFormula'); } }
  10. 10. FEATURE ROUTER final class Flags { protected static $map = []; public static function enabled($key) { if (empty(static::$map)) { // hydrate map } return !empty(static::$map[$key]); } }
  11. 11. TRADITIONAL FEATURE FLAG SYSTEM Curb Long-Lived Feature Branches Easy To Use (If This Then That) Testable
  12. 12. TONS OF APPLICATIONS Release Toggles - change frequently, short lived Timed Releases Operations Toggles - sometimes long lived, rarely change
  13. 13. WHAT'S NOT TO LIKE?
  14. 14. CYCLOMATIC COMPLEXITY public function getSauce() { if ($this->useNewRecipe()) { if ($this->testSpicyVersion()) { // add spice } if ($this->fixEnabled()) { // fix bug in new code } } else { // original code here if ($this->fixEnabled()) { // fix bug in old code } } } Complexity: 5
  15. 15. ALL OR NOTHING
  16. 16. CANARY DEPLOYMENTS
  17. 17. COHORTS […] a cohort is a group of subjects who have shared a particular event together during a particular time span. — Wikipedia
  18. 18. MORE CONDITIONS? if ( Flags::enabled('SomeFeature') && $user->canSeeFeature('SomeFeature') ) { // execute feature code } class User { public function canSeeFeature($feature) { // check the db or user session? } }
  19. 19. INTRODUCING SWIVEL Swivel can enable features for a subset of users. No more complex control ow; Swivel takes care of determining code paths.
  20. 20. BASIC CONCEPTS 1. Segment users into 10 cohorts. Swivel calls these Buckets. 2. Associate a Feature to a number of active Buckets. 3. Write code that runs when a particular Feature is enabled. Swivel calls these Behaviors.
  21. 21. SEGMENT USERS INTO BUCKETS id user_id bucket_id 1 160 6 2 956 2 3 189 7 4 412 2
  22. 22. ASSOCIATE FEATURES TO BUCKETS id slug buckets 1 "AwesomeSauce" "[ ]" 2 "AwesomeSauce.Spicy" "[1,2]" 3 "AwesomeSauce.Saucy" "[3,4]"
  23. 23. BOOTSTRAP $bucket = 5; // From Session or DB $map = [ 'AwesomeSauce' => [1,2,3,4], 'AwesomeSauce.Spicy' => [1,2], 'AwesomeSauce.Saucy' => [3,4] ]; $config = new ZumbaSwivelConfig($map, $bucket); $swivel = new ZumbaSwivelManager($config);
  24. 24. TOGGLE EXAMPLE class AmazingRecipe { public function __construct(ZumbaSwivelManager $swivel) { $this->swivel = $swivel; } public function getSauce() { return $this->swivel->forFeature('AwesomeSauce') ->addBehavior('Spicy', [$this, 'getSpicyFormula']) ->addBehavior('Saucy', [$this, 'getSaucyFormula']) ->defaultBehavior([$this, 'getFormula']) ->execute(); } protected function getSpicyFormula() { } protected function getSaucyFormula() { } protected function getFormula() { } }
  25. 25. SHORTHAND EXAMPLE class AmazingRecipe { public function __construct(ZumbaSwivelManager $swivel) { $this->swivel = $swivel; } public function getSauce() { return $this->swivel->invoke( 'AwesomeSauce.New', [$this, 'getNewSauce'], [$this, 'getOldSauce'] ); } }
  26. 26. METRICS FOR CANARY DEPLOYMENTS
  27. 27. SWIVEL LOGGING PSR-3 LOGGER AWARE $config = new ZumbaSwivelConfig($map, $bucket, $psr3Logger); // or $config->setLogger($psr3Logger);
  28. 28. SWIVEL METRICS STATSD STYLE METRICS INTERFACE interface MetricsInterface { public function count($context, $source, $value, $metric); public function decrement($context, $source, $metric); public function endMemoryProfile($context, $source, $metric); public function endTiming($context, $source, $metric); public function gauge($context, $source, $value, $metric); public function increment($context, $source, $metric); public function memory($context, $source, $memory, $metric); public function set($context, $source, $value, $metric); public function setNamespace($namespace); public function startMemoryProfile($context, $source, $metric); public function startTiming($context, $source, $metric); public function time($context, $source, Closure $func, $metric); public function timing($context, $source, $value, $metric); }
  29. 29. METRICS FOR A/B TESTING
  30. 30. MORE APPLICATIONS Release Toggles Timed Releases Operations Toggles Experiments Permissions?
  31. 31. COMPLEXITY
  32. 32. TRADITIONAL FEATURE FLAGS PROS Eliminate Long Lived Branches Disable Problematic Code CONS Complexity Bump All Or Nothing
  33. 33. COHORT FEATURE FLAGS PROS Eliminate Long Lived Branches Disable Problematic Code Roll Out Features A/B Testing CONS Complexity Bump All Or Nothing
  34. 34. SWIVEL FEATURE FLAGS PROS Eliminate Long Lived Branches Disable Problematic Code Roll Out Features A/B Testing Built In Logging Built In Metrics CONS Complexity Bump
  35. 35. SWIVELJS swivel .forFeature('AwesomeSauce') .addBehavior('Spicy', this.getSpicyFormula) .addBehavior('Saucy', this.getSaucyFormula) .defaultBehavior(this.getFormula) .execute();
  36. 36. COMMUNITY PACKAGES ZF2 module: https://github.com/mrferos/mrf-swivel Yii extension: https://github.com/DanaLuther/yii-swivel Yii 2.0 extension: https://github.com/DanaLuther/yii2-swivel CakePHP 2 Plugin: https://github.com/zumba/swivel-cake
  37. 37. ROADMAP Bucket Ranges Con gurable Granularity CakePHP 3.0 Plugin
  38. 38. QUESTIONS?
  39. 39. GO FORTH AND TOGGLE! Twitter: @young_steveo Swivel: https://github.com/zumba/swivel Joindin: https://legacy.joind.in/17583 THANKS!

×