Feature Flags
At Pair Team, we use Flipper to manage feature flags. Flipper is a feature flagging library for Ruby on Rails applications that provides a simple and flexible way to control feature availability. It provides an API to control feature flags programmatically and a web dashboard for end users.
Implementing Feature Flags in a Project
Implementing a new feature flag is a product team decision:
Definition | Implementation | Administration |
---|---|---|
- PM and Eng decide when a new implementation (task, milestone, project) will be rolled out behind a feature flag - Eng defines the feature flag name. E.g. care_pods, ecm_dashboard | - Eng adds the defined feature flag to the backend - Eng implements the feature flag logic | - PM will enable or disable feature flags from the Admin UI using their ARC credentials: DEV, PROD - Users can be added using their ARC user email - A group named product exists to easily add all product + eng members |
Adding or Removing Feature Flags
New feature flags are added and removed in config/feature_flags.yml by providing a name, description, and tags.
cca_generate_csv:
description: Molina CSV generation
tags: ['Payers']
elation_sync:
description: Elation sync
tags: ['Integrations']
Check the Flipper initializer to know more about the sync process.
Enabling a feature flag
Feature flags can be enabled in multiple ways:
- All users
- A group of users
- For a specific Care Pod (all users and patients in the Care Pod)
- Other options
Using one of the following methods:
- Programatically: (Flipper ActiveRecord docs).
- From Flipper dashboard page:
Environment | URL |
---|---|
Locally | http://localhost:3000/flipper/features |
In DEV | https://api.preview.pairteam.dev/flipper |
In PROD | https://api.pairteam.com/flipper |
Check the following PR to know more about Flipper UI customizations to support tags.
Using feature flags in code
Frontend
- React component to conditionally show content when a feature is enabled
import { Feature as FeatureName } from 'generated/graphql';
// ...
<Feature name={FeatureName.EcmCarePods}>
<p>ecm_care_pods feature enabled</p>
</Feature>;
- Or React component to conditionally hide content when a feature is enabled
import { Feature as FeatureName } from 'generated/graphql';
// ...
<FeatureDisabled name={FeatureName.EcmCarePods}>
<p>ecm_care_pods feature disabled</p>
</FeatureDisabled>;
- React hooks for programatic access
import { useFeatures } from 'hooks/useFeatures';
import { Feature as FeatureName } from 'generated/graphql';
const [hasFeature, features] = useFeatures();
// Check if the user has a feature flag enabled
if (hasFeature(FeatureName.EcmCarePods)) {
// the user has ecm_care_pods feature
}
// Display all the features for a user
console.log("User features: ${features.join(', ')}");
Backend
Check if a feature flag is enabled for the current user
Checks whether a feature flag is enabled for the current user or the Care Pod they belong to. If the feature flag is globally enabled, this method will return true
Arc::Feature.enabled_for_user?(:ecm_care_pods)
# equivalent to
Arc::Feature.enabled_for_user?(:ecm_care_pods, Current.user)
If Current.user
is set to the Automation Bot, the check will return true only if the feature flag is globally enabled (i.e., enabled for all actors).
Check if a feature flag is enabled globally
Useful for feature flags that are not tied to a user, such as those used in background jobs.
Arc::Feature.enabled_globally?(:ecm_care_pods)
Check if a feature flag is enabled for a patient
Checks whether a feature flag is enabled for a specific patient or the Care Pod the patient belongs to.
Arc::Feature.enabled_for_patient?(:ecm_care_pods, chart)
Advanced: Check if a feature flag is enabled for the current user and a specific patient
Flipper.enabled?('ecm_care_pods', [current_user, some_chart])
Any model can support feature flags as long as they are a Flipper actor
Although this is supported by Flipper, RuboCop is configured to flag this as an offense.
If you find yourself needing this pattern, consider extending the Arc::Feature
class to support your use case explicitly.
Guidelines
- Keep Flags Short-Lived: Regularly review and remove feature flags that are no longer needed to avoid clutter and reduce technical debt. Include a cleanup plan for feature flags in the project roadmap.
- Test Thoroughly: Ensure that both the enabled and disabled states of a feature flag are thoroughly tested to prevent unexpected issues.
Feature Flags types
- Product features: feature flags for new features that are being rolled out. They should be short-lived and removed once the feature is fully rolled out.
- Jobs: feature flags for background jobs that can be enabled or disabled anytime. Can be long-lived.
Naming conventions
- Naming style: use
snake_case
for feature flag names to follow Ruby conventions. e.g.ecm_care_pods
- Short and descriptive: the name should be easy to understand and remember.
- Prefixes: consider using a prefix to group related feature flags. e.g.
ecm_
for all features related to the ECM module.
Avoid using the word "enable" or "feature" in the name or description, as it's redundant.
❌ enable_ecm_care_pods_feature
✅ ecm_care_pods
Maintainability
Feature flags should be constructed so that a minimal amount of maintenance is required to add or remove the code associated with the flag.
Granularity
- Small: Feature flags for components or a group of components should be inline with the page.
- Medium/Big: Feature flags that encompass and entire page or most of a page should happen at the navigation level such as a menu or if not possible at the route.
- Duplicate a file or folder to create a new feature when needed to isolate old code.
- Rename the old feature file/folder using a prefix e.g
LCMDashboardOld
. Once the feature flag is removed, all the previous code can be removed without affecting the new code.
Back End considerations
- Ideally a back end feature will be backwards compatible. e.g. A feature that only contains additional attributes and does not create new behavior.
- Features that require data migrations might be tricky to implement with a feature flag. There will be some situations when both data structures will need to be supported.