1
+ /**
2
+ * Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3
+ * Licensed under the GNU Affero General Public License (AGPL).
4
+ * See License-AGPL.txt in the project root for license information.
5
+ */
6
+
7
+ const UI_EXPERIMENTS_KEY = "gitpod-ui-experiments" ;
8
+
9
+ /**
10
+ * This enables UI-experiments: Dashboard-local changes that we'd like to try out and get some feedback on/validate
11
+ * our assumptions on via mixpanel before we roll them out to everyone. The motivation is to make UI development more
12
+ * data-driven then the current approach.
13
+ *
14
+ * An experiment is supposed to:
15
+ * - only be applied to a subset of users, so we are guaranteed to always have a control group (configurable per experiment)
16
+ * - is dashboard/component local: it's not a mechnanism to cross the API boundary, mostly even component-local
17
+ * - is stable per session/client
18
+ * - is defined in code (see below): adding/deprecating experiments requires a deployment
19
+ * It is NOT supposed to:
20
+ * - big a way to roll-out big, completly new features
21
+ * - have a lot of different experiments in parallel (too noisy)
22
+ *
23
+ * Questions:
24
+ * - multiple experiments per user/time
25
+ */
26
+ const Experiments = {
27
+ /**
28
+ * Experiment "example" will be activate on login for 10% of all clients.
29
+ */
30
+ // "example": 0.1,
31
+ } ;
32
+ const ExperimentsSet = new Set ( Object . keys ( Experiments ) ) as Set < Experiment > ;
33
+ export type Experiment = keyof ( typeof Experiments ) ;
34
+
35
+ export namespace Experiment {
36
+ export function seed ( keepCurrent : boolean ) : Set < Experiment > {
37
+ const current = keepCurrent ? get ( ) : undefined ;
38
+
39
+ // add all current experiments to ensure stability
40
+ const result = new Set < Experiment > ( [ ...( current || [ ] ) ] . filter ( e => ExperimentsSet . has ( e ) ) ) ;
41
+
42
+ // identify all new experiments and add if random
43
+ const newExperiment = new Set < Experiment > ( [ ...ExperimentsSet ] . filter ( e => ! result . has ( e ) ) ) ;
44
+ for ( const e of newExperiment ) {
45
+ if ( Math . random ( ) < Experiments [ e ] ) {
46
+ result . add ( e ) ;
47
+ }
48
+ }
49
+
50
+ return result ;
51
+ }
52
+
53
+ export function set ( set : Set < Experiment > ) : void {
54
+ try {
55
+ const arr = Array . from ( set ) ;
56
+ window . localStorage . setItem ( UI_EXPERIMENTS_KEY , JSON . stringify ( arr ) ) ;
57
+ } catch ( err ) {
58
+ console . error ( `error setting ${ UI_EXPERIMENTS_KEY } ` , err ) ;
59
+ }
60
+ }
61
+
62
+ export function has ( experiment : Experiment ) : boolean {
63
+ const set = get ( ) ;
64
+ if ( ! set ) {
65
+ return false ;
66
+ }
67
+ return set . has ( experiment ) ;
68
+ }
69
+
70
+ export function get ( ) : Set < Experiment > | undefined {
71
+ const arr = window . localStorage . getItem ( UI_EXPERIMENTS_KEY ) ;
72
+ if ( arr === null ) {
73
+ return undefined ;
74
+ }
75
+ return new Set ( arr ) as Set < Experiment > ;
76
+ }
77
+
78
+ export function getAsArray ( ) : Experiment [ ] {
79
+ const set = get ( ) ;
80
+ if ( ! set ) {
81
+ return [ ] ;
82
+ }
83
+ return Array . from ( set ) ;
84
+ }
85
+ }
0 commit comments