See featury.servactory.com for comprehensive guides and API documentation.
Complete documentation is also available in the docs directory:
- Getting Started
- Features
- Groups
- Actions
- Resources
- Conditions
- Working with Features
- Info and Introspection
- Integration
- Examples
- Best Practices
- 🎯 Unified Feature Management — Group and manage multiple feature flags through a single interface with automatic prefix handling
- 🔌 Flexible Integration — Works with any backend: Flipper, Redis, databases, HTTP APIs, or custom solutions
- 🗂️ Powerful Organization — Organize features with prefixes, groups, and nested hierarchies for scalable feature management
- 🔍 Rich Introspection — Full visibility into features, actions, and resources through the comprehensive info API
- 🪝 Lifecycle Hooks — Before/after callbacks for actions with customizable scope and full context access
- 🛡️ Type-Safe Resources — Built on Servactory for robust resource validation, type checking, and automatic coercion
Add Featury to your Gemfile:
gem"featury"Create a base class that defines how features interact with your feature flag system:
classApplicationFeature < Featury::Baseaction:enabled?,web: :enabled?do |features:, **options|
features.all?{ |feature| Flipper.enabled?(feature, *options.values)}endaction:disabled?,web: :regulardo |features:, **options|
features.any?{ |feature| !Flipper.enabled?(feature, *options.values)}endaction:enable,web: :enabledo |features:, **options|
features.all?{ |feature| Flipper.enable(feature, *options.values)}endaction:disable,web: :disabledo |features:, **options|
features.all?{ |feature| Flipper.disable(feature, *options.values)}endaction:add,web: :regulardo |features:, **options|
features.all?{ |feature| Flipper.add(feature, *options.values)}endbeforedo |action:,features:|
Slack::API::Notify.call!(action:,features:)endafter:enabled?,:disabled?do |action:,features:|
Slack::API::Notify.call!(action:,features:)endendDefine features with prefixes, resources, conditions, and groups:
classUser::OnboardingFeature < ApplicationFeatureprefix:user_onboardingresource:user,type: Usercondition->(resources:){resources.user.onboarding_awaiting?}feature:passage,description: "User onboarding passage feature"# => :user_onboarding_passagegroupBillingFeature,description: "Billing functionality group"groupPaymentSystemFeature,description: "Payment system functionality group"endclassBillingFeature < ApplicationFeatureprefix:billingfeature:api,description: "Billing API feature"# => :billing_apifeature:webhooks,description: "Billing webhooks feature"# => :billing_webhooksendclassPaymentSystemFeature < ApplicationFeatureprefix:payment_systemfeature:api,description: "Payment system API feature"# => :payment_system_apifeature:webhooks,description: "Payment system webhooks feature"# => :payment_system_webhooksend# Direct method callsUser::OnboardingFeature.enabled?(user:)# => trueUser::OnboardingFeature.enable(user:)# => trueUser::OnboardingFeature.disable(user:)# => true# Using .with() methodfeature=User::OnboardingFeature.with(user:)feature.enabled?# => truefeature.enable# => truefeature.disable# => trueThis project is intended to be a safe, welcoming space for collaboration. Contributors are expected to adhere to the Contributor Covenant code of conduct. We recommend reading the contributing guide as well.
Featury is built and maintained by amazing contributors.
Featury is available as open source under the terms of the MIT License.