-
Notifications
You must be signed in to change notification settings - Fork 172
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add feature flag support for gradual rollouts (#2825)
### Motivation Add the ability to rollout features to users based on percentages. This PR will essentially replace our experimental features configuration with a much more granular and controlled rolled out strategy. Note: I'll leave removing the old experimental features setting to a future PR. ### Implementation The idea is to combine the user's machine ID and the feature flag name to generate a number that can be checked against the desired rollout percentage. With this strategy, we can guarantee that if we increase the percentage, users who already had the feature enabled will continue to do so. From our side, the idea is to maintain a constant with the feature names and the desired rollout, so that we can gradually increase it as needed. ### Automated Tests Added tests.
- Loading branch information
Showing
3 changed files
with
150 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import * as assert from "assert"; | ||
|
||
import * as vscode from "vscode"; | ||
import sinon from "sinon"; | ||
|
||
import { featureEnabled, FEATURE_FLAGS } from "../../common"; | ||
|
||
suite("Common", () => { | ||
let sandbox: sinon.SinonSandbox; | ||
|
||
setup(() => { | ||
sandbox = sinon.createSandbox(); | ||
const number = 42; | ||
sandbox.stub(vscode.env, "machineId").value(number.toString(16)); | ||
}); | ||
|
||
teardown(() => { | ||
sandbox.restore(); | ||
}); | ||
|
||
test("returns consistent results for the same rollout percentage", () => { | ||
const firstCall = featureEnabled("tapiocaAddon"); | ||
|
||
for (let i = 0; i < 50; i++) { | ||
const result = featureEnabled("tapiocaAddon"); | ||
|
||
assert.strictEqual( | ||
firstCall, | ||
result, | ||
"Feature flag should be deterministic", | ||
); | ||
} | ||
}); | ||
|
||
test("maintains enabled state when increasing rollout percentage", () => { | ||
// For the fake machine of 42 in base 16 and the name `fakeFeature`, the feature flag activation percetange is | ||
// 0.357. For every percetange below that, the feature should appear as disabled | ||
[0.25, 0.3, 0.35].forEach((percentage) => { | ||
(FEATURE_FLAGS as any).fakeFeature = percentage; | ||
assert.strictEqual(featureEnabled("fakeFeature" as any), false); | ||
}); | ||
|
||
// And for every percentage above that, the feature should appear as enabled | ||
[0.36, 0.45, 0.55, 0.65, 0.75, 0.85, 0.9, 1].forEach((percentage) => { | ||
(FEATURE_FLAGS as any).fakeFeature = percentage; | ||
assert.strictEqual(featureEnabled("fakeFeature" as any), true); | ||
}); | ||
}); | ||
|
||
test("returns false if user opted out of specific feature", () => { | ||
(FEATURE_FLAGS as any).fakeFeature = 1; | ||
|
||
const stub = sandbox.stub(vscode.workspace, "getConfiguration").returns({ | ||
get: () => { | ||
return { fakeFeature: false }; | ||
}, | ||
} as any); | ||
|
||
const result = featureEnabled("fakeFeature" as any); | ||
stub.restore(); | ||
assert.strictEqual(result, false); | ||
}); | ||
|
||
test("returns false if user opted out of all features", () => { | ||
(FEATURE_FLAGS as any).fakeFeature = 1; | ||
|
||
const stub = sandbox.stub(vscode.workspace, "getConfiguration").returns({ | ||
get: () => { | ||
return { all: false }; | ||
}, | ||
} as any); | ||
|
||
const result = featureEnabled("fakeFeature" as any); | ||
stub.restore(); | ||
assert.strictEqual(result, false); | ||
}); | ||
|
||
test("returns true if user opted in to all features", () => { | ||
(FEATURE_FLAGS as any).fakeFeature = 0.02; | ||
|
||
const stub = sandbox.stub(vscode.workspace, "getConfiguration").returns({ | ||
get: () => { | ||
return { all: true }; | ||
}, | ||
} as any); | ||
|
||
const result = featureEnabled("fakeFeature" as any); | ||
stub.restore(); | ||
assert.strictEqual(result, true); | ||
}); | ||
}); |