Feature Flags
Feature flags are declared in Sentry's codebase (look for SENTRY_FEATURES
in
server.py
).
For self-hosted users, those flags are then configured via sentry.conf.py
.
For Sentry's SaaS deployment, options automator is used to configure flags in production.
You can find a list of features available by looking at two files:
sentry/features/permanent.py
- Features typically managed by subscriptions for sentry.iosentry/features/temporary.py
- Features used during development intended to be removed
They're declared on the FeatureManager
like so:
# pass FeatureHandlerStrategy.OPTIONS to use options automator:
manager.add("organizations:onboarding", OrganizationFeature, FeatureHandlerStrategy.OPTIONS)
# pass FeatureHandlerStrategy.INTERNAL if you don't plan to use options automator:
manager.add("organizations:onboarding", OrganizationFeature, FeatureHandlerStrategy.INTERNAL)
The feature can be enabled with the following in your sentry.conf.py
, usually located at ~/.sentry/
:
SENTRY_FEATURES["organizations:onboarding"] = True
You can modify the state of feature flags in tests using a context manager.
Generally you want your feature names to be unique to help in their
removal. For example a feature flag like trends
may prove difficult to find because trends
may appear throughout the codebase.
But a name like performance-trends-view
is more likely to be unique and easier
to remove later
Creating a new Feature Flag
Determine what scope the feature should have
Features can be scoped by organization, and projects. If you're not confident
you want a project feature, create an organization level one. In this example
we'll build a feature called test-feature
scoped at the organization level.
Determine the permanency of your feature
Typically we use feature flags for development. They are usually intended to be graduated. Feature flag that controls a subscription-based feature for sentry.io are permanent.
Most feature flags are placed in temporary.py
, permanent flags live in
permanent.py
.
Add your feature to server.py
conf/server.py
contains many of the default settings in the application. Here you will add
your feature, and decide what default value it should hold unless specified by
the user.
The SENTRY_FEATURES
dictionary contains all the features in the application
with their corresponding scope. Your feature should start off disabled by default:
SENTRY_FEATURES = {
'organizations:test-feature': False,
'auth:register': True,
# ...
'projects:minidump': False,
}
Add your feature to the FeatureManager
If you want to back your feature flag via options
add the feature to the FeatureManager
like so using the OPTIONS
enum.:
default_manager.add('organizations:test-feature', OrganizationFeature, FeatureHandlerStrategy.OPTIONS)
If you don't plan to use options, use FeatureHandlerStrategy.INTERNAL
, for example:
default_manager.add('organizations:test-feature', OrganizationFeature, FeatureHandlerStrategy.INTERNAL)
Add it to the Organization Model Serializer
The Organization model serializer
(src/sentry/api/serializers/models/organization.py
) builds a list
called feature_list
that is given to the front-end to use. By default the all
features are checked and those that are present are added into the list. If
your feature requires additional custom logic you will have to update the
organization serializer
Using Model Flags (Less common)
Sometimes a flag on the model is used to indicate a feature flag as shown
below. This is not recommended unless there is a specific reason to make
changes to the model. For example, the require_2fa
flag affects behavior on
the backend to enforce two-factor authentication.
feature_list = []
if getattr(obj.flags, 'allow_joinleave'):
feature_list.append('open-membership')
if not getattr(obj.flags, 'disable_shared_issues'):
feature_list.append('shared-issues')
if getattr(obj.flags, 'require_2fa'):
feature_list.append('require-2fa')
Checking your feature
In Python code
The FeatureManager's has
method checks see if the feature exists. The has
method takes in the feature's name, the object that corresponds to the scope of
the feature (i.e. an organization for an organization level feature or
a project for a project level feature), and the actor (aka user). In our case
the feature will be added like:
if features.has('organizations:test-feature', obj, actor=user):
feature_list.append('test-feature')
which only adds the feature to the feature_list
if that feature is enabled for
the organization and the type of user given. Note that when we give the feature
to the frontend, we remove the scope prefix, and
our 'organizations:test-feature'
becomes 'test-feature'
.
In JavaScript
There is a difference between using the flag in Sentry and in GetSentry. At this stage you're not quite ready to use your feature flag in GetSentry, but you are able to use it inside Sentry.
Declarative features with the Feature component
React uses a declarative programming paradigm. As such, we have a utility component that we use to hide components based on the feature flags available to a organization/project
import Feature from 'sentry/components/acl/feature';
const toRender = (
<Feature features={['test-feature']}>
<MyComponentToFlag />
</Feature>
);
Imperative feature flag checks
There are some exceptions when React components are generated imperatively (e.g.
headers/columns for Tables). In difficult times like these, the Organization
/ Project
object has a array of the feature flags, which you can use in this
way:
const {organization} = this.props;
// Method 2
organization.features.includes('test-feature'); // evals to True/False
Enabling features in development
In Sentry you can run sentry devserver
to view your changes in development
mode. If you would like to view a change behind a feature flag, you will need to
open the file ~/.sentry/sentry.conf.py
on your local machine. This file
contains your local settings for the sentry application, and can be viewed and
edited. If you would like to toggle a flag on or off, add this to your
configuration file:
SENTRY_FEATURES['organizations:test-feature'] = True
Where SENTRY_FEATURES
will correspond to the SENTRY_FEATURES
from step 2
.
Set it to True
if you'd like the feature to be available and False
if not.
Enabling your feature in production
Feature flags are declared in Sentry's codebase. For self-hosted users, those
flags are then configured via sentry.conf.py
. For Sentry's SaaS deployment,
you have the choice of using an option backed rollout via Options Automator,
or by writing a custom feature flag handler.
- Options based features allow a feature to be rolled out to a specific subset of LA orgs, a percentage of EA orgs, and/or a percentage of all orgs. These can be used in high scale situations, and are generally preferred over customer feature handlers.
Building your options based feature
Declare your feature in sentry/features/temporary.py or sentry/features/permanent.py like so:
manager.add("organizations:your-new-flag", OrganizationFeature, FeatureHandlerStrategy.OPTIONS)
This automatically registers a feature handler for you in getsentry, and also auto registers the relevant options.
To roll out your feature, you'll want to set the rollout options via options automator. To figure out the options you need to configure, you can run the following script in getsentry:
./bin/generate_automator_feature_options.py --flag organizations:your-new-flag
It will then produce output like
Generating automator options for feature flag `organizations:your-new-flag`
Add these to the appropriate file in options automator to roll out your feature
For example, to roll out in the US, you can add them to https://github.com/getsentry/sentry-options-automator/blob/main/options/regions/us/app.yaml
To roll out to all production regions, you can add them to https://github.com/getsentry/sentry-options-automator/blob/main/options/default/app.yaml
Options:
organizations.your-new-flag.la-rollout: 0.0
organizations.your-new-flag.ea-rollout: 0.0
organizations.your-new-flag.ga-rollout: 0.0
organizations.your-new-flag.la-orgs: [
"codecov",
"sentry",
"sentry-sdks",
"sentry-test",
"sentry-st",
"sentry-emerging-tech",
"specto-dev",
]
Open a pr in https://github.com/getsentry/sentry-options-automator to set
these options and you can roll out your feature flag. You can also customize
which orgs will be in your la rollout by modifying la-orgs
After launch (Graduation)
After your feature has been mainlined and is available for all customers on sentry.io, you have a few potential paths:
- If the feature cannot be disabled, or you don't need to conditionally disable the feature, remove the feature flag and all related checks from the Sentry code base. If necessary, also remove references to the feature from the self-hosted and getsentry repositories.
- If the feature will only be available to SaaS customers on specific plans, you
need to add your feature flag to the appropriate plans and update feature
handlers (see below).You should also enable the feature by default in
conf/server.py
in sentry to ensure that the feature is available for self-hosted deployments.
Getsentry feature handlers
Getsentry contains a variety of feature handlers that override the
SENTRY_FEATURES
map.
Plan specific features
If your feature is available for a subset of plans (eg. only on Business plans) you need to:
- Disable the feature in
getsentry/conf/settings/defaults.py
by updatingSENTRY_FEATURES
. - Add your feature to the appropriate plan feature list.
- Update
SubscriptionPlanFeatureHandler
to handle your feature.
Custom handlers
If your feature requires additional logic to become active, you can also implement a feature handler in getsentry. For example, you can create a feature flag that is backed by options. follow these steps:
- Disable the feature in
getsentry/conf/settings/defaults.py
by updatingSENTRY_FEATURES
. - Add a new feature handler class in
getsentry/features.py
that determines availability of the feature based on the organization or actor. - Register the handler at the bottom of
getsentry/features.py
.