Feature toggles can be used in both vets-api and vets-website to manage unreleased features in a continuous integration environment. Feature toggles enable VFS teams to test out new functionality (applications, features, VA.gov content pages, Metalsmith) in the VSP development, staging, or production environments for a set of users. Teams can enable or disable a feature for all users, a percentage of all users, a percentage of all logged-in users, a list of users, or users defined in a method.

Feature toggles:

  • Allow for production toggle switching without redeploying vets-website

  • Provide a user interface for managing feature toggle behavior

  • Provide code helpers for handling common user experience scenarios

  • Are powered by an open-source gem called Flipper gem

See the following sections for information about creating feature toggles:

Writing good feature toggles

Keep the following items in mind as you add feature toggles:

  • Remember that each environment has its own set of feature toggle values.

  • Test your feature toggle in staging before using it in production.

  • Remove feature toggles as soon as they are not needed.

  • Make toggles that are easy to delete by gating a behavior in as few places as possible. It's often better to have blocks of repeating code that can be quickly deleted later than it is to gate several small pieces of code.

After a page is rendered, the feature toggle client retrieves the latest toggle values from the feature toggle service and the page is updated using the latest feature toggle values. The application shows a loading state for the new feature while the toggle values are retrieved from the service.

Adding a new feature toggle (frontend)

Follow these steps to add and use a new feature toggle in vets-website:

  1. Determine your feature toggle name.

    Note: There are no naming conventions yet. Current examples put the application name first, such as facilityLocatorShowCommunityCares and profileShowDirectDeposit.

  2. Add the feature name to vets-api (in snake case) by updating config/features.yml:

    features:
     app_name_then_your_feature_name:
       actor_type: user
       description: >
         This describes what the feature does and
         which team is responsible for the toggle.
    CODE
  3. Determine how you want the feature toggle to be "sticky". For the behavior to be consistent across all devices for a logged in user choose "user" as the actor_type. For the behavior to be consistent for a user for the duration of a cookie within a single browser, regardless of their logged in status choose "cookie_id" as the actor_type.

  4. Run vets-api locally. This can be done on main after your pull request (PR) is merged or off of your feature branch.

  5. Navigate to http://localhost:3000/flipper/features and verify that you see your new feature name. If not, restart your rails server.

  6. Add the feature toggle name to vets-website by updating featureFlagNames.js.

    const FEATURE_FLAG_NAMES = Object.freeze({
     showYourFeatureName: 'app_name_then_your_feature_name',
    })
    CODE

    Note: The key should be camelCase for use in JavaScript, but the value should exactly match the toggle name in features.yml.

  7. Submit a PR for each feature. Crosslinking the PRs in a comment will make it easier for the reviewers to check.

  8. Use the selector helper to build a selector for your feature toggle. See an example here.

    In the following example, the toggleValues object is a flat list of toggleName and boolean key value pairs. Also, note that toggleValues is imported from platform/site-wide/feature-toggles/selectors and FEATURE_FLAG_NAMES is imported from platform/utilities/feature-toggles/featureFlagNames.

    // import the toggleValues helper
    import { toggleValues } from 'platform/site-wide/feature-toggles/selectors';
    
    // use the toggleValues helper to select the toggle values list
    export const appNameThenYourFeatureName = state =>
      toggleValues(state).appNameThenYourFeatureName;
    CODE
  9. Use the feature toggle value to gate your new behavior. For example, you can use the selector above in mapStateToProps to pass the toggle as a prop into your component.

    Note: Currently the feature toggle values are only available on the global redux state.

    function mapStateToProps(state) {
      return {
        showYourFeatureName:
          appNameThenYourFeatureName(state),
      };
    }
    
    ...
    // inside your connected component
    
    render() {
       const { showYourFeatureName } = this.props;
    
       return (
         { showYourFeatureName && <NewFeature /> }
       );
    }
    CODE
  10. Use the Flipper UI to test out the toggle locally. Refresh the page to update the feature toggle state. This value can take up to a minute to update in staging and production.

Adding a new feature toggle (backend)

Follow these steps to add and use a new feature toggle in vets-api:

  1. Determine your feature toggle name.

    Note: There are no naming conventions yet. Current examples put the application name first, such as facilityLocatorShowCommunityCares and profileShowDirectDeposit.

  2. Add the feature name to vets-api (in snake case) by updating config/features.yml:

    features:
       app_name_then_your_feature_name:
       actor_type: user
       description: >
         This describes what the feature does and
         which team is responsible for the toggle.
    CODE
  3. Determine how you want the feature toggle to be "sticky". For the behavior to be consistent across all devices for a logged in user choose "user" as the actor_type. For the behavior to be consistent for a user for the duration of a cookie within a single browser, regardless of their logged in status choose "cookie_id" as the actor_type.

  4. Run vets-api locally. This can be done on master after your PR is merged or off of your feature branch.

  5. Navigate to http://localhost:3000/flipper/features and verify that you see your new feature name. If not, restart your rails server.

