First choose the general type of toggle and then the more specific implementation below.
There is also a decision map in OEP-17 on feature toggles.
type |
description |
config as data |
config as code |
beyond on/off |
---|---|---|---|---|
Boolean Django Settings |
Boolean Django Settings are simple on/off toggles. At edX, this would be set and deployed via remote config. |
X |
||
Waffle Switches |
Waffle switches are simple on/off toggles. These are configured through Django admin. |
X |
||
Waffle Flags |
Waffle flags are on/off toggles with a variety of other capabilities, such as percent rollout, setting for individual users or courses, etc. These are configured through Django admin. |
X |
Percent rollout, setting by user or course, potentially handles A/B experiments. |
|
Config Models with Boolean Fields |
Config models enable more complex configuration models with audit capabilities. Like Django Settings, config models would only contain toggles if it contained boolean fields. These are configured through Django admin. |
X |
This section covers waffle switches and flags as well as Django Settings toggles. For information on Configuration Models refer to that library.
Flags and switches are created with a name. The namespace prefix is used to categorize them and prevent conflicts.
FLAG_FOO = WaffleFlag('namespace.feature', module_name=__name__)
SWITCH_BAR = WaffleSwitch('namespace.feature', module_name=__name__)
SETTING_TOGGLE_BAZ = SettingToggle("SETTING_NAME", default=False, module_name=__name__)
For the waffle flag and switch, you will use Django Admin “waffle” section to configure for a flag named namespace.feature
. Setting toggles are a wrapper around standard Django settings.
Functions to override waffle flags in test are provided in testutils.
Unlike the underlying waffle and settings libraries which look up values by name, toggles are imported and used directly:
from whereever import FLAG_FOO, SWITCH_BAR, SETTING_TOGGLE_BAZ
FLAG_FOO.is_enabled()
SWITCH_BAR.is_enabled()
SETTING_TOGGLE_BAZ.is_enabled()
Obviously since some toggles take into account the user they must have more data than the zero arguments of is_enabled()
.
Waffle switches and flags have a few hidden sources of information. Both use a per-request cache, so a toggle value is stable during a request if there is one. Flags (including Course and Experiment types) additionally access the request via crum.get_request
and through it the user. This means that in an environment without a request, flags will fail unless they are turned on for everyone like a simple switch.
Settings toggles are unsurprisingly reading from the django settings object.
As a best practice, when implementing toggles in a library, we recommend keeping those toggles limited to private or internal use within the library. In other words, only code within the library should refer directly to the toggle.
If you wish to expose whether a library, service, or feature is available to its consumers, we recommend you instead expose a boolean method that may be implemented by wrapping a toggle.
As part of implementing your new toggle, read how to document the toggle, which should also help you think through the use cases and life expectancy of the toggle.
For additional details, see references to the actual toggle class implementations.
Use the SettingToggle and SettingDictToggle classes to implement toggles based on a Django Setting. This new class should be added to the Django app that most closely relates to the setting. See the ADR for the Setting Toggle classes to understand the advantages over using the Django Setting directly.
If the toggle is being added to edx-platform, and it needs to be used by both LMS and Studio, you can add it to openedx/core/toggles.py
.
Avoid referring to boolean Django Settings directly. However, if a boolean setting toggle is implemented without one of the wrapping classes, its annotation implementation would be DjangoSetting.
Use the WaffleSwitch class, a wrapper around the waffle switch.
If you are wrapping a legacy switch that does not have a namespaced name (i.e. no .
in the name), use the NonNamespacedWaffleSwitch
instead.
For the basic capabilities, use the WaffleFlag class, a wrapper around the waffle flag.
If you are wrapping a legacy flag that does not have a namespaced name (i.e. no .
in the name), use the NonNamespacedWaffleFlag
instead.
In edx-platform, there is also:
CourseWaffleFlag: A WaffleFlag that adds override capabilities per course and per organization.
ExperimentWaffleFlag: A somewhat complex CourseWaffleFlag that enables bucketing of users for A/B experiments.
A ConfigurationModel can be used if all other options do not suit your needs. In most cases, it is no longer necessary.