experimentation.rb 4.27 KB
Newer Older
1 2 3 4
# frozen_string_literal: true

# == Experimentation
#
5 6 7
# Utility module for A/B testing experimental features. Define your experiments in the `EXPERIMENTS` constant.
# Experiment options:
# - tracking_category (optional, used to set the category when tracking an experiment event)
8
# - use_backwards_compatible_subject_index (optional, set this to true if you need backwards compatibility)
9 10
#
# The experiment is controlled by a Feature Flag (https://docs.gitlab.com/ee/development/feature_flags/controls.html),
11
# which is named "#{experiment_key}_experiment_percentage" and *must* be set with a percentage and not be used for other purposes.
12
#
13 14
# To enable the experiment for 10% of the users:
#
Alex Buijs's avatar
Alex Buijs committed
15 16
# chatops: `/chatops run feature set experiment_key_experiment_percentage 10`
# console: `Feature.enable_percentage_of_time(:experiment_key_experiment_percentage, 10)`
17 18 19
#
# To disable the experiment:
#
20
# chatops: `/chatops run feature delete experiment_key_experiment_percentage`
21
# console: `Feature.remove(:experiment_key_experiment_percentage)`
22 23 24 25 26
#
# To check the current rollout percentage:
#
# chatops: `/chatops run feature get experiment_key_experiment_percentage`
# console: `Feature.get(:experiment_key_experiment_percentage).percentage_of_time_value`
27
#
28

Alex Buijs's avatar
Alex Buijs committed
29
# TODO: see https://gitlab.com/gitlab-org/gitlab/-/issues/217490
30 31 32
module Gitlab
  module Experimentation
    EXPERIMENTS = {
33
      onboarding_issues: {
34
        tracking_category: 'Growth::Conversion::Experiment::OnboardingIssues',
35
        use_backwards_compatible_subject_index: true
36
      },
37
      ci_notification_dot: {
38
        tracking_category: 'Growth::Expansion::Experiment::CiNotificationDot',
39
        use_backwards_compatible_subject_index: true
40
      },
41
      upgrade_link_in_user_menu_a: {
42
        tracking_category: 'Growth::Expansion::Experiment::UpgradeLinkInUserMenuA',
43
        use_backwards_compatible_subject_index: true
44 45
      },
      invite_members_version_a: {
46
        tracking_category: 'Growth::Expansion::Experiment::InviteMembersVersionA',
47
        use_backwards_compatible_subject_index: true
48
      },
49
      invite_members_version_b: {
50
        tracking_category: 'Growth::Expansion::Experiment::InviteMembersVersionB',
51
        use_backwards_compatible_subject_index: true
52
      },
53
      invite_members_empty_group_version_a: {
54 55
        tracking_category: 'Growth::Expansion::Experiment::InviteMembersEmptyGroupVersionA',
        use_backwards_compatible_subject_index: true
56
      },
57
      contact_sales_btn_in_app: {
58
        tracking_category: 'Growth::Conversion::Experiment::ContactSalesInApp',
59
        use_backwards_compatible_subject_index: true
60 61
      },
      customize_homepage: {
62
        tracking_category: 'Growth::Expansion::Experiment::CustomizeHomepage',
63
        use_backwards_compatible_subject_index: true
64 65
      },
      invite_email: {
66
        tracking_category: 'Growth::Acquisition::Experiment::InviteEmail',
67
        use_backwards_compatible_subject_index: true
68 69
      },
      invitation_reminders: {
70
        tracking_category: 'Growth::Acquisition::Experiment::InvitationReminders',
71
        use_backwards_compatible_subject_index: true
72 73
      },
      group_only_trials: {
74
        tracking_category: 'Growth::Conversion::Experiment::GroupOnlyTrials',
75
        use_backwards_compatible_subject_index: true
76 77
      },
      default_to_issues_board: {
78
        tracking_category: 'Growth::Conversion::Experiment::DefaultToIssuesBoard',
79
        use_backwards_compatible_subject_index: true
Alex Buijs's avatar
Alex Buijs committed
80 81 82
      },
      jobs_empty_state: {
        tracking_category: 'Growth::Activation::Experiment::JobsEmptyState'
83 84 85
      },
      remove_known_trial_form_fields: {
        tracking_category: 'Growth::Conversion::Experiment::RemoveKnownTrialFormFields'
86 87 88 89
      }
    }.freeze

    class << self
90
      def experiment(key)
91
        Gitlab::Experimentation::Experiment.new(key, **EXPERIMENTS[key])
92 93
      end

94
      def enabled?(experiment_key)
95 96
        return false unless EXPERIMENTS.key?(experiment_key)

97
        experiment(experiment_key).enabled?
98
      end
99

100 101 102 103 104
      def enabled_for_attribute?(experiment_key, attribute)
        index = Digest::SHA1.hexdigest(attribute).hex % 100
        enabled_for_value?(experiment_key, index)
      end

105 106
      def enabled_for_value?(experiment_key, value)
        enabled?(experiment_key) && experiment(experiment_key).enabled_for_index?(value)
107 108 109 110
      end
    end
  end
end