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

Dr Sheldon Cooper Presents: Fun with Flags

Dr Sheldon Cooper Presents: Fun with Flags

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

May 11, 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. Rollout (Ruby) # Is a feature active? $rollout.active?(:chat) # Activate

    a specific user $rollout.activate_user(:chat, @user) # Define a group $rollout.define_group(:admins) do |user| user.admin? end # Roll out to a percentage of users $rollout.activate_percentage(:chat, 20)
  14. Rollout (PHP) # Is a feature active? $rollout->isActive('chat'); # Activate

    a specific user $rollout->activateUser('chat', $user); # Define a group $rollout->defineGroup('admins', function($user = null) { return $user->isAdmin(); }); # Roll out to a percentage of users $rollout->activatePercentage('chat', 20);
  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_UNANIMOUS); $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_UNANIMOUS); $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_UNANIMOUS); $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_AFFIRMATIVE); $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_MAJORITY); $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_AFFIRMATIVE); $manager->add($toggle); $context = new Context(); $context->set('time', (int) date('G')); echo 'Everyone is ' . ($manager->active('disable-work-email', $context)) ? ‘happy!' : 'sad :(';
  23. 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 :(';
  24. 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 :(';
  25. 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 :(';
  26. $manager = new ToggleManager(new InMemoryCollection()); $toggle = new Toggle('some-feature', [

    new OperatorCondition('user_id', new GreaterThan(41)) ]); $manager->add($toggle); Configuration: Code
  27. 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);
  28. 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);
  29. 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
  30. Buy

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

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

    product 3. Dependent on 3rd party
  33. Hard Coded Deploy Config Parameterised Config Change the code Deploy

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

    code Deploy your application Edit config, restart app Edit via UI Dynamic Static
  35. 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
  36. Testing Flags Feature A Feature B Feature C ✅ ✅

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

    ✅ ✅ ✅ ✅ ✅ ✅ ✅ ☠ ✅ ✅ ☠ ✅ ✅ ☠ ✅ ✅ ☠ ✅ ✅ ✅ ✅ ✅ ☠ ☠ ✅ ☠ ✅ ☠ ✅ ☠ ☠ ✅ ☠ ✅ ✅ ☠ ☠ ✅ ☠ ✅ ☠ ☠ ✅ ✅ ✅ ☠ ☠ ☠ ☠ ✅ ☠ ☠ ☠ ☠ ✅ ☠ ☠ ☠ ☠ ✅ ☠ ☠ ☠ ☠
  38. 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
  39. 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 ✅ ✅ ✅ ☠ ☠ ✅ ☠ ☠
  40. 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.
  41. 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
  42. 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
  43. Knight Capital 1. Reused flags 2. Didn’t delete obsolete code

    3. Couldn’t be disabled without a deployment
  44. 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