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

Flipper

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for Alex Wheeler Alex Wheeler
November 15, 2017
140

 Flipper

Avatar for Alex Wheeler

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