Skip to content

Conversation

@asalsys
Copy link

@asalsys asalsys commented Nov 30, 2025

Remote Feature Flag Controller - Override Support & A/B Test Visibility

Explanation

Current State & Problem

The RemoteFeatureFlagController currently only supports remote feature flags with no ability to locally override them. This creates several pain points for developers and QA teams:

  • No local testing capability: Developers cannot test different flag configurations without changing remote settings
  • A/B test visibility gap: Once A/B test arrays are processed into single values, the raw options are lost, making it impossible to see what alternatives were available
  • Limited debugging: No way to understand current flag assignments vs. available options
  • QA testing constraints: Testing teams cannot systematically test different flag scenarios locally

Solution Overview

This PR adds local override functionality with A/B test visibility through direct state access:

Core Features:

  • Local Overrides: New localOverrides state field allows manual flag overrides that take precedence over remote flags
  • A/B Test Visibility: New rawRemoteFeatureFlags field stores raw A/B test arrays (preserves original flag data before processing)
  • Override Management: Complete CRUD operations for managing overrides (setFlagOverride, clearFlagOverride, clearAllOverrides)
  • State-Based Access: Flag values and A/B test data accessible through controller state properties

How It Works:

  1. Processing Enhancement: The #updateCache method now stores raw flag data in rawRemoteFeatureFlags before processing A/B tests into single values
  2. Priority System: Consumers can check state.localOverrides[flagName] ?? state.remoteFeatureFlags[flagName] for override-aware access
  3. Raw Storage: Raw flag data is stored for all flags, allowing visibility into original A/B test arrays
  4. Persistence: All override data persists across sessions and remote flag updates via the #updateCache method

Implementation Details

  • State Management: Three state fields work together:

    • remoteFeatureFlags: Processed flags (A/B tests resolved to single values)
    • localOverrides: Manual overrides that take precedence
    • rawRemoteFeatureFlags: Original flag data before processing (preserves A/B test arrays)
  • Override Persistence: Remote flag updates preserve existing local overrides through state preservation in #updateCache

  • Messenger Integration: All override methods are exposed as controller actions for external access

  • Metadata Configuration: All new state fields are properly configured for logging, persistence, and UI exposure

Access Patterns

Getting Flag Values (with override support):

// Manual override-aware access
const flagValue = controller.state.localOverrides.flagName ?? 
                  controller.state.remoteFeatureFlags.flagName;

Accessing A/B Test Groups:

// View available A/B test options
const abTestGroups = controller.state.rawRemoteFeatureFlags.flagName;

Managing Overrides:

// Set override
controller.setFlagOverride('flagName', 'testValue');

// Clear specific override
controller.clearFlagOverride('flagName');

// Clear all overrides
controller.clearAllOverrides();

Dependencies & Imports

  • Added Json type import from @metamask/utils for type safety
  • No new external dependencies required

References

This enhancement addresses the need for local flag testing and A/B test visibility that has been requested by development and QA teams for improved testing workflows.

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note: No breaking changes were introduced - this is purely additive functionality that maintains full backward compatibility.


Note

Adds local override support and raw A/B test flag visibility to RemoteFeatureFlagController, updates exports/metadata, and adjusts consumers/tests accordingly.

  • Remote Feature Flag Controller:
    • State: Add localOverrides and rawRemoteFeatureFlags; update metadata (persistence, logging, UI exposure).
    • Overrides API: Add setFlagOverride, clearFlagOverride, clearAllOverrides; local overrides take precedence; preserved across remote updates via #updateCache.
    • Processing/Cache: Store processed flags in remoteFeatureFlags and raw flags in rawRemoteFeatureFlags; export controllerName.
    • Types/Actions/Exports: Export new action types (RemoteFeatureFlagControllerSetFlagOverrideAction, ...ClearFlagOverrideAction, ...ClearAllOverridesAction) and updated state type via index.ts.
  • Consumers & Tests:
    • Update tests in transaction-controller and transaction-pay-controller to include new state fields and use updated flag state.
    • Add tests for override behavior and metadata exposure.
  • Docs:
    • Update CHANGELOG.md with new features and behavior changes.

Written by Cursor Bugbot for commit c1af969. This will update automatically on new commits. Configure here.

@asalsys asalsys requested review from a team as code owners November 30, 2025 21:53
@asalsys asalsys requested a review from a team as a code owner December 2, 2025 18:46
@asalsys asalsys force-pushed the feat/override-functionality-to-remote-feature-flags branch from f131cc8 to 11c0f59 Compare December 5, 2025 00:38
@asalsys asalsys force-pushed the feat/override-functionality-to-remote-feature-flags branch from 11c0f59 to 7ae47a3 Compare December 8, 2025 21:48
if (Array.isArray(processedValue) && thresholdValue) {
// Store the raw A/B test array for later use
rawProcessedRemoteFeatureFlags[remoteFeatureFlagName] =
remoteFeatureFlagValue;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Raw A/B flags not stored when threshold equals zero

The new raw flag storage code on lines 297-299 is inside a conditional that checks Array.isArray(processedValue) && thresholdValue. Since thresholdValue is a number that can be exactly 0 (when a user's metaMetricsId produces the minimum UUID value), and 0 is falsy in JavaScript, the entire block is skipped. This means rawProcessedRemoteFeatureFlags won't capture A/B test arrays for users with threshold value 0, breaking the new A/B test visibility feature for this edge case. The raw flag storage logic likely needs to be independent of the thresholdValue check.

Fix in Cursor Fix in Web

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is existing functionality

@asalsys asalsys self-assigned this Dec 10, 2025
@github-project-automation github-project-automation bot moved this to Needs dev review in PR review queue Dec 10, 2025
Copy link
Contributor

@Cal-L Cal-L left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a comment

* @param value - The override value for the feature flag.
*/
setFlagOverride(flagName: string, value: Json): void {
this.update(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can spread ...this.state for unaffected states

this.update(() => {
return {
remoteFeatureFlags: this.state.remoteFeatureFlags,
localOverrides: newOverrides,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spread ..this.state for unaffected states

this.update(() => {
return {
remoteFeatureFlags: this.state.remoteFeatureFlags,
localOverrides: {},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spread state for unaffected states

remoteFeatureFlags: processedRemoteFeatureFlags,
remoteFeatureFlags: processedFlags,
localOverrides: this.state.localOverrides,
rawRemoteFeatureFlags: remoteFeatureFlags,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: rawRemoteFeatureFlags stores all flags instead of only A/B test flags

The #updateCache method stores the entire remoteFeatureFlags object as rawRemoteFeatureFlags, but the PR description, CHANGELOG, and tests clearly indicate that only A/B test flags (threshold arrays) should be stored for efficiency. The test 'only stores raw values for A/B test flags, not simple flags' expects rawRemoteFeatureFlags to contain only threshold-based flags like testFlagForThreshold, excluding simple boolean/string flags. The implementation stores everything, contradicting the documented "selective raw storage" behavior and causing the test to fail.

Fix in Cursor Fix in Web

@asalsys asalsys requested a review from Cal-L December 11, 2025 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Needs dev review

Development

Successfully merging this pull request may close these issues.

3 participants