Dr Sheldon Cooper Presents: Fun with Flags (NEPHP)

Bbf9decfbfc2ab5b450ec503749ded28?s=47 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.

Bbf9decfbfc2ab5b450ec503749ded28?s=128

Michael Heap

September 20, 2018
Tweet

Transcript

  1. None
  2. Michael Heap

  3. Don’t leave, pleeeaaaasseee

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

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

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

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

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

  14. Michael @mheap Developer Relations NexmoDev

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

  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. Separate 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 NEW_ALGORITHM NEW_ALGORITHM(input) #else ALGORITHM(input) #endif $ make build -DNEW_ALGORITHM

  33. ./make_call.sh --use-new-algorithm ./make_call.sh --use-original-algorithm

  34. if (in_array($user->id, $whitelistedUsers)) { return $service->useNewAlgorithm($input); } return $service->useAlgorithm($input);

  35. I love a good history lesson

  36. Use Cases?

  37. 1. Permissions

  38. 1. Permissions 2. Early-access

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

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

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

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

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

    Block users 6. Calendar driven 7. Kill switch
  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
  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
  46. 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
  47. None
  48. Flag Types

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

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

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

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

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

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

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

    ops Permission toggles Experiment toggles owned by product
  56. None
  57. Flag Granularity

  58. On/Off Switch Enable or disable entirely

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

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

    Toggle for a percentage of users Toggle for specific users
  61. 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
  62. 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
  63. None
  64. Implementation

  65. Build vs Buy

  66. 1. Dashboard

  67. 1. Dashboard 2. Access control

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

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

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

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

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

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

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

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

  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_UNANIMOUS); $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_AFFIRMATIVE); $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_MAJORITY); $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_AFFIRMATIVE); $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. 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 :(';
  86. $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
  87. 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);
  88. 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);
  89. Configuration: Redis $predis = new Predis\Client(); $collection = new PredisCollection('toggle_demo',

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

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

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

    rules
  94. Buy

  95. None
  96. Multi-Language

  97. Prebuilt Dashboard

  98. Environment Aware

  99. Scaleable

  100. Audit Log

  101. A/B Testing

  102. ld-relay

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

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

    modify the product 3. Dependent on 3rd party
  105. Build vs Buy

  106. So. Much. Choice

  107. It depends

  108. Show me code

  109. <?php use Qandidate\Bundle\ToggleBundle\Annotations\Toggle; /** * @Toggle("profile-management") */ class ProfileController {

    public function updatePassword() { } public function updateEmail() { } }
  110. <?php use Qandidate\Bundle\ToggleBundle\Annotations\Toggle; class ProfileController { /** * @Toggle("profile-edit-password") */

    public function updatePassword() { } public function updateEmail() { } }
  111. 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
  112. {% if feature_is_active('new-profile-image-picker') %} {# Show new picker #} {%

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

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

    different UI elements Branch by abstraction for refactoring
  115. None
  116. Toggle Configuratin

  117. Hard Coded Change the code Dynamic Static

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

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

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

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

  123. Flag State

  124. Role Based Access

  125. Audit Log

  126. Non-technical Users

  127. Technical Debt

  128. DON’T

  129. Cleanup based on activity

  130. Cleanup based on activity

  131. Create & Cleanup

  132. WIP limits

  133. Expiration date

  134. Explosion date

  135. Tips & Tricks

  136. Testing Flags

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

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

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

    ✅ ✅ ✅ ✅ ✅ ✅ ✅ ☠ ✅ ✅ ☠ ✅ ✅ ☠ ✅ ✅ ☠ ✅ ✅ ✅ ✅ ✅ ☠ ☠ ✅ ☠ ✅ ☠ ✅ ☠ ☠ ✅ ☠ ✅ ✅ ☠ ☠ ✅ ☠ ✅ ☠ ☠ ✅ ✅ ✅ ☠ ☠ ☠ ☠ ✅ ☠ ☠ ☠ ☠ ✅ ☠ ☠ ☠ ☠ ✅ ☠ ☠ ☠ ☠
  140. 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
  141. 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 ✅ ✅ ✅ ☠ ☠ ✅ ☠ ☠
  142. Expose State { "api_v2": true, "audit_log": true, "new_recommendation_algo": false }

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

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

  146. 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
  147. 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
  148. Conclusion

  149. Woooooooo

  150. Flags are great!

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

  152. Build vs Buy

  153. Qandidate Toggle

  154. None
  155. One More Thing

  156. No Reuse

  157. Knight Capital

  158. High frequency trader

  159. None
  160. None
  161. Mistakes were made

  162. Knight Capital 1. Reused flags

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

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

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

  167. @mheap / @NexmoDev m@michaelheap.com