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 26, 2018
Tweet

Transcript

  1. None
  2. Michael Heap

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

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

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

  13. Fun with Country Flags! Presented by @mheap at #phpsrb

  14. Fun with Feature Flags! Presented by @mheap at #phpsrb

  15. Michael @mheap Developer Relations NexmoDev

  16. Fun with Feature Flags! Presented by @mheap at #phpsrb

  17. Today’s Plan

  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. 1. What are feature flags? 2. Implementation 3. Flag management

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

  24. Feature Toggles?

  25. Feature Flip?

  26. Feature Gate?

  27. Feature Switches?

  28. Separate feature release from code deployment

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

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

  33. #ifdef NEW_ALGORITHM NEW_ALGORITHM(input) #else ALGORITHM(input) #endif $ make build -DNEW_ALGORITHM

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

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

  36. None
  37. Use Cases?

  38. 1. Permissions

  39. 1. Permissions 2. Early-access

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

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

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

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

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

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

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

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

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

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

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

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

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

    ops Permission toggles Experiment toggles owned by product
  57. None
  58. Flag Scopes

  59. On/Off Switch Enable or disable entirely

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

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

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

  66. Build vs Buy

  67. 1. Dashboard

  68. 1. Dashboard 2. Access control

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

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

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

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

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

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

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

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

  77. 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)
  78. 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);
  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_UNANIMOUS); $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_UNANIMOUS); $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_AFFIRMATIVE); $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_MAJORITY); $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_AFFIRMATIVE); $manager->add($toggle); $context = new Context(); $context->set('time', (int) date('G')); echo 'Everyone is ' . ($manager->active('disable-work-email', $context)) ? ‘happy!' : 'sad :(';
  86. 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 :(';
  87. 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 :(';
  88. 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 :(';
  89. $manager = new ToggleManager(new InMemoryCollection()); $toggle = new Toggle('some-feature', [

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

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

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

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

    rules
  97. Buy

  98. None
  99. Multi-Language

  100. Prebuilt Dashboard

  101. Environment Aware

  102. Scaleable

  103. Audit Log

  104. A/B Testing

  105. ld-relay

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

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

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

  109. None
  110. It depends

  111. Show me code

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

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

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

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

    ($this->toggleManager->active('new-profile', $context)) { return new ShinyNewProfile($args); } return new Profile($args); } }
  117. Controller View Factory Enable a route for specific users Show

    different UI elements Branch by abstraction for refactoring
  118. None
  119. Toggle Configuratin

  120. Hard Coded Change the code Dynamic Static

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

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

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

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

  126. Flag State

  127. Role Based Access

  128. Audit Log

  129. Non-technical Users

  130. Technical Debt

  131. DON’T

  132. Cleanup based on activity

  133. Cleanup based on activity

  134. Create & Cleanup

  135. WIP limits

  136. WIP limits

  137. Expiration date

  138. Explosion date

  139. Tips & Tricks

  140. Testing Flags

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

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

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

    ✅ ✅ ✅ ✅ ✅ ✅ ✅ ☠ ✅ ✅ ☠ ✅ ✅ ☠ ✅ ✅ ☠ ✅ ✅ ✅ ✅ ✅ ☠ ☠ ✅ ☠ ✅ ☠ ✅ ☠ ☠ ✅ ☠ ✅ ✅ ☠ ☠ ✅ ☠ ✅ ☠ ☠ ✅ ✅ ✅ ☠ ☠ ☠ ☠ ✅ ☠ ☠ ☠ ☠ ✅ ☠ ☠ ☠ ☠ ✅ ☠ ☠ ☠ ☠
  144. 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
  145. 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 ✅ ✅ ✅ ☠ ☠ ✅ ☠ ☠
  146. Expose State { "api_v2": true, "audit_log": true, "new_recommendation_algo": false }

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

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

  150. 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
  151. 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
  152. Conclusion

  153. None
  154. Flags are great!

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

  156. Build vs Buy

  157. Qandidate Toggle

  158. None
  159. One More Thing

  160. No Reuse

  161. Knight Capital

  162. High frequency trader

  163. None
  164. None
  165. Mistakes were made

  166. Knight Capital 1. Reused flags

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

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

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

  171. @mheap / @NexmoDev m@michaelheap.com https://joind.in/talk/583e8