Skip to main content

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:

DefinitionImplementationAdministration
- 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.

config/feature_flags.yml
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:

  1. Programatically: (Flipper ActiveRecord docs).
  2. From Flipper dashboard page:
EnvironmentURL
Locallyhttp://localhost:3000/flipper/features
In DEVhttps://api.preview.pairteam.dev/flipper
In PRODhttps://api.pairteam.com/flipper

Check the following PR to know more about Flipper UI customizations to support tags.

Using feature flags in code

Frontend

  1. 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>;
  1. 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>;
  1. 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)
info

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])
info

Any model can support feature flags as long as they are a Flipper actor

warning

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

  1. 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.
  2. Test Thoroughly: Ensure that both the enabled and disabled states of a feature flag are thoroughly tested to prevent unexpected issues.

Feature Flags types

  1. 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.
  2. 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

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.
Things to consider (frontend / backend)
  • 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.

Additional material