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.

Bbf9decfbfc2ab5b450ec503749ded28?s=128

Michael Heap

May 11, 2018
Tweet

Transcript

  1. None
  2. Michael Heap

  3. None
  4. None
  5. None
  6. None
  7. None
  8. None
  9. Fun with Country Flags! Presented by @mheap at @phpday

  10. Fun with Country Flags! Presented by @mheap at @phpday

  11. Fun with Country Flags! Presented by @mheap at @phpday

  12. Fun with Country Flags! Presented by @mheap at @phpday

  13. Fun with Feature Flags! Presented by @mheap at @phpday

  14. Michael @mheap Developer Relations NexmoDev

  15. Fun with Feature Flags! Presented by @mheap at @phpday

  16. Today’s Plan

  17. 1. What are feature flags? 2. Implementation 3. Flag management

    4. Tips and tricks 5. Conclusion
  18. 1. What are feature flags? 2. Implementation 3. Flag management

    4. Tips and tricks 5. Conclusion
  19. 1. What are feature flags? 2. Implementation 3. Flag management

    4. Tips and tricks 5. Conclusion
  20. 1. What are feature flags? 2. Implementation 3. Flag management

    4. Tips and tricks 5. Conclusion
  21. 1. What are feature flags? 2. Implementation 3. Flag management

    4. Tips and tricks 5. Conclusion
  22. Feature Flags?

  23. Feature Toggles?

  24. Feature Flip?

  25. Feature Gate?

  26. Feature Switches?

  27. Separates feature release from code deployment

  28. http://code.flickr.net/2009/12/02/flipping-out/

  29. None
  30. None
  31. Feature flags aren’t new

  32. #ifdef IBM_ANALYSIS IBM_ANALYSIS(audio) #else INTERNAL_ANALYSIS(audio) #endif $ make build -DIBM_ANALYSIS

  33. ./make_call.sh —-use-internal-analysis ./make_call.sh —-use-ibm-analysis

  34. if (in_array($user->id, $whitelistedUsers)) { return $ibm->analyzeAudio($audio); } return $sentiment->analyze($audio);

  35. Use Cases?

  36. 1. Permissions

  37. 1. Permissions 2. Early-access

  38. 1. Permissions 2. Early-access 3. Opt-in

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

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

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

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

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

    Block users 6. Calendar driven 7. Kill switch 8. Power user
  44. 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
  45. 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
  46. Flag Classes

  47. Ops Toggles Release Toggles Flag Classes Permission Toggles Experiment Toggles

  48. Release Toggles Ops Toggles Flag Classes Permission Toggles Experiment Toggles

    Lifetime Days Dynamism Per deployment
  49. Release Toggles Ops Toggles Flag Classes Permission Toggles Experiment Toggles

    Lifetime Days - Weeks Dynamism Human Controlled
  50. Release Toggles Ops Toggles Flag Classes Permission Toggles Experiment Toggles

    Lifetime Years Dynamism Per Request
  51. Release Toggles Ops Toggles Flag Classes Permission Toggles Experiment Toggles

    Lifetime Weeks - Months Dynamism Per Request
  52. https://martinfowler.com/articles/feature-toggles.html

  53. Ownership Release toggles owned by devs Ops toggles owned by

    ops Permission toggles Experiment toggles owned by product
  54. Flag Scopes

  55. On/Off Switch Enable or disable entirely

  56. On/Off Switch Percentage of users Enable or disable entirely Toggle

    for a percentage of users
  57. On/Off Switch Percentage of users Individuals Enable or disable entirely

    Toggle for a percentage of users Toggle for specific users
  58. 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
  59. 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
  60. Implementation

  61. Build vs Buy

  62. 1. Dashboard

  63. 1. Dashboard 2. Access control

  64. 1. Dashboard 2. Access control 3. Complex rules

  65. 1. Dashboard 2. Access control 3. Complex rules 4. Cascading

    flags
  66. 1. Dashboard 2. Access control 3. Complex rules 4. Cascading

    flags 5. Multivariate rules
  67. 1. Dashboard 2. Access control 3. Complex rules 4. Cascading

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

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

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

    flags 5. Multivariate rules 6. Auditing 7. Speed 8. Scale 9. 100% uptime
  71. Build

  72. 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)
  73. 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);
  74. 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 :(';
  75. 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 :(';
  76. 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 :(';
  77. 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 :(';
  78. 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 :(';
  79. 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 :(';
  80. 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 :(';
  81. 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 :(';
  82. 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 :(';
  83. 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 :(';
  84. 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 :(';
  85. $manager = new ToggleManager(new InMemoryCollection()); $toggle = new Toggle('some-feature', [

    new OperatorCondition('user_id', new GreaterThan(41)) ]); $manager->add($toggle); Configuration: Code
  86. 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);
  87. 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);
  88. Configuration: Redis $predis = new Predis\Client(); $collection = new PredisCollection('toggle_demo',

    $predis); $manager = new ToggleManager($collection);
  89. 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
  90. Qandidate Toggle UI https://github.com/qandidate-labs/qandidate-toggle-ui

  91. Build (Pros) 1. Free! 2. Total control 3. Complex rules

    4. API and UI available
  92. Build (Cons) 1. Self hosted 2. PHP only 3. Complex

    rules
  93. Buy

  94. None
  95. Multi-Language

  96. Prebuilt Dashboard

  97. Environment Aware

  98. Scaleable

  99. Audit Log

  100. A/B Testing

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

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

    product 3. Dependent on 3rd party
  103. Build vs Buy

  104. It depends

  105. Flag Management

  106. Toggle Configuratin

  107. Hard Coded Change the code Dynamic Static

  108. Hard Coded Deploy Config Change the code Deploy your application

    Dynamic Static
  109. Hard Coded Deploy Config Parameterised Config Change the code Deploy

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

    code Deploy your application Edit config, restart app Edit via UI Dynamic Static
  111. 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
  112. Feature Dashboard

  113. Flag State

  114. Role Based Access

  115. Audit Log

  116. Non-technical Users

  117. Technical Debt

  118. Cleanup based on activity

  119. Cleanup based on activity

  120. Create & Cleanup

  121. WIP limits

  122. WIP limits

  123. Expiration date

  124. Explosion date

  125. Tips & Tricks

  126. Testing Flags

  127. Testing Flags Feature A Feature B ✅ ✅ ✅ ☠

    ☠ ✅ ☠ ☠
  128. Testing Flags Feature A Feature B Feature C ✅ ✅

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

    ✅ ✅ ✅ ✅ ✅ ✅ ✅ ☠ ✅ ✅ ☠ ✅ ✅ ☠ ✅ ✅ ☠ ✅ ✅ ✅ ✅ ✅ ☠ ☠ ✅ ☠ ✅ ☠ ✅ ☠ ☠ ✅ ☠ ✅ ✅ ☠ ☠ ✅ ☠ ✅ ☠ ☠ ✅ ✅ ✅ ☠ ☠ ☠ ☠ ✅ ☠ ☠ ☠ ☠ ✅ ☠ ☠ ☠ ☠ ✅ ☠ ☠ ☠ ☠
  130. 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
  131. 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 ✅ ✅ ✅ ☠ ☠ ✅ ☠ ☠
  132. Expose State { "api_v2": true, "audit_log": true, "new_recommendation_algo": false }

    GET /toggle_status
  133. Flag Descriptions basic-rec-algo: true

  134. 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.
  135. Clear Naming allow_user_delete and allow_user_edit not user_control

  136. 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
  137. 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
  138. Conclusion

  139. Flags are great!

  140. Ops Toggles Release Toggles Flag Classes Permission Toggles Experiment Toggles

  141. Build vs Buy

  142. Qandidate Toggle

  143. None
  144. One More Thing

  145. No Reuse

  146. Knight Capital

  147. High frequency trader

  148. Mistakes were made

  149. Knight Capital 1. Reused flags

  150. Knight Capital 1. Reused flags 2. Didn’t delete obsolete code

  151. Knight Capital 1. Reused flags 2. Didn’t delete obsolete code

    3. Couldn’t be disabled without a deployment
  152. 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
  153. $460m Gone

  154. @mheap / @NexmoDev m@michaelheap.com https://joind.in/talk/298e7