Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dr Sheldon Cooper Presents: Fun with Flags (NEPHP)

Michael Heap
September 20, 2018

Dr Sheldon Cooper Presents: Fun with Flags (NEPHP)

No no, not country flags, feature flags! Feature flags are a powerful technique that allows teams to modify a system’s behaviour without changing code. They can be used several reasons – canary releases and A/B testing to name a few.

This talk will show you how you’re already using feature flags in your application without realising it. Next, we’ll take a look at some of the best tooling out there to help you take feature flags to the next level. Finally, we’ll cover strategies for removing feature flags before they become technical debt that you have to manage.

Michael Heap

September 20, 2018
Tweet

More Decks by Michael Heap

Other Decks in Technology

Transcript

  1. 1. Permissions 2. Early-access 3. Opt-in 4. Incremental roll-out 5.

    Block users 6. Calendar driven 7. Kill switch
  2. 1. Permissions 2. Early-access 3. Opt-in 4. Incremental roll-out 5.

    Block users 6. Calendar driven 7. Kill switch 8. Power user
  3. 1. Permissions 2. Early-access 3. Opt-in 4. Incremental roll-out 5.

    Block users 6. Calendar driven 7. Kill switch 8. Power user 9. Maintenance
  4. 1. Permissions 2. Early-access 3. Opt-in 4. Incremental roll-out 5.

    Block users 6. Calendar driven 7. Kill switch 8. Power user 9. Maintenance 10. Sunset
  5. Ownership Release toggles owned by devs Ops toggles owned by

    ops Permission toggles Experiment toggles owned by product
  6. On/Off Switch Percentage of users Individuals Enable or disable entirely

    Toggle for a percentage of users Toggle for specific users
  7. On/Off Switch Percentage of users Individuals Groups Enable or disable

    entirely Toggle for a percentage of users Toggle for specific users Enable for specific groups e.g. Power Users
  8. On/Off Switch Percentage of users Individuals Groups Request information Enable

    or disable entirely Toggle for a percentage of users Toggle for specific users Enable for specific groups e.g. Power Users Enable for specific request headers or bodies
  9. 1. Dashboard 2. Access control 3. Complex rules 4. Cascading

    flags 5. Multivariate rules 6. Auditing
  10. 1. Dashboard 2. Access control 3. Complex rules 4. Cascading

    flags 5. Multivariate rules 6. Auditing 7. Speed
  11. 1. Dashboard 2. Access control 3. Complex rules 4. Cascading

    flags 5. Multivariate rules 6. Auditing 7. Speed 8. Scale
  12. 1. Dashboard 2. Access control 3. Complex rules 4. Cascading

    flags 5. Multivariate rules 6. Auditing 7. Speed 8. Scale 9. 100% uptime
  13. Qandidate Toggle $manager = new ToggleManager(new InMemoryCollection()); $afterSix = new

    GreaterThanEqual(18); $beforeTen = new LessThanEqual(22); $conditions = []; $conditions[] = new OperatorCondition('time', $afterSix); $conditions[] = new OperatorCondition('time', $beforeTen); $toggle = new Toggle('disable-work-email', $conditions, Toggle::STRATEGY_UNANIMOUS); $manager->add($toggle); $context = new Context(); $context->set('time', (int) date('G')); echo 'Everyone is ' . ($manager->active('disable-work-email', $context)) ? ‘happy!' : 'sad :(';
  14. Qandidate Toggle $manager = new ToggleManager(new InMemoryCollection()); $afterSix = new

    GreaterThanEqual(18); $beforeTen = new LessThanEqual(22); $conditions = []; $conditions[] = new OperatorCondition('time', $afterSix); $conditions[] = new OperatorCondition('time', $beforeTen); $toggle = new Toggle('disable-work-email', $conditions, Toggle::STRATEGY_UNANIMOUS); $manager->add($toggle); $context = new Context(); $context->set('time', (int) date('G')); echo 'Everyone is ' . ($manager->active('disable-work-email', $context)) ? ‘happy!' : 'sad :(';
  15. Qandidate Toggle $manager = new ToggleManager(new InMemoryCollection()); $afterSix = new

    GreaterThanEqual(18); $beforeTen = new LessThanEqual(22); $conditions = []; $conditions[] = new OperatorCondition('time', $afterSix); $conditions[] = new OperatorCondition('time', $beforeTen); $toggle = new Toggle('disable-work-email', $conditions, Toggle::STRATEGY_UNANIMOUS); $manager->add($toggle); $context = new Context(); $context->set('time', (int) date('G')); echo 'Everyone is ' . ($manager->active('disable-work-email', $context)) ? ‘happy!' : 'sad :(';
  16. Qandidate Toggle $manager = new ToggleManager(new InMemoryCollection()); $afterSix = new

    GreaterThanEqual(18); $beforeTen = new LessThanEqual(22); $conditions = []; $conditions[] = new OperatorCondition('time', $afterSix); $conditions[] = new OperatorCondition('time', $beforeTen); $toggle = new Toggle('disable-work-email', $conditions, Toggle::STRATEGY_UNANIMOUS); $manager->add($toggle); $context = new Context(); $context->set('time', (int) date('G')); echo 'Everyone is ' . ($manager->active('disable-work-email', $context)) ? ‘happy!' : 'sad :(';
  17. Qandidate Toggle $manager = new ToggleManager(new InMemoryCollection()); $afterSix = new

    GreaterThanEqual(18); $beforeTen = new LessThanEqual(22); $conditions = []; $conditions[] = new OperatorCondition('time', $afterSix); $conditions[] = new OperatorCondition('time', $beforeTen); $toggle = new Toggle('disable-work-email', $conditions, Toggle::STRATEGY_AFFIRMATIVE); $manager->add($toggle); $context = new Context(); $context->set('time', (int) date('G')); echo 'Everyone is ' . ($manager->active('disable-work-email', $context)) ? ‘happy!' : 'sad :(';
  18. Qandidate Toggle $manager = new ToggleManager(new InMemoryCollection()); $afterSix = new

    GreaterThanEqual(18); $beforeTen = new LessThanEqual(22); $conditions = []; $conditions[] = new OperatorCondition('time', $afterSix); $conditions[] = new OperatorCondition('time', $beforeTen); $toggle = new Toggle('disable-work-email', $conditions, Toggle::STRATEGY_MAJORITY); $manager->add($toggle); $context = new Context(); $context->set('time', (int) date('G')); echo 'Everyone is ' . ($manager->active('disable-work-email', $context)) ? ‘happy!' : 'sad :(';
  19. Qandidate Toggle $manager = new ToggleManager(new InMemoryCollection()); $afterSix = new

    GreaterThanEqual(18); $beforeTen = new LessThanEqual(22); $conditions = []; $conditions[] = new OperatorCondition('time', $afterSix); $conditions[] = new OperatorCondition('time', $beforeTen); $toggle = new Toggle('disable-work-email', $conditions, Toggle::STRATEGY_AFFIRMATIVE); $manager->add($toggle); $context = new Context(); $context->set('time', (int) date('G')); echo 'Everyone is ' . ($manager->active('disable-work-email', $context)) ? ‘happy!' : 'sad :(';
  20. Qandidate Toggle $manager = new ToggleManager(new InMemoryCollection()); $afterSix = new

    GreaterThanEqual(18); $beforeTen = new LessThanEqual(22); $conditions = []; $conditions[] = new OperatorCondition('time', $afterSix); $conditions[] = new OperatorCondition('time', $beforeTen); $toggle = new Toggle('disable-work-email', $conditions, Toggle::STRATEGY_UNANIMOUS); $manager->add($toggle); $context = new Context(); $context->set('time', (int) date('G')); echo 'Everyone is ' . ($manager->active('disable-work-email', $context)) ? ‘happy!' : 'sad :(';
  21. Qandidate Toggle $manager = new ToggleManager(new InMemoryCollection()); $afterSix = new

    GreaterThanEqual(18); $beforeTen = new LessThanEqual(22); $conditions = []; $conditions[] = new OperatorCondition('time', $afterSix); $conditions[] = new OperatorCondition('time', $beforeTen); $toggle = new Toggle('disable-work-email', $conditions, Toggle::STRATEGY_UNANIMOUS); $manager->add($toggle); $context = new Context(); $context->set('time', (int) date('G')); echo 'Everyone is ' . ($manager->active('disable-work-email', $context)) ? ‘happy!' : 'sad :(';
  22. Qandidate Toggle $manager = new ToggleManager(new InMemoryCollection()); $afterSix = new

    GreaterThanEqual(18); $beforeTen = new LessThanEqual(22); $conditions = []; $conditions[] = new OperatorCondition('time', $afterSix); $conditions[] = new OperatorCondition('time', $beforeTen); $toggle = new Toggle('disable-work-email', $conditions, Toggle::STRATEGY_UNANIMOUS); $manager->add($toggle); $context = new Context(); $context->set('time', (int) date('G')); echo 'Everyone is ' . ($manager->active('disable-work-email', $context)) ? ‘happy!' : 'sad :(';
  23. $manager = new ToggleManager(new InMemoryCollection()); $afterSix = new GreaterThanEqual(18); $beforeTen

    = new LessThanEqual(22); $conditions = []; $conditions[] = new OperatorCondition('time', $afterSix); $conditions[] = new OperatorCondition('time', $beforeTen); $toggle = new Toggle('disable-work-email', $conditions); $manager->add($toggle); Configuration: Code
  24. Configuration: Array $data = array( 'some-feature' => array( 'name' =>

    'toggling', 'conditions' => array( array( 'name' => 'operator-condition', 'key' => 'user_id', 'operator' => array('name' => 'greater-than', 'value' => 41), ), ), ), ); $serializer = new InMemoryCollectionSerializer(); $collection = $serializer->deserialize($data); $manager = new ToggleManager($collection);
  25. Configuration: JSON/YAML $yaml = <<<YML toggles: - name: some-feature conditions:

    - name: operator-condition key: user_id operator: name: greater-than value: 41 YML; $array = Yaml::parse($yaml); $serializer = new InMemoryCollectionSerializer(); $collection = $serializer->deserialize($array); $manager = new ToggleManager($collection);
  26. Qandidate Toggle API { "conditions" : [ { "name" :

    "operator-condition", "operator" : { "name" : "less-than", "value" : "1337" }, "key" : "user_id" } ], "name" : "foo", "status" : "conditionally-active", "originalName" : "foo" } PUT /toggles/{name} https://github.com/qandidate-labs/qandidate-toggle-api
  27. Buy

  28. Buy (Pros) 1. Lovely dashboard 2. Multi-language 3. Built in

    audit log 4. Environment aware 5. A/B testing conversion tracking
  29. Buy (Cons) 1. It costs (at least) $79/month 2. Can’t

    modify the product 3. Dependent on 3rd party
  30. public function onKernelRequest($event) { $flag = $event->getRequest->attributes->get('_flag'); $alt = $event->getRequest->attributes->get('_alt');

    if ($toggleManager->active($flag, $context)) { $event->getRequest->attributes->set('controller', $alt); } } profile: path: /profile/{user} defaults: _controller: AcmeBundle:Profile:update _alt: AcmeBundle:NewProfile:update _flag: new-profile-controller
  31. {% if feature_is_active('new-profile-image-picker') %} {# Show new picker #} {%

    else %} {# Use old flash-based picker #} {% endif %}
  32. <?php class AudioProcessorFactory { public function build($args, $context) { if

    ($this->toggleManager->active('enhanced-audio', $context)) { return new EnhancedAudioStreamProcessor($args); } return new BasicAudioStreamProcessor($args); } }
  33. Controller View Factory Enable a route for specific users Show

    different UI elements Branch by abstraction for refactoring
  34. Hard Coded Deploy Config Parameterised Config Change the code Deploy

    your application Edit config, restart app Dynamic Static
  35. Hard Coded Deploy Config Parameterised Config Database Config Change the

    code Deploy your application Edit config, restart app Edit via UI Dynamic Static
  36. Hard Coded Deploy Config Parameterised Config Database Config Distributed Config

    Change the code Deploy your application Edit config, restart app Edit via UI e.g. Consul or Zookeeper Dynamic Static
  37. Testing Flags Feature A Feature B Feature C ✅ ✅

    ✅ ✅ ☠ ✅ ✅ ✅ ☠ ☠ ✅ ✅ ✅ ☠ ☠ ☠ ✅ ☠ ☠ ☠ ✅ ☠ ☠ ☠
  38. Testing Flags Feature A Feature B Feature C Feature D

    ✅ ✅ ✅ ✅ ✅ ✅ ✅ ☠ ✅ ✅ ☠ ✅ ✅ ☠ ✅ ✅ ☠ ✅ ✅ ✅ ✅ ✅ ☠ ☠ ✅ ☠ ✅ ☠ ✅ ☠ ☠ ✅ ☠ ✅ ✅ ☠ ☠ ✅ ☠ ✅ ☠ ☠ ✅ ✅ ✅ ☠ ☠ ☠ ☠ ✅ ☠ ☠ ☠ ☠ ✅ ☠ ☠ ☠ ☠ ✅ ☠ ☠ ☠ ☠
  39. Testing Flags Number of flags Possible permutations 2 4 3

    8 4 16 5 32 10 1024 25 33,554,432 50 1.12589991E+15 100 1.2676506E+30
  40. Homepage Carousel Homepage CTA ✅ ✅ ✅ ☠ ☠ ✅

    ☠ ☠ Forum Edit Permission Forum Delete Permission ✅ ✅ ✅ ☠ ☠ ✅ ☠ ☠ Upload Profile Image ✅ ☠ New Interesting Algorithm ✅ ☠ High Post Length ✅ ☠ Show Debug Info? ✅ ☠ Enable Audit Log ✅ ☠ Auto Logout ✅ ☠ API Access API v2 ✅ ✅ ✅ ☠ ☠ ✅ ☠ ☠
  41. Flag Descriptions basic-rec-algo: enabled: true description: Use a simplistic recommendation

    algorithm. This is fast and produces less load on backend systems, but is way less accurate than our standard algorithm.
  42. Audit Log Feature State User Date allow_user_edit ✅ Sarah 2018-05-11

    12:22:08 enhanced_rec_algo ✅ Ashley 2018-05-04 15:33:16 allow_user_signup ☠ Ashley 2018-05-04 15:27:33 enhanced_rec_algo ☠ system:load_manager 2018-05-04 15:16:30 allow_user_signup ✅ Bob 2018-05-04 15:12:01 enable_api ✅ Sarah 2018-04-30 09:56:59
  43. Fail Safe Do positively enable features e.g. enable-ibm- analysis, enhanced-recomendation-algo

    Don’t use flags for suppressing new features e.g. disable-shiny-new-feature
  44. Knight Capital 1. Reused flags 2. Didn’t delete obsolete code

    3. Couldn’t be disabled without a deployment
  45. Knight Capital 1. Reused flags 2. Didn’t delete obsolete code

    3. Couldn’t be disabled without a deployment 4. The deploy actually enabled the old code on all 8 servers, instead of disabling it