Feature Flags
How feature flags and rollouts work — the FeatureManager
In the previous concept on ExperienceManager, we learned how the SDK manages A/B tests (or "experiences") where visitors are assigned to different variations. But what if you don't need multiple variations? What if you just want to switch a specific piece of functionality on or off for certain users, like rolling out a new beta feature?
This is where Feature Flags come in, and the SDK has a dedicated manager for them.
The Problem: Toggling Features On and Off
Imagine you've developed a cool new "Dark Mode" for your website. You're not ready to release it to everyone yet. You want to:
- Enable it only for internal testers first.
- Later, enable it for 10% of all visitors.
- Eventually, roll it out to everyone.
- Maybe pass some configuration data (like the exact dark theme color) along with the feature flag.
Doing this with a full A/B test (ExperienceManager) feels like overkill. You don't have multiple variations of Dark Mode; you just want to turn it on or off. You need a simpler way to control the availability of this feature.
What is FeatureManager? Your Feature Control Panel
FeatureManager? Your Feature Control PanelThink of the FeatureManager as the control panel or switchboard for your website's features. While the ExperienceManager is directing complex A/B tests with different variations, the FeatureManager is focused on the simpler task of flipping switches: turning individual features on or off for specific visitors.
Key jobs of the FeatureManager:
- Knows the Features: It can list all features defined in your Convert project (via methods like
getList()andgetFeature()). - Checks the Switch: It can tell you if a specific feature (like "dark-mode") should be enabled for a particular visitor (via methods like
isFeatureEnabled()andrunFeature()). - Provides Configuration: If a feature is enabled, it can also give you any configuration variables associated with it (e.g., the
themeColorfor Dark Mode). - Uses Underlying Logic: Just like the
ExperienceManager, it doesn't make decisions in isolation. It relies on the underlying system (especially the DataManager) to figure out if a visitor meets the rules and bucketing criteria set up in Convert experiences that might enable this feature.
In essence, FeatureManager lets you ask: "For this visitor, is feature X turned on?".
Using the FeatureManager (via Context)
Similar to experiences, you typically won't use the FeatureManager directly. You'll use the convenient methods provided by the visitor's Context object.
Let's check if a visitor should get the "Dark Mode" feature. Assume the feature is identified by the key 'dark-mode' in your Convert project.
-
Get the Context: First, ensure you have the context for your visitor by calling
createContexton your initialized SDK instance, passing the visitor ID and any known attributes. -
Check if the Feature is Enabled: Call
isFeatureEnabledon the context, passing the feature key (e.g.,'dark-mode'). This returns a simple boolean:trueif the feature should be active for this visitor,falseotherwise. -
Getting Feature Details (including variables): If you need more than just on/off, like configuration values, call
runFeatureon the context, passing the feature key and optionally a set of bucketing attributes (visitor properties, location properties, etc.). The attributes are important if your feature targeting rules depend on them (like "only enable for users in Canada").The call returns a
BucketedFeatureobject with the following properties:Property Description featureIdThe feature's unique ID featureKeyThe feature's key (e.g., 'dark-mode')statusEither enabledordisabledvariablesKey-value pairs of feature variables (when enabled) experienceKeyThe key of the experience that enabled this feature (when enabled) If enabled, the result contains
status: enabledand avariablesmap (e.g.,{ themeColor: '#111', fontSize: '16px' }).If disabled, the result contains
status: disabled.
Under the Hood: How runFeature (and isFeatureEnabled) Works
runFeature (and isFeatureEnabled) WorksHow does the SDK decide if 'dark-mode' is enabled for 'user123'? It's a bit different from A/B testing. A feature isn't usually tested in isolation; it's often enabled as part of one or more experiences.
- Call Context Method: Your code calls
runFeature('dark-mode', ...)on the visitor context. - Delegate to FeatureManager: The
Contextobject passes the request to theFeatureManager'srunFeaturemethod, supplying the visitor ID and bucketing attributes. - FeatureManager Orchestrates: The
FeatureManagerdoesn't do bucketing itself. Instead, it needs to find out if the visitor is bucketed into any variation of any relevant experience that happens to enable the 'dark-mode' feature. It does this using itsrunFeatureshelper method. - Call
runFeatures: TherunFeaturemethod calls the internalrunFeaturesmethod, filtering to only consider experiences linked to 'dark-mode'. runFeaturesLogic:- Get Relevant Experiences: It asks the DataManager for the list of experiences that might affect the 'dark-mode' feature (or all experiences if not filtered).
- Check Each Experience: For each relevant experience, it asks the DataManager to perform bucketing for 'user123'. This involves the RuleManager and BucketingManager we saw in the previous concept.
- Examine Variation: If the visitor is bucketed into a variation for an experience,
runFeaturesinspects that variation'schangesdata. - Feature Enabled? It checks if the variation's
changesexplicitly list the 'dark-mode' feature (feature_id) as aFULLSTACK_FEATUREchange type. - Collect Results: If it finds any variation (across all checked experiences) that enables 'dark-mode' for 'user123', it gathers the feature details (key, status='enabled', variables).
- Return Result:
runFeaturesreturns a list of enabled features.runFeaturepicks the relevant one ('dark-mode') from this list. If 'dark-mode' wasn't found in any enabled variation's changes, it returns the disabled status object. - Context Returns: The
Contextreturns the finalBucketedFeatureobject to your code.
Here's a simplified diagram:
sequenceDiagram
participant YourCode as Your Application Code
participant Context as Visitor Context
participant FeatureMgr as FeatureManager
participant DataMgr as DataManager
participant BucketingEtc as Bucketing/Rules
YourCode->>Context: runFeature('dark-mode', ...)
activate Context
Context->>FeatureMgr: runFeature('user123', 'dark-mode', ...)
activate FeatureMgr
FeatureMgr->>FeatureMgr: runFeatures('user123', ..., features: ['dark-mode'])
FeatureMgr->>DataMgr: Get relevant experiences for 'dark-mode'
activate DataMgr
DataMgr-->>FeatureMgr: List of Experiences (e.g., Exp1, Exp2)
deactivate DataMgr
loop For Each Experience (Exp1, Exp2)
FeatureMgr->>DataMgr: getBucketing('user123', Exp.key, ...)
activate DataMgr
DataMgr->>BucketingEtc: Check rules & perform bucketing
activate BucketingEtc
BucketingEtc-->>DataMgr: Return Variation (or RuleError)
deactivate BucketingEtc
DataMgr-->>FeatureMgr: Return Variation (e.g., VarA for Exp1)
deactivate DataMgr
Note over FeatureMgr: Inspect VarA's changes. Does it enable 'dark-mode'?
end
alt 'dark-mode' was enabled by some variation
FeatureMgr-->>Context: Return BucketedFeature (status: 'enabled', variables: {...})
else 'dark-mode' was not enabled
FeatureMgr-->>Context: Return BucketedFeature (status: 'disabled')
end
deactivate FeatureMgr
Context-->>YourCode: Return BucketedFeature object
deactivate Context
The key idea is that features are often switched on/off as part of the variations within experiences. FeatureManager figures out which features are enabled by checking the outcome of the visitor's bucketing across relevant experiences.
Implementation Details
The Constructor
Like other managers, the FeatureManager receives and stores references to its dependencies at construction time -- primarily the DataManager and an optional LogManager. No complex setup is needed.
The runFeature Method
runFeature MethodThis method uses the more general runFeatures method internally:
- It first checks if the feature key exists in the project data using the DataManager's
getEntitylookup. - The main work is done by calling
runFeatures, passing the feature key in a filter object. This tellsrunFeaturesto only care about this specific feature. - It handles the case where the feature is enabled (returning the result from
runFeatures) or disabled (returning a status object withdisabled).
The runFeatures Method (Core Logic)
runFeatures Method (Core Logic)This is where the core feature flag evaluation happens:
- It gets the relevant experiences from the DataManager.
- It loops through each experience and calls the DataManager's
getBucketingto see which variation the visitor gets (if any). - Crucially, it inspects the
changesarray within each bucketed variation. - If a change is of type
FULLSTACK_FEATURE, it extracts thefeature_idand checks if it matches the desired feature (if filtering). - If it's a match, it builds a
BucketedFeatureresult withstatus: enabledand extracts anyvariables. - When no feature filter is provided, it also adds all remaining declared features with a
disabledstatus, giving you the complete picture. - It returns an array containing all the features found to be enabled for this visitor based on their bucketing outcomes.
Conclusion
The FeatureManager provides a focused way to handle feature flags within the Convert SDK. It allows you to easily check if a feature should be enabled for a visitor and retrieve any associated configuration variables.
Key takeaways:
- What feature flags are and why
FeatureManageris useful. - How to use
isFeatureEnabledon the context for a simple on/off check. - How to use
runFeatureon the context to get the feature's status and variables as aBucketedFeatureobject. - That
FeatureManagerworks by checking the results of visitor bucketing across relevant experiences (via the DataManager) to see if any variation enables the target feature.
Throughout the last few concepts, we've seen the ExperienceManager and FeatureManager constantly relying on another component to get experiment data, check rules, and perform bucketing. This central component is the DataManager.
Ready to see how the SDK stores and accesses all the project configuration data? Let's dive into the DataManager next!
Updated about 1 month ago