flippercloud / flipper

🐬 Beautiful, performant feature flags for Ruby.
https://www.flippercloud.io/docs
MIT License
3.66k stars 411 forks source link

Bit by actor delegation #872

Closed bockets closed 2 months ago

bockets commented 2 months ago

Thanks for Flipper, I've literally been using it for a decade.

Recently I encountered an issue that was a bit difficult to figure out.

I had this style of group declaration (not my actual code, I'm not using globals, etc.):

$list = []
Flipper.register(:group) { |actor| $list.include?(actor) }
Flipper.enable_group(:feature, :group)

It took me way too many hours to realize that actor in that Proc is a Flipper::Actor, not the thing I pass when I do a feature check:

user = User.new
$list << [user]
Flipper.enabled?(:feature, user) #=> false, but I expected it to be true

Since Flipper::Actor is a delegator to the actual User object, I have to refactor my design so that it can be picked up by the delegator... i.e. don't get clever with dependency injection:

Flipper.register(:group) { |actor| actor.in?($list) }

class User
  def in?(list)
    list.include?(self)
  end
end

I think this may warrant a brief callout in the docs.

jnunemaker commented 2 months ago

Where are you thinking? Maybe on this page? https://www.flippercloud.io/docs/features/groups

Any thoughts on what it should say? Something like this?

Note: The actor is always a Flipper::Actor instance which delegates to whatever actor you provide when you call Flipper.enabled?.

jnunemaker commented 2 months ago

@bockets also... so great to hear that you've been such a long time user!

If you don't mind, I would love to know what you are using flipper for as well and how it is working out for you (what went well, what was hard other than what you mentioned here).

If you aren't comfortable leaving that information here in a public comment, feel free to email me directly john@fewerandfaster.com. I'm just always curious. :)

jnunemaker commented 2 months ago

I'm going to close this to keep things tidy but I'll update the docs once I hear back from you.

bockets commented 2 months ago

Thanks for the reply @jnunemaker.

Your statement looks good to me. Flipper::Actor is a bit of a mystery guest. I'm sure there's a good reason that the Proc can't just receive the original object?

We use Flipper at BiggerPockets for mostly AB tesing feature releases. In that last 7 months, we've started using it a lot for bigger experimentation. We use it as an alternative to something like split or scientist because we've been using it longer so it's better integrated with our analytics platforms (Segment, Amplitude). We know it well and it does exactly what it says on the tin. Amplitude now also has feature flippers, but it costs money. And Flipper is great, faster, and free (literally good + fast + cheap). We've had a few aborted migrations to FlipperCloud but I've not been a part of those.

I would go into more details about our integration, but it's a bit of a droll to be honest and not something really worthy of sharing. If I was going to do it again from scratch, I'd make sure that I stored the same flipper_session_id both in the Rails session object (for guests) and User object (if there is one) and ensure that the User is given the flipper_session_id upon signup/login. Or maybe an object related to the User to that I didn't add yet another column to the users table.

Basically I'd start with the guest scenario and use that to do the User scenario. But I think most flipper adopters probably do what we did and start with the User scenario, which makes it a bit harder to retrofit for the guest scenario.