Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Splitting re-usable page-level experiment functionality out of google/ads/traffic-experiments.js #8374

Merged
merged 1 commit into from Apr 25, 2017

Conversation

tlong2
Copy link
Contributor

@tlong2 tlong2 commented Mar 24, 2017

For testing amp-auto-ads for Google ad users we'd like to have page-level experiments that divert client-side.

This CL splits the generic page-level experiment selection logic out of google/ads/traffic-experiments.js, so that it can be re-used for other things.

Other details:

  • introduces an isTrafficEligible function in ExperimentInfo, so experiments can be easily limited to certain slices of traffic (e.g. only those tagged with amp-auto-ads)
  • adds unit tests for googleAdsIsA4AEnabled in traffic-experiments.js
  • changes the list of experiment branches in ExperimentInfo to an array, so that the types aren't being violated when more than 2 branches are defined. This also protects against random other properties that may get inserted onto the ExperimentInfo object by the compiler.

Copy link

@tdrl tdrl left a comment

Choose a reason for hiding this comment

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

Generally looks good, and I'm willing to approve as-is. But one suggestion (about migrating all experiments info to the new format) and one open question (about whether the page-level-experiments should move to src/, instead of ads/google/)

const experimentInfo = {};
experimentInfo[experimentName] = internalBranches;
const experimentInfoMap = {};
experimentInfoMap[experimentName] = {
Copy link

Choose a reason for hiding this comment

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

The format change is reasonable, but why not just migrate the existing experiment info descriptions to the new format, rather than copying them here at runtime?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I started doing that, but the logic on line 105 that decides whether or not A4A is enabled, needs to know whether the chosen experiment was the control or the experiment, so it's not completely trivial to change it to using anonymous branches. There's probably something we can do, but would rather tackle it in a separate PR request so this one doesn't get too far reaching.

Copy link

Choose a reason for hiding this comment

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

Makes sense and SGTM.

@@ -0,0 +1,133 @@
/**
* Copyright 2016 The AMP HTML Authors. All Rights Reserved.
Copy link

Choose a reason for hiding this comment

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

Only question is whether this should continue to live in the ads/google hierarchy or just move to the src/ hierarchy? Perhaps @lannka has an opinion? (History: When I wrote the traffic-experiments.js code in the first place, we debated whether it was better to make it local to A4A or shared. At that time, it was decided to keep it local until there was a need to expand it. This is a clear need to expand, at least to Google scope. Do we foresee that it could be valuable to make this available to other clients as well, and so move to src/?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I imagine it would be useful outside the google scope and nothing in page-level-experiments.js is specific to google (or even ads) now so happy to move if @lannka is happy with that?

Copy link
Contributor

Choose a reason for hiding this comment

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

i was about to ask the same thing. feel free to move to src/experiments.js
also, in the context of src/experiments.js , all the experiment is page-level, page prefix is no longer needed in your method names.

@lannka
Copy link
Contributor

lannka commented Apr 4, 2017

@tlong2 this looks good.
but have you considered introducing a more generic experiment control in src/experiments.js

getExperimentBranch('experiment-a') {
 ...
}

It will be configured at build-system/global-configs/prod-config.json:

{
  "experiment-a": {
    "branch-1": 0.5,
    "branch-2": 0.5,
  }
}

It will not have isTrafficEligible though

@tlong2
Copy link
Contributor Author

tlong2 commented Apr 5, 2017

@lannka, the isTrafficEligible feature is important. The immediate use case for this is to test amp-auto-ads with traffic experiments. Since very few pubs will have the tag it will be necessary to limit the experiment to only pubs with the <amp-auto-ads> tags, otherwise the data will be too diluted to be useful.

* @visibleForTesting
*/
export function randomlySelectUnsetPageExperiments(win, experiments) {
win.pageExperimentBranches = win.pageExperimentBranches || {};
Copy link
Contributor

Choose a reason for hiding this comment

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

@tdrl any reason why this was set on window object?

Copy link

Choose a reason for hiding this comment

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

It's page-level, rather than element-level info, so I needed some page shared state to track. (Ensures that multiple slots on the same page don't reach different conclusions about their experiment status.) Is there a better place to store such shared state?

Copy link
Contributor

Choose a reason for hiding this comment

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

you can just put a local var, as we did here

@@ -0,0 +1,133 @@
/**
* Copyright 2016 The AMP HTML Authors. All Rights Reserved.
Copy link
Contributor

Choose a reason for hiding this comment

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

i was about to ask the same thing. feel free to move to src/experiments.js
also, in the context of src/experiments.js , all the experiment is page-level, page prefix is no longer needed in your method names.

continue;
}

if (!experiments[experimentName].isTrafficEligible(win)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

better check the availability of isTrafficEligible function before using it.

Copy link
Contributor

@lannka lannka left a comment

Choose a reason for hiding this comment

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

@tlong2 this is very cool. thanks for making the change. Actually my next PR will be benefitted from your refactoring.

As commented in code, please move the logic into src/experiments.js

@tlong2
Copy link
Contributor Author

tlong2 commented Apr 6, 2017

@lannka Have moved the logic to src/experiments.js and removed the page prefix. Also now checks that the isTrafficEligible function is definitely defined before calling it.

Copy link
Contributor

@lannka lannka left a comment

Choose a reason for hiding this comment

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

Sorry for the delay. (Was in paternity leave).
The refactoring looks great.

I'll let @tdrl do a final check to make sure it's not going to break the A4A experiments.

@lannka
Copy link
Contributor

lannka commented Apr 17, 2017

@tlong2 pls also resolve the conflicts

@tlong2
Copy link
Contributor Author

tlong2 commented Apr 18, 2017

@lannka Conflicts resolved

@lannka
Copy link
Contributor

lannka commented Apr 18, 2017

seems @tdrl is OOO, @keithwrightbos can you take a look to make sure the new changes after @tdrl 's approval does not break any A4A experiment settings?

Copy link
Contributor

@keithwrightbos keithwrightbos left a comment

Choose a reason for hiding this comment

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

LGTM though if there is no rush and we're concerned, we could wait for Terran to return from vacation (will be back Monday)

*/
export const PROFILING_BRANCHES = {
a4aProfilingRate: {
control: 'unused',
experiment: 'unused',
isTrafficEligible: () => { return true; },
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be isTrafficEligible: () => true,

Copy link

@tdrl tdrl left a comment

Choose a reason for hiding this comment

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

Some comments about restructuring tests, but they're not critical. (Nice to have, if there's time, though.) Overall, LGTM.

const renderViaA4a = googleAdsIsA4AEnabled(
sandbox.win, element, 'exp_name', externalBranches, internalBranches);
expect(renderViaA4a).to.be.true;
expect(element.getAttribute(EXPERIMENT_ATTRIBUTE)).to.equal('34,2088461');
Copy link

Choose a reason for hiding this comment

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

Better to use traffic-experiments.js#IsExperimentOn to check for eid 34, rather than literal string match -- the other active experiments could change outside of your test control. And use expect(IsExperimentOn(12)).to.be.false to ensure that unwanted experiments are not turned on. Finally, import traffic-experiments.js#EXTERNALLY_SELECTED_ID and check for it via IsExperimentOn rather than checking 2088461 directly. Might be simplest to roll all this into a helper function that does all these expectations and can be called from every test case. (Ditto for other eid tests below.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

experimentInfo[experimentName] = internalBranches;
const experimentInfoMap = {};
experimentInfoMap[experimentName] = {
isTrafficEligible: () => { return true; },
Copy link

Choose a reason for hiding this comment

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

I think, like @keithwrightbos mentioned elsewhere, that this can just be isTrafficEligible: () => true

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

const selectedBranch = getPageExperimentBranch(win, experimentName);
addExperimentIdToElement(selectedBranch, element);
const selectedBranch = getExperimentBranch(win, experimentName);
if (selectedBranch) {
Copy link

Choose a reason for hiding this comment

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

Could selectedBranch ever fail to be truthy here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

With the exact current combinations of the internal branches, isTrafficEligible value, and behavior of all the various functions probably no. But since anyone of those things is liable to be changed by someone at some point, we should check that it is truthy here.

@lannka lannka merged commit 6038844 into ampproject:master Apr 25, 2017
KenneyE pushed a commit to spotxchange/amphtml that referenced this pull request May 3, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants