An introduction to Feature Flags, canary deployments and finally, Swivel: A PHP library that will manage cohorts and features automatically for the developer.
3. 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
4. SIMPLE TOGGLE
class AmazingRecipe {
public function getSauce() {
$useNewRecipe = false;
// $useNewRecipe = true;
if ($useNewRecipe) {
// new formula here
} else {
// original code here
}
}
}
6. SIMPLE TOGGLE
class AmazingRecipe {
public function getSauce() {
$useNewRecipe = false;
// $useNewRecipe = true;
if ($useNewRecipe) {
// new formula here
} else {
// original code here
}
}
}
❌
❌
Not Testable
7. 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
9. 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
10. 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');
}
}
11. FEATURE ROUTER
final class Flags {
protected static $map = [];
public static function enabled($key) {
if (empty(static::$map)) {
// hydrate map
}
return !empty(static::$map[$key]);
}
}
12. TRADITIONAL FEATURE FLAG SYSTEM
Curb Long-Lived Feature Branches
Easy To Use (If This Then That)
Testable
Tons of Applications
Continuous Delivery
Timed Releases
Operations Toggles
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
17. COHORTS
[…] a cohort is a group of subjects who have
shared a particular event together during a
particular time span.
— Wikipedia
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. INTRODUCING SWIVEL
Swivel can enable features for a subset of users.
No more complex control ow; Swivel takes care of
determining code paths.
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.
4. Group Features under common Parent Feature to toggle
multiple at once.
NewExperience
NewExperience.NavLinks
NewExperience.Dashboard
NewExperience.Pro le
21. SEGMENT USERS INTO BUCKETS
id user_id bucket_id
1 160 6
2 956 2
3 189 7
4 412 2
22. ASSOCIATE FEATURES TO BUCKETS
id slug buckets
1 "AwesomeSauce" "1,2,3,4"
2 "AwesomeSauce.Spicy" "1,2"
3 "AwesomeSauce.Saucy" "3,4"
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. 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() { }
}
26. SWIVEL LOGGING
PSR-3 LOGGER AWARE
$config = new ZumbaSwivelConfig($map, $bucket, $psr3Logger);
// or
$config->setLogger($psr3Logger);
27. 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);
}
31. COHORT FEATURE FLAGS
PROS
Eliminate Long Lived Branches
Disable Problematic Code
Roll Out Features
A/B Testing
CONS
Complexity Bump
All Or Nothing
32. 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