A common software project best practice is to break features down into small, deployable pieces. The pieces provide value to the customer by themselves, even if they're part of a larger project. That's the ideal. However, a team will encounter times when they can't do that. Not all features slice up so neatly. Sometimes you have a big feature that needs a bunch of moving parts deployed before it all works. You don't want to hold up the CI/CD pipeline until it's ready. And you don't want to postpone integrating your code with the rest of the team. That's where feature flags can help.
Feature flags let you deploy the pieces of a project without impacting existing functionality.
To implement a feature flag, protect your new code with conditionals that check whether the current user has the feature flag enabled. At runtime, your system fetches the flags for the current user from a feature flag API. Then it decides which code path to use depending on which flags are enabled.
In a typical web app stack, doing this on the frontend helps you switch between code paths cleanly. Depending on the flags enabled, the frontend can call different APIs and display different UI.
For example, if you're performing a migration to React on your frontend, then a feature flag could protect it like this:
- Create a feature flag. Let's call it REACT_MIGRATION.
- Once a user logs in, call your feature flag API to get back the features the user has enabled.
- If the user has REACT_MIGRATION enabled, redirect then to the React app.
- If the user does not have REACT_MIGRATION enabled, redirect them to the original app.
Now you can seamlessly deploy your React work-in-progress. No need to hold up the deployment. This keeps the whole team moving forward and deploying frequently.
You can enable feature flags for specific users or groups for testing purposes. So you could enable REACT_MIGRATION for yourself to test out the migration in Staging or Production environments.
Once you've finished the React code and tested it out, then you can switch the feature flag on for everyone. Migration complete.
AWS CloudWatch Evidently as a feature flag service
I use CloudWatch Evidently as a feature flag service. Here's how to use it:
- Open CloudWatch Evidently in the AWS console and create a Project.
- In the new project, add features.
I add a feature for every feature flag + deployed environment combination. This allows me to toggle the feature per environment. For example, if I have Staging and Production environments and a React migration feature flag, then I'll create REACT_MIGRATION_STAGING and REACT_MIGRATION_PRODUCTION features in Evidently. Then I can toggle it on in Staging for testing purposes while leaving it off in Production.
For the feature variations, create boolean on/off variations:
3. In your API, create an endpoint that calls Evidently to fetch feature flags at runtime for a given user. Let's call this endpoint EvaluateFeaturesForUser.
EvaluateFeaturesForUser should accept an array of feature names and call the Evidently BatchEvaluateFeature API. The BatchEvaluateFeature API takes the feature name and an identifier for the current user (the "entityId"). I use the user's database ID or email for the entityId. This way I can enable/disable features for specific users in the Evidently console.
EvaluateFeaturesForUser should add a suffix to the feature name that it sends to Evidently, so that it evaluates the flag for the environment it's currently deployed to. For example, if EvaluateFeaturesForUser is deployed to Staging, then it should evaluate REACT_MIGRATION_STAGING. If it's deployed to Production, it should evaluate REACT_MIGRATION_PRODUCTION.
4. In your frontend code, hardcode feature flag names. Call EvaluateFeaturesForUser with an array of those names. That way, your frontend can tell which features are enabled for the user. Then it can use conditionals to show different UI, call different APIs, and whatever else you need to do.
5. When you need to toggle on/off features, go to the Evidently console and edit the feature. Change the default feature variation to Enabled or Disabled as desired (see above screenshot.) Or add overrides for specific users. Once you edit the feature, the changes take effect immediately.
When you need to add a new feature flag, the process goes like this:
- Decide on a feature flag name. E.g. REACT_MIGRATION.
- Create features in Evidently to represent the combinations of that flag and environments: REACT_MIGRATION_STAGING, REACT_MIGRATION_PRODUCTION.
- In the frontend code, hardcode the new feature flag name. E.g.
export const reactMigrationFeatureFlag = "REACT_MIGRATION"
- Include the new flag name in the call to EvaluateFeaturesForUser. Typically I call the API one time with all available feature names and cache the results in memory.
- Use the flag in your frontend.
Evidently can do a bunch of other things, but this is a good starting point for feature flags.