We have used user variants (feature flags stored as user preferences) for various A/B testing or scaling related things in GrowthExperiments: homepage A/B tests (T238887: [EPIC] Growth: Homepage variant tests), gradually enabling new features like Add Link, gradually enabling existing features on new wikis. It's a common enough use case that we could save time and increase the robustness of the code by having a generic mechanism instead of writing one-off code every time the need for a new variant arises.
We have started working towards that goal with the ExperimentUserManager and VariantHooks classes and the variant-related functionality in ge.utils, but those only handle variants for one flag (typically the ongoing A/B test, which is currently structured vs. traditional Add Link). We should expand these to handle multiple different flags at the same time, so they can also be used for new wiki rollouts of old features etc.
Roughly what would be needed:
- Some configuration file to declare flags and their valid values (such as linkrecommendation => 0/1, variant => A/B/C/D, mentorship => 0/1) and defaults. Currently this is VariantHooks but it only supports one dimension/flag.
- A storage and lookup mechanism which is tied to the user (the current, single-flag-only system for this is ExperimentUserManager). One option would be to convert all the flags to a JSON object and store it in a user preference, but it's probably better to turn each flag into its own user preference via some prefix. That allows backward compatibility (we'd just need a map of flags which use nonstandard preference keys), easier to inspect in the database, and plays nicer with the occasional need to mass-update with a maintenance script.
- Configuration for probably weights to use when setting the flags for a new user, and a mechanism for manually overriding that configuration when registering (both for test accouts, and for special use cases like outreach events). For the current one-dimensional system this lives in HomepageHooks::onLocalUserCreated and HomepageHooks::onAuthChangeFormFields. Since the generalized version is not related to the homepage anymore, we probably want to move it to VariantHooks.
- A mechanism for accessing the (validated, fallbacked) values on the client side, and changing it for testing/debugging purposes. Currently handled via VariantHooks and ext.growthExperiments.Utils.js.
- Maybe a mechanism for automatically including them in analytics events. Currently this is done in ext.growthExperiments.Homepage.Logger.js. Not sure how much this part can be generalized.
The new system should be backwards compatible with the existing ad hoc one in at least two ways:
- Values stored in current (somewhat randomly named) user preferences should continue to be used when the respective feature flag gets imported into the system.
- The current URL overrides (which live in HomepageHooks::onAuthChangeFormFields) should continue to work.
See also: T283867: Maintenance script for changing user settings