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

Flipper

Alex Wheeler
November 15, 2017
110

 Flipper

Alex Wheeler

November 15, 2017
Tweet

Transcript

  1. • feature flipping Ruby gem • feature flipping: enabling or

    disabling features or parts of your application, ideally without re- deploying or changing anything in your code base
  2. • Key concepts - features, gates, adapters • How Flipper

    works • Flipper REST Api / HTTP client • Best Practices
  3. use cases • Turn on a feature for: • all

    users • group of users • specific user • % of users • % of time
  4. Actors • anything that responds to flipper_id class User def

    flipper_id email end end class ClientCompany def flipper_id "client_company:#{id}" end end
  5. Tables create_table "flipper_features", force: true do |t| t.string "key", null:

    false end create_table "flipper_gates", force: true do |t| t.string "feature_key", null: false t.string "key", null: false t.string "value" end Flipper::Adapters::ActiveRecord::Feature Flipper::Adapters::ActiveRecord::Gate
  6. INSERT INTO flipper_gates (feature_key, key, value) VALUES ('side_pane', 'actors', '[email protected]')

    INSERT INTO flipper_gates (feature_key, key, value) VALUES ('side_pane', 'actors', '[email protected]') INSERT INTO flipper_gates (feature_key, key, value) VALUES ('side_pane', 'actors', '[email protected]') INSERT INTO flipper_gates (feature_key, key, value) VALUES ('side_pane', 'actors', '[email protected]') INSERT INTO flipper_gates (feature_key, key, value) VALUES ('side_pane', 'actors', ‘[email protected]')
  7. • “Whatever your data store, throughput, or experience, feature flipping

    should be easy and have minimal impact on your application”
  8. Flipper Adapters • ActiveRecord • Mongo • Redis • Cassandra

    • Memcached • Sequel • PStore • Memory
  9. Adapter Interface • features - Get the set of known

    features. • add(feature) - Add a feature to the set of known features. • remove(feature) - Remove a feature from the set of known features. • clear(feature) - Clear all gate values for a feature. • get(feature) - Get all gate values for a feature. • enable(feature, gate, thing) - Enable a gate for a thing. • disable(feature, gate, thing) - Disable a gate for a thing. • get_multi(features) - Get all gate values for several features at once.
  10. $flipper = Flipper.new(adapter) # Mongo mongo_client = Mongo::Client.new(["vts.com:1234"], server_selection_timeout: 1,

    database: 'viewthespace') adapter = Flipper::Adapters::Mongo.new(mongo_client) # Redis redis_client = Redis.new(url: 'vts.com:1234') adapter = Flipper::Adapters::Redis.new(redis_client) # PStore dir = FlipperRoot.join('tmp').tap(&:mkpath) pstore_file = dir.join('flipper.pstore') adapter = Flipper::Adapters::PStore.new(pstore_file)
  11. side_pane = $flipper[:side_pane] class Flipper alias_method :[], :feature def feature(name)

    Feature.new(name, @adapter) end end side_pane = $flipper.feature(:side_pane)
  12. # ActiveRecord def add(feature) unless @feature_class.where(key: feature.key).first @feature_class.create! { |f|

    f.key = feature.key } end true end # Mongo def add(feature) update FeaturesKey, '$addToSet' => { 'features' => feature.key } true end # Redis def add(feature) @client.sadd FeaturesKey, feature.key true end
  13. def feature(name) if !name.is_a?(String) && !name.is_a?(Symbol) raise ArgumentError, "#{name} must

    be a String or Symbol" end @memoized_features[name.to_sym] ||= Feature.new(name, @adapter, instrumenter: instrumenter) end def enable(thing = true) instrument(:enable) do |payload| adapter.add self gate = gate_for(thing) wrapped_thing = gate.wrap(thing) payload[:gate_name] = gate.name payload[:thing] = wrapped_thing adapter.enable self, gate, wrapped_thing end end
  14. HTTP Adapter options = { uri: URI('http://app.com/mount-point'), headers: { 'X-Custom-Header'

    => 'foo' }, basic_auth_username: 'username', basic_auth_password: 'password', read_timeout: 100, open_timeout: 40, } adapter = Flipper::Adapters::Http.new(options) $flipper = Flipper.new(adapter)
  15. Best Practices • turning on a feature for > 20

    users? • use a group gate! Flipper.register(:admins) do |actor| actor.respond_to?(:admin?) && actor.admin? end $flipper[:side_pane].enable_group(:admins)
  16. • Turning on a feature for every user? (boolean gate)

    • Delete all other gates first! $flipper[:side_pane].disable; $flipper[:side_pane].enable
  17. Flipper.register(:admins) do |actor| enabled_users = User.where(attr: true) enabled_users.include?(actor) end Flipper.register(:admins)

    do |actor| enabled_users = User.where(attr: true) enabled_users.include?(actor.thing) end