Backend example

def base_method_name(params)
  if Flipper.enabled?(:feature_flag, @current_user)
    base_method_name_feature_enabled(params)
  else
    base_method_name_feature_disabled(params)
  end
end
def base_method_name_feature_enabled(params)
  # How it behaves with the feature toggle enabled
end
def base_method_name_feature_disabled(params)
  # How it behaves without the feature toggle enabled
end

### spec
RSpec.describe Object do
  context "Feature feature_flag=true" do
    before do
      Flipper.enable(:feature_flag)
    end
    it "behaves this way with the feature enabled" do
    end
  end
  context "Feature feature_flag=false" do
    before do
      Flipper.disable(:feature_flag)
    end
    it "behaves this way with the feature disabled" do
    end
  end
end
CODE

Enabling and disabling features

You can enable or disable features in the Flipper UI:

  1. Navigate to the Flipper UI at the following URLs:

    1. Dev: http://localhost:3000/flipper/features

    2. Staging: https://staging-api.va.gov/flipper/features

    3. Production: https://api.va.gov/flipper/features

  2. To access the Flipper UI, you must sign in using an identity-verified id.me user that is listed in settings.yml:

    flipper:
      admin_user_emails:
        - email@email.us
        - email1@email.us
    CODE
    1. In the Dev environment, to get around the incompleteness of user data in the sandbox environment, you should sign in with a specific test user - see dev-settings.local.yml for more information. Credentials for this user can be found here.

    2. If you are not on the list, you can add yourself or your teammates to the file.

    3. If you're not sure if your account is identity-verified, you can check by going to this page. If you need to verify your account you'll see a "Verify with ID.me" button.

  3. Once you have logged into the Flipper UI, you can perform the following actions:

    1. Select "Enable for everyone" or "Disable for everyone" to enable or disable the feature for all users.

    2. Use "Percentage of Logged in Users" for a staged rollout (gradual rollout or an a/b test).

      • "Percentage of Logged in Users" will enable the feature for only a percentage of logged in users. It will be applied to the same users each time they return to the site (even when they log out and back in) as long as you don't change the percentage.

      • If the feature toggle's actor_type in config/features.yml is set to "cookie_id" rather than "user" then the feature instead applies to only a percentage of users for the duration of a cookie within a single browser, regardless of their logged in status. This is useful when you need to apply a staged rollout that involves an unauthenticated user experience.

    3. Use "Percentage of Time" to enable a feature for all users for a percentage of time.

    4. Register a "Group" of users to enable a feature for.

    5. You can also roll out a feature for a select few users by adding their email address to the “Users” section. For performance reasons, the list of users is intended to be small — do not use this option for hundreds of users.

The values of each toggle are cached in memory for one minute, so it may take that long to see the effect of enabling or disabling the toggle.

Screen shot of feature toggle application

Creating a toggle and turning it on or off

When running a new vets-website build, the script will fetch all CMS feature toggles from Drupal. This allows the toggles to be controlled from within Drupal.

Before writing any code to use a new feature toggle, it must first be created from within Drupal for all three environments. The feature toggles can be found at https://dev.cms.va.gov/admin/config/system/feature_toggle for dev. Staging and production have similar pages.

The new feature toggle must be in every Drupal environment or vets-website builds will fail when we try to use it! This is intentional so we don't have "accidentally" false feature toggles when Drupal doesn't have a toggle that vets-website is trying to use.

Using toggles in GraphQL queries

Because the toggles are fetched dynamically, they aren't stored in a file that we can require from a GraphQL query file. The build script puts the current toggles are put into global.cmsFeatureFlags after it either fetches the most recent toggles or uses the cache.

Keep in mind the advice in the writing good feature toggles section. You should write the logic in a way that is easy to remove later. It's often easier to change some logic and then add a conditional that modifies something with the toggle is not enabled. That lets you simply remove the conditional later.

Using feature toggles in Liquid templates

All liquid templates have access to the current feature toggle state:

{% if enabledFeatureFlags.feature1 == true %}
  <div>Fancy new feature</div>
{% endif %}
CODE

From within the JavaScript context, the feature toggles are in global.cmsFeatureFlags, but within the Liquid template context, they can be found in enabledFeatureFlags.

Need assistance?

Tag #vfs-platform-support in Slack if you need more information.


Feedback and support