SlideShare a Scribd company logo
1 of 90
Download to read offline
The Sky is Not Falling:
Scaling Experimentation for Product Development
Bill Hinderman
Web and Experimentation Engineering Lead, Vivid Seats
Hi.
Hi.
Direct
Organic
Search
Paid
Search
Ad
Placements
Email
Only I didn’t
say fudge.
falling
sky
The
is not
Opticon 8
@billhinderman
billhinderman.com
bill@billhinderman.com
18
falling
sky
The
is not
In brief.
What haven’t I done this?
How do I start?
How do I grow?
What size of team needs to scale experimentation?
Every
techincal change
is a
political change.
Every
technical change
is a political change.
How
do I
start?.
Pick your tools.
Build a roadmap based off of learning.
fig. 1: “Whoops!”
$
Build tools that fit
testing into your own
workflow.
Layer of abstraction allows us to skip call
Rapid QA cycle
Ability to share recipes with stakeholders
Easy debugging
Built into our own API
?experiment_key=recipe_key
private Variation setForcedVariationFromRequest(..., String uuid, String experimentKey) {
String parameter = OPTIMIZELY_FORCE_COOKIE_PREFIX + experimentKey;
String variationKey = request.getParameter(parameter);
if(variationKey == null) {
variationKey = cookieService.getCookieValue(request, parameter);
}
return new Variation(uuid, variationKey);
}
?experiment_key=recipe_key
private Variation setForcedVariationFromRequest(..., String uuid, String experimentKey) {
String parameter = OPTIMIZELY_FORCE_COOKIE_PREFIX + experimentKey;
String variationKey = request.getParameter(parameter);
if(variationKey == null) {
variationKey = cookieService.getCookieValue(request, parameter);
}
return new Variation(uuid, variationKey);
}
?experiment_key=recipe_key
private Variation setForcedVariationFromRequest(..., String uuid, String experimentKey) {
String parameter = OPTIMIZELY_FORCE_COOKIE_PREFIX + experimentKey;
String variationKey = request.getParameter(parameter);
if(variationKey == null) {
variationKey = cookieService.getCookieValue(request, parameter);
}
return new Variation(uuid, variationKey);
}
?experiment_key=recipe_key
Align experiment environments
with development environments.
When
am I
done
starting?.
Testing is part of product & engineering KPIs.
Team structure supports efficient experimentation.
Experimentation tools become their own product.
Decide upon a team
structure.
Experimentation Search App Checkout UI Components Supplier Integration
Experimentation Team
Experimentation
Feature / Platform
Experimentation Search App Checkout UI Components Supplier Integration
Decentralized experimentation
Experimentation
Feature / Platform
Experimentation Search App Checkout UI Components Supplier Integration
Experimentation
Feature / Platform
Experimentation Search App Checkout UI Components Supplier Integration
Experimentation Consultancy
Experimentation
Feature / Platform
Black box your testing
engine.
public Variation getVariation(HttpServletRequest request, HttpServletResponse response, String uuid, String experimentKey, Map<String, String> attributes) {
try {
if (requestUtils.isCustomer(request)) {
Cookie optimizelyCookie = cookieService.getCookie(request, OPTIMIZELY_COOKIE_PREFIX + experimentKey);
Variation variation;
if (attributes == null) {
attributes = new HashMap<>();
}
if (!attributes.containsKey("device_type")) {
attributes.putAll(getBaseAttributes(request));
}
String forceParameter = OPTIMIZELY_FORCE_COOKIE_PREFIX + experimentKey;
if (request.getParameter(forceParameter) != null || cookieService.doesCookieExist(request, forceParameter)) {
variation = setForcedVariationFromRequest(request, response, uuid, experimentKey);
} else if (optimizelyCookie != null) {
Optimizely optimizelyClient = getOptimizelyClient();
if (optimizelyClient == null) {
return null;
}
String optimizelyUUID = initOptimizelyUUID(request, response, uuid);
variation = optimizelyClient.getVariation(experimentKey, optimizelyUUID);
if (variation == null) {
cookieService.deleteCookie(response, optimizelyCookie.getName(), false);
variation = activateExperiment(request, response, uuid, experimentKey, attributes);
}
public Variation getVariation(HttpServletRequest request, HttpServletResponse response, String uuid, String experimentKey, Map<String, String> attributes) {
try {
if (requestUtils.isCustomer(request)) {
Cookie optimizelyCookie = cookieService.getCookie(request, OPTIMIZELY_COOKIE_PREFIX + experimentKey);
Variation variation;
if (attributes == null) {
attributes = new HashMap<>();
}
if (!attributes.containsKey("device_type")) {
attributes.putAll(getBaseAttributes(request));
}
String forceParameter = OPTIMIZELY_FORCE_COOKIE_PREFIX + experimentKey;
if (request.getParameter(forceParameter) != null || cookieService.doesCookieExist(request, forceParameter)) {
variation = setForcedVariationFromRequest(request, response, uuid, experimentKey);
} else if (optimizelyCookie != null) {
Optimizely optimizelyClient = getOptimizelyClient();
if (optimizelyClient == null) {
return null;
}
String optimizelyUUID = initOptimizelyUUID(request, response, uuid);
variation = optimizelyClient.getVariation(experimentKey, optimizelyUUID);
if (variation == null) {
cookieService.deleteCookie(response, optimizelyCookie.getName(), false);
variation = activateExperiment(request, response, uuid, experimentKey, attributes);
}
private Map<String, String> getBaseAttributes(HttpServletRequest request) {
Map<String, String> baseAttributes = new HashMap<>();
try {
String userAgentString = request.getHeader("User-Agent") == null ? "" : request.getHeader("User-Agent");
UserAgent userAgent = UserAgent.parseUserAgentString(userAgentString);
String browserType = userAgent.getBrowser().getName().toLowerCase();
if (browserType.contains("chrome") || browserType.contains("safari")) {
browserType = browserType.replaceAll("[^a-z]","");
}
String browserVersion = userAgent.getBrowserVersion().getMajorVersion();
String deviceType = userAgent.getOperatingSystem().getDeviceType() == eu.bitwalker.useragentutils.DeviceType.COMPUTER ? "desktop" :
userAgent.getOperatingSystem().getDeviceType().getName().toLowerCase();
String platform = userAgent.getOperatingSystem().getName().toLowerCase();
OrderChannel orderChannel = channelService.getActiveChannel(request);
String channel = "direct";
if (orderChannel != null && orderChannel.getH() != null) {
channel = orderChannel.getH().toLowerCase();
}
baseAttributes.put("browser_type", browserType);
baseAttributes.put("browser_version", browserVersion);
baseAttributes.put("channel", channel);
baseAttributes.put("device_type", deviceType);
baseAttributes.put("platform", platform);
} catch (Exception e) {
LOGGER.warn(e.getMessage(), e);
}
return baseAttributes;
}
public Variation getVariation(HttpServletRequest request, HttpServletResponse response, String uuid, String experimentKey, Map<String, String> attributes) {
try {
if (requestUtils.isCustomer(request)) {
Cookie optimizelyCookie = cookieService.getCookie(request, OPTIMIZELY_COOKIE_PREFIX + experimentKey);
Variation variation;
if (attributes == null) {
attributes = new HashMap<>();
}
if (!attributes.containsKey("device_type")) {
attributes.putAll(getBaseAttributes(request));
}
String forceParameter = OPTIMIZELY_FORCE_COOKIE_PREFIX + experimentKey;
if (request.getParameter(forceParameter) != null || cookieService.doesCookieExist(request, forceParameter)) {
variation = setForcedVariationFromRequest(request, response, uuid, experimentKey);
} else if (optimizelyCookie != null) {
Optimizely optimizelyClient = getOptimizelyClient();
if (optimizelyClient == null) {
return null;
}
String optimizelyUUID = initOptimizelyUUID(request, response, uuid);
variation = optimizelyClient.getVariation(experimentKey, optimizelyUUID);
if (variation == null) {
cookieService.deleteCookie(response, optimizelyCookie.getName(), false);
variation = activateExperiment(request, response, uuid, experimentKey, attributes);
}
public Variation getVariation(HttpServletRequest request, HttpServletResponse response, String uuid, String experimentKey, Map<String, String> attributes) {
try {
if (requestUtils.isCustomer(request)) {
Cookie optimizelyCookie = cookieService.getCookie(request, OPTIMIZELY_COOKIE_PREFIX + experimentKey);
Variation variation;
if (attributes == null) {
attributes = new HashMap<>();
}
if (!attributes.containsKey("device_type")) {
attributes.putAll(getBaseAttributes(request));
}
String forceParameter = OPTIMIZELY_FORCE_COOKIE_PREFIX + experimentKey;
if (request.getParameter(forceParameter) != null || cookieService.doesCookieExist(request, forceParameter)) {
variation = setForcedVariationFromRequest(request, response, uuid, experimentKey);
} else if (optimizelyCookie != null) {
Optimizely optimizelyClient = getOptimizelyClient();
if (optimizelyClient == null) {
return null;
}
String optimizelyUUID = initOptimizelyUUID(request, response, uuid);
variation = optimizelyClient.getVariation(experimentKey, optimizelyUUID);
if (variation == null) {
cookieService.deleteCookie(response, optimizelyCookie.getName(), false);
variation = activateExperiment(request, response, uuid, experimentKey, attributes);
}
String optimizelyUUID = initOptimizelyUUID(request, response, uuid);
private String initOptimizelyUUID(HttpServletRequest request, HttpServletResponse response, String uuid) {
String uuidCookie = cookieService.getCookieValue(request, OPTIMIZELY_COOKIE_PREFIX + "uuid");
if (StringUtils.isBlank(uuidCookie)) {
cookieService.setCookie(response, OPTIMIZELY_COOKIE_PREFIX + "uuid", uuid, SECONDS_5_YEARS);
return uuid;
}
return uuidCookie;
}
String forceParameter = OPTIMIZELY_FORCE_COOKIE_PREFIX + experimentKey;
if (request.getParameter(forceParameter) != null || cookieService.doesCookieExist(request, forceParameter)) {
variation = setForcedVariationFromRequest(request, response, uuid, experimentKey);
} else if (optimizelyCookie != null) {
Optimizely optimizelyClient = getOptimizelyClient();
if (optimizelyClient == null) {
return null;
}
String optimizelyUUID = initOptimizelyUUID(request, response, uuid);
variation = optimizelyClient.getVariation(experimentKey, optimizelyUUID);
if (variation == null) {
cookieService.deleteCookie(response, optimizelyCookie.getName(), false);
variation = activateExperiment(request, response, uuid, experimentKey, attributes);
}
} else {
variation = activateExperiment(request, response, uuid, experimentKey, attributes);
}
if (variation != null) {
setLogContextData(request);
return variation;
}
}
} catch (Exception e) {
LOGGER.warn("Unable get variation for experiment key: {}", experimentKey, e);
}
return null;
}
private Variation activateExperiment(HttpServletRequest request, HttpServletResponse response, String uuid, String experimentKey) {
return activateExperiment(request, response, uuid, experimentKey, new HashMap<String, String>());
}
private Variation activateExperiment(HttpServletRequest request, HttpServletResponse response, String uuid, String experimentKey, Map<String, String> attributes) {
Optimizely optimizelyClient = getOptimizelyClient();
if (optimizelyClient == null) {
return null;
}
String optimizelyUUID = initOptimizelyUUID(request, response, uuid);
Variation variation = optimizelyClient.activate(experimentKey, optimizelyUUID, attributes);
if (variation != null) {
LOGGER.info("Optimizely SDK: activateExperiment (Java)");
cookieService.setCookie(response, OPTIMIZELY_COOKIE_PREFIX + experimentKey, variation.getKey(), 2592000);
}
return variation;
}
private Variation activateExperiment(HttpServletRequest request, HttpServletResponse response, String uuid, String experimentKey) {
return activateExperiment(request, response, uuid, experimentKey, new HashMap<String, String>());
}
private Variation activateExperiment(HttpServletRequest request, HttpServletResponse response, String uuid, String experimentKey, Map<String, String> attributes) {
Optimizely optimizelyClient = getOptimizelyClient();
if (optimizelyClient == null) {
return null;
}
String optimizelyUUID = initOptimizelyUUID(request, response, uuid);
Variation variation = optimizelyClient.activate(experimentKey, optimizelyUUID, attributes);
if (variation != null) {
LOGGER.info("Optimizely SDK: activateExperiment (Java)");
cookieService.setCookie(response, OPTIMIZELY_COOKIE_PREFIX + experimentKey, variation.getKey(), 2592000);
}
return variation;
}
Web server-side
Web client-side
iOS client
Android client
Partner tools
Java full-stack
Java full-stack
iOS full-stack
Android full-stack
Partner tools full-stack
Web server-side
Web client-side
iOS client
Android client
Partner tools
The experiment stuff
How
do I
grow?.
Grow your appetite.
Improve your efficiency.
We don’t have the
appetite for more
experimentation.
Create a rubric for good
experiment hypothesis.
M
V
M
R
C
Adding a ticket details screen between
the tickets list and checkout screen will

aid users to find a more desirable ticket

which will ultimately improve conversion.
M
V
M
R
C
M
V
M
R
C
Measurable
Visible
Meaningful
Realistic
Cheap
Adding a ticket details screen between
the tickets list and checkout screen will

aid users to find a more desirable ticket

which will ultimately improve conversion.
Measurable
Visible
Meaningful
Realistic
Cheap
Adding a ticket details screen between
the tickets list and checkout screen will

aid users to find a more desirable ticket

which will ultimately enjoy their event more.
Measurable
Visible
Meaningful
Realistic
Cheap
Adding a ticket details screen between
the tickets list and checkout screen will

aid users to find a more desirable ticket

which will ultimately improve conversion.
Measurable
Visible
Meaningful
Realistic
Cheap
Adding a ticket details link in the
post-checkout receipt email will aid users
to find a more desirable ticket which will
ultimately improve conversion.
Measurable
Visible
Meaningful
Realistic
Cheap
Adding a ticket details screen between
the tickets list and checkout screen will

aid users to find a more desirable ticket

which will ultimately improve conversion.
Measurable
Visible
Meaningful
Realistic
Cheap
Adding a ticket details screen between
the tickets list and checkout screen will

aid users to view the venue’s code
of conduct which will ultimately
improve conversion.
Measurable
Visible
Meaningful
Realistic
Cheap
Adding a ticket details screen between
the tickets list and checkout screen will

aid users to find a more desirable ticket

which will ultimately improve conversion.
Measurable
Visible
Meaningful
Realistic
Cheap
Adding a ticket details screen that offers
free tickets will aid users to find a more
desirable ticket which will ultimately
improve conversion.
Measurable
Visible
Meaningful
Realistic
Cheap
Adding a ticket details screen between
the tickets list and checkout screen will

aid users to find a more desirable ticket

which will ultimately improve conversion.
Measurable
Visible
Meaningful
Realistic
Cheap
Building a ticket details app will aid users
to view find a more desirable ticket which will
ultimately improve conversion.
Measurable
Visible
Meaningful
Realistic
Cheap
Adding a ticket details screen between
the tickets list and checkout screen will

aid users to find a more desirable ticket

which will ultimately improve conversion.
We don’t have the
bandwidth for more
experimentation.
Build a hyperanalytical
platform.
Get QA involved.
Ideate
Build Disable
Analyze
Rollout
&
Regress
Run
Ideate
Build Disable
Analyze
Rollout
&
Regress
Run
Ideate
Build
Run
private Resolution getTicketPageView(UserAgent userAgent) {
try {
getContext().getResponse().addHeader("Vary", "User-Agent");
String web4801VariationKey = initVariation(WEB_4801_EXPERIMENT_KEY);
boolean isWEB4801Control = !"variant_1".equals(web4801VariationKey);
boolean showMobileView = userAgent.getDeviceType() == DeviceType.MOBILE || userAgent.getDeviceType() == DeviceType.TABLET;
if (!isMobileViewOverride() && showMobileView) {
if(!isWEB4801Control) {
initVariation(WEB_4304_EXPERIMENT_KEY);
return new ForwardResolution(JSP_PATH + RESPONSIVE_TICKET_PAGE_VIEW);
}
return new RedirectResolution(MobileProductionAction.class).addParameter(RequestAttributeConstants.PRODUCTION_PRODUCTION_ID,
production.getProductionId());
…
String web4456Variation = initVariation(WEB_4456_EXPERIMENT_KEY);
boolean isWEB4456Control = !"variant_1".equals(web4456Variation) && !"variant_2".equals(web4456Variation);
if (!isWEB4456Control) {
initVariation(WEB_4304_EXPERIMENT_KEY);
return new ForwardResolution(JSP_PATH + RESPONSIVE_TICKET_PAGE_VIEW);
}
Empower & trust your
center of excellence.
Every
techincal change
is a
political change.
Every
technical change
is a political change.
Let’s review.
Define your team’s optimization goal
Couple technology and process
Mandate measurable hypotheses
By hyperanalytical
Trust your center of excellence
You’re okay.
@billhinderman
billhinderman.com
bill@billhinderman.com
vividseats.com goshortwave.com podcastcampfire.com
mar-
tini
limi
-tedEst. 2018
martini.limited
vividseats.com goshortwave.com podcastcampfire.com
mar-
tini
limi
-tedEst. 2018
martini.limited
Questions?.
Opticon 8
@billhinderman
billhinderman.com
bill@billhinderman.com
18
Victor Forman
linkedin.com/in/victor-forman/
Andy Caples
linkedin.com/in/andy-caples-970b35/
John Banta
linkedin.com/in/bantaj/
Shanu Pant
linkedin.com/in/shanu-pant-093a807/
Smart people.
www.optimizely.com
www.shorpy.com
www.quora.com
www.caracaschronicles.com
www.tenor.com
Image and
story credit.
Thank you!
#opticon18

More Related Content

What's hot

Scala meetup
Scala meetupScala meetup
Scala meetup扬 明
 
Secret unit testing tools no one ever told you about
Secret unit testing tools no one ever told you aboutSecret unit testing tools no one ever told you about
Secret unit testing tools no one ever told you aboutDror Helper
 
Nashville Symfony Functional Testing
Nashville Symfony Functional TestingNashville Symfony Functional Testing
Nashville Symfony Functional TestingBrent Shaffer
 
HTML5 workshop, part 2
HTML5 workshop, part 2HTML5 workshop, part 2
HTML5 workshop, part 2Robert Nyman
 
Django’s nasal passage
Django’s nasal passageDjango’s nasal passage
Django’s nasal passageErik Rose
 
To change this template
To change this templateTo change this template
To change this templatekio1985
 
code for quiz in my sql
code for quiz  in my sql code for quiz  in my sql
code for quiz in my sql JOYITAKUNDU1
 
Тестирование и Django
Тестирование и DjangoТестирование и Django
Тестирование и DjangoMoscowDjango
 
Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Robert DeLuca
 
UA testing with Selenium and PHPUnit - PHPBenelux Summer BBQ
UA testing with Selenium and PHPUnit - PHPBenelux Summer BBQUA testing with Selenium and PHPUnit - PHPBenelux Summer BBQ
UA testing with Selenium and PHPUnit - PHPBenelux Summer BBQMichelangelo van Dam
 
Redux for ReactJS Programmers
Redux for ReactJS ProgrammersRedux for ReactJS Programmers
Redux for ReactJS ProgrammersDavid Rodenas
 

What's hot (20)

Clean Test Code
Clean Test CodeClean Test Code
Clean Test Code
 
Scala meetup
Scala meetupScala meetup
Scala meetup
 
SOLID Principles
SOLID PrinciplesSOLID Principles
SOLID Principles
 
Test
TestTest
Test
 
Secret unit testing tools no one ever told you about
Secret unit testing tools no one ever told you aboutSecret unit testing tools no one ever told you about
Secret unit testing tools no one ever told you about
 
Nashville Symfony Functional Testing
Nashville Symfony Functional TestingNashville Symfony Functional Testing
Nashville Symfony Functional Testing
 
Developer Testing Tools Roundup
Developer Testing Tools RoundupDeveloper Testing Tools Roundup
Developer Testing Tools Roundup
 
HTML5 workshop, part 2
HTML5 workshop, part 2HTML5 workshop, part 2
HTML5 workshop, part 2
 
Django’s nasal passage
Django’s nasal passageDjango’s nasal passage
Django’s nasal passage
 
Vaadin7
Vaadin7Vaadin7
Vaadin7
 
Qtp test
Qtp testQtp test
Qtp test
 
To change this template
To change this templateTo change this template
To change this template
 
code for quiz in my sql
code for quiz  in my sql code for quiz  in my sql
code for quiz in my sql
 
Тестирование и Django
Тестирование и DjangoТестирование и Django
Тестирование и Django
 
Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React
 
Nativescript angular
Nativescript angularNativescript angular
Nativescript angular
 
UA testing with Selenium and PHPUnit - PHPBenelux Summer BBQ
UA testing with Selenium and PHPUnit - PHPBenelux Summer BBQUA testing with Selenium and PHPUnit - PHPBenelux Summer BBQ
UA testing with Selenium and PHPUnit - PHPBenelux Summer BBQ
 
Javascript quiz
Javascript quizJavascript quiz
Javascript quiz
 
Graphql, REST and Apollo
Graphql, REST and ApolloGraphql, REST and Apollo
Graphql, REST and Apollo
 
Redux for ReactJS Programmers
Redux for ReactJS ProgrammersRedux for ReactJS Programmers
Redux for ReactJS Programmers
 

Similar to The Sky is Not Falling: Scaling Experimentation for Product Development

The secret unit testing tools no one ever told you about
The secret unit testing tools no one ever told you aboutThe secret unit testing tools no one ever told you about
The secret unit testing tools no one ever told you aboutDror Helper
 
Testing, Performance Analysis, and jQuery 1.4
Testing, Performance Analysis, and jQuery 1.4Testing, Performance Analysis, and jQuery 1.4
Testing, Performance Analysis, and jQuery 1.4jeresig
 
Understanding JavaScript Testing
Understanding JavaScript TestingUnderstanding JavaScript Testing
Understanding JavaScript Testingjeresig
 
How to write clean tests
How to write clean testsHow to write clean tests
How to write clean testsDanylenko Max
 
Tech In Asia PDC 2017 - Best practice unit testing in mobile apps
Tech In Asia PDC 2017 - Best practice unit testing in mobile appsTech In Asia PDC 2017 - Best practice unit testing in mobile apps
Tech In Asia PDC 2017 - Best practice unit testing in mobile appsFandy Gotama
 
"Unit Testing for Mobile App" by Fandy Gotama (OLX Indonesia)
"Unit Testing for Mobile App" by Fandy Gotama  (OLX Indonesia)"Unit Testing for Mobile App" by Fandy Gotama  (OLX Indonesia)
"Unit Testing for Mobile App" by Fandy Gotama (OLX Indonesia)Tech in Asia ID
 
Integration testing - Yasub Hashmi
Integration testing  - Yasub HashmiIntegration testing  - Yasub Hashmi
Integration testing - Yasub HashmiPiyush Rahate
 
Building Smart Async Functions For Mobile
Building Smart Async Functions For MobileBuilding Smart Async Functions For Mobile
Building Smart Async Functions For MobileGlan Thomas
 
2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good TestsTomek Kaczanowski
 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good TestsTomek Kaczanowski
 
1 aleksandr gritsevski - attd example using
1   aleksandr gritsevski - attd example using1   aleksandr gritsevski - attd example using
1 aleksandr gritsevski - attd example usingIevgenii Katsan
 
Scrum Gathering 2012 Shanghai_工程实践与技术卓越分会场:how to write unit test for new cod...
Scrum Gathering 2012 Shanghai_工程实践与技术卓越分会场:how to write unit test for new cod...Scrum Gathering 2012 Shanghai_工程实践与技术卓越分会场:how to write unit test for new cod...
Scrum Gathering 2012 Shanghai_工程实践与技术卓越分会场:how to write unit test for new cod...LetAgileFly
 
Testdrevet javautvikling på objektorienterte skinner
Testdrevet javautvikling på objektorienterte skinnerTestdrevet javautvikling på objektorienterte skinner
Testdrevet javautvikling på objektorienterte skinnerTruls Jørgensen
 
How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014Guillaume POTIER
 
The uniform interface is 42
The uniform interface is 42The uniform interface is 42
The uniform interface is 42Yevhen Bobrov
 
ASP.NET Web API
ASP.NET Web APIASP.NET Web API
ASP.NET Web APIhabib_786
 
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)Jen Wong
 
どう使う? Windows azure テーブル
どう使う? Windows azure テーブルどう使う? Windows azure テーブル
どう使う? Windows azure テーブルAkihiroYamamoto
 

Similar to The Sky is Not Falling: Scaling Experimentation for Product Development (20)

The secret unit testing tools no one ever told you about
The secret unit testing tools no one ever told you aboutThe secret unit testing tools no one ever told you about
The secret unit testing tools no one ever told you about
 
Testing, Performance Analysis, and jQuery 1.4
Testing, Performance Analysis, and jQuery 1.4Testing, Performance Analysis, and jQuery 1.4
Testing, Performance Analysis, and jQuery 1.4
 
Understanding JavaScript Testing
Understanding JavaScript TestingUnderstanding JavaScript Testing
Understanding JavaScript Testing
 
Good Tests Bad Tests
Good Tests Bad TestsGood Tests Bad Tests
Good Tests Bad Tests
 
How to write clean tests
How to write clean testsHow to write clean tests
How to write clean tests
 
Tech In Asia PDC 2017 - Best practice unit testing in mobile apps
Tech In Asia PDC 2017 - Best practice unit testing in mobile appsTech In Asia PDC 2017 - Best practice unit testing in mobile apps
Tech In Asia PDC 2017 - Best practice unit testing in mobile apps
 
"Unit Testing for Mobile App" by Fandy Gotama (OLX Indonesia)
"Unit Testing for Mobile App" by Fandy Gotama  (OLX Indonesia)"Unit Testing for Mobile App" by Fandy Gotama  (OLX Indonesia)
"Unit Testing for Mobile App" by Fandy Gotama (OLX Indonesia)
 
Integration testing - Yasub Hashmi
Integration testing  - Yasub HashmiIntegration testing  - Yasub Hashmi
Integration testing - Yasub Hashmi
 
Building Smart Async Functions For Mobile
Building Smart Async Functions For MobileBuilding Smart Async Functions For Mobile
Building Smart Async Functions For Mobile
 
2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests
 
#ajn3.lt.marblejenka
#ajn3.lt.marblejenka#ajn3.lt.marblejenka
#ajn3.lt.marblejenka
 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests
 
1 aleksandr gritsevski - attd example using
1   aleksandr gritsevski - attd example using1   aleksandr gritsevski - attd example using
1 aleksandr gritsevski - attd example using
 
Scrum Gathering 2012 Shanghai_工程实践与技术卓越分会场:how to write unit test for new cod...
Scrum Gathering 2012 Shanghai_工程实践与技术卓越分会场:how to write unit test for new cod...Scrum Gathering 2012 Shanghai_工程实践与技术卓越分会场:how to write unit test for new cod...
Scrum Gathering 2012 Shanghai_工程实践与技术卓越分会场:how to write unit test for new cod...
 
Testdrevet javautvikling på objektorienterte skinner
Testdrevet javautvikling på objektorienterte skinnerTestdrevet javautvikling på objektorienterte skinner
Testdrevet javautvikling på objektorienterte skinner
 
How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014
 
The uniform interface is 42
The uniform interface is 42The uniform interface is 42
The uniform interface is 42
 
ASP.NET Web API
ASP.NET Web APIASP.NET Web API
ASP.NET Web API
 
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)
 
どう使う? Windows azure テーブル
どう使う? Windows azure テーブルどう使う? Windows azure テーブル
どう使う? Windows azure テーブル
 

More from Optimizely

Clover Rings Up Digital Growth to Drive Experimentation
Clover Rings Up Digital Growth to Drive ExperimentationClover Rings Up Digital Growth to Drive Experimentation
Clover Rings Up Digital Growth to Drive ExperimentationOptimizely
 
Make Every Touchpoint Count: How to Drive Revenue in an Increasingly Online W...
Make Every Touchpoint Count: How to Drive Revenue in an Increasingly Online W...Make Every Touchpoint Count: How to Drive Revenue in an Increasingly Online W...
Make Every Touchpoint Count: How to Drive Revenue in an Increasingly Online W...Optimizely
 
The Science of Getting Testing Right
The Science of Getting Testing RightThe Science of Getting Testing Right
The Science of Getting Testing RightOptimizely
 
Atlassian's Mystique CLI, Minimizing the Experiment Development Cycle
Atlassian's Mystique CLI, Minimizing the Experiment Development CycleAtlassian's Mystique CLI, Minimizing the Experiment Development Cycle
Atlassian's Mystique CLI, Minimizing the Experiment Development CycleOptimizely
 
Autotrader Case Study: Migrating from Home-Grown Testing to Best-in-Class Too...
Autotrader Case Study: Migrating from Home-Grown Testing to Best-in-Class Too...Autotrader Case Study: Migrating from Home-Grown Testing to Best-in-Class Too...
Autotrader Case Study: Migrating from Home-Grown Testing to Best-in-Class Too...Optimizely
 
Zillow + Optimizely: Building the Bridge to $20 Billion Revenue
Zillow + Optimizely: Building the Bridge to $20 Billion RevenueZillow + Optimizely: Building the Bridge to $20 Billion Revenue
Zillow + Optimizely: Building the Bridge to $20 Billion RevenueOptimizely
 
The Future of Optimizely for Technical Teams
The Future of Optimizely for Technical TeamsThe Future of Optimizely for Technical Teams
The Future of Optimizely for Technical TeamsOptimizely
 
Empowering Agents to Provide Service from Anywhere: Contact Centers in the Ti...
Empowering Agents to Provide Service from Anywhere: Contact Centers in the Ti...Empowering Agents to Provide Service from Anywhere: Contact Centers in the Ti...
Empowering Agents to Provide Service from Anywhere: Contact Centers in the Ti...Optimizely
 
Experimentation Everywhere: Create Exceptional Online Shopping Experiences an...
Experimentation Everywhere: Create Exceptional Online Shopping Experiences an...Experimentation Everywhere: Create Exceptional Online Shopping Experiences an...
Experimentation Everywhere: Create Exceptional Online Shopping Experiences an...Optimizely
 
Building an Experiment Pipeline for GitHub’s New Free Team Offering
Building an Experiment Pipeline for GitHub’s New Free Team OfferingBuilding an Experiment Pipeline for GitHub’s New Free Team Offering
Building an Experiment Pipeline for GitHub’s New Free Team OfferingOptimizely
 
AMC Networks Experiments Faster on the Server Side
AMC Networks Experiments Faster on the Server SideAMC Networks Experiments Faster on the Server Side
AMC Networks Experiments Faster on the Server SideOptimizely
 
Evolving Experimentation from CRO to Product Development
Evolving Experimentation from CRO to Product DevelopmentEvolving Experimentation from CRO to Product Development
Evolving Experimentation from CRO to Product DevelopmentOptimizely
 
Overcoming the Challenges of Experimentation on a Service Oriented Architecture
Overcoming the Challenges of Experimentation on a Service Oriented ArchitectureOvercoming the Challenges of Experimentation on a Service Oriented Architecture
Overcoming the Challenges of Experimentation on a Service Oriented ArchitectureOptimizely
 
How The Zebra Utilized Feature Experiments To Increase Carrier Card Engagemen...
How The Zebra Utilized Feature Experiments To Increase Carrier Card Engagemen...How The Zebra Utilized Feature Experiments To Increase Carrier Card Engagemen...
How The Zebra Utilized Feature Experiments To Increase Carrier Card Engagemen...Optimizely
 
Making Your Hypothesis Work Harder to Inform Future Product Strategy
Making Your Hypothesis Work Harder to Inform Future Product StrategyMaking Your Hypothesis Work Harder to Inform Future Product Strategy
Making Your Hypothesis Work Harder to Inform Future Product StrategyOptimizely
 
Kick Your Assumptions: How Scholl's Test-Everything Culture Drives Revenue
Kick Your Assumptions: How Scholl's Test-Everything Culture Drives RevenueKick Your Assumptions: How Scholl's Test-Everything Culture Drives Revenue
Kick Your Assumptions: How Scholl's Test-Everything Culture Drives RevenueOptimizely
 
Experimentation through Clients' Eyes
Experimentation through Clients' EyesExperimentation through Clients' Eyes
Experimentation through Clients' EyesOptimizely
 
Shipping to Learn and Accelerate Growth with GitHub
Shipping to Learn and Accelerate Growth with GitHubShipping to Learn and Accelerate Growth with GitHub
Shipping to Learn and Accelerate Growth with GitHubOptimizely
 
Test Everything: TrustRadius Delivers Customer Value with Experimentation
Test Everything: TrustRadius Delivers Customer Value with ExperimentationTest Everything: TrustRadius Delivers Customer Value with Experimentation
Test Everything: TrustRadius Delivers Customer Value with ExperimentationOptimizely
 
Optimizely Agent: Scaling Resilient Feature Delivery
Optimizely Agent: Scaling Resilient Feature DeliveryOptimizely Agent: Scaling Resilient Feature Delivery
Optimizely Agent: Scaling Resilient Feature DeliveryOptimizely
 

More from Optimizely (20)

Clover Rings Up Digital Growth to Drive Experimentation
Clover Rings Up Digital Growth to Drive ExperimentationClover Rings Up Digital Growth to Drive Experimentation
Clover Rings Up Digital Growth to Drive Experimentation
 
Make Every Touchpoint Count: How to Drive Revenue in an Increasingly Online W...
Make Every Touchpoint Count: How to Drive Revenue in an Increasingly Online W...Make Every Touchpoint Count: How to Drive Revenue in an Increasingly Online W...
Make Every Touchpoint Count: How to Drive Revenue in an Increasingly Online W...
 
The Science of Getting Testing Right
The Science of Getting Testing RightThe Science of Getting Testing Right
The Science of Getting Testing Right
 
Atlassian's Mystique CLI, Minimizing the Experiment Development Cycle
Atlassian's Mystique CLI, Minimizing the Experiment Development CycleAtlassian's Mystique CLI, Minimizing the Experiment Development Cycle
Atlassian's Mystique CLI, Minimizing the Experiment Development Cycle
 
Autotrader Case Study: Migrating from Home-Grown Testing to Best-in-Class Too...
Autotrader Case Study: Migrating from Home-Grown Testing to Best-in-Class Too...Autotrader Case Study: Migrating from Home-Grown Testing to Best-in-Class Too...
Autotrader Case Study: Migrating from Home-Grown Testing to Best-in-Class Too...
 
Zillow + Optimizely: Building the Bridge to $20 Billion Revenue
Zillow + Optimizely: Building the Bridge to $20 Billion RevenueZillow + Optimizely: Building the Bridge to $20 Billion Revenue
Zillow + Optimizely: Building the Bridge to $20 Billion Revenue
 
The Future of Optimizely for Technical Teams
The Future of Optimizely for Technical TeamsThe Future of Optimizely for Technical Teams
The Future of Optimizely for Technical Teams
 
Empowering Agents to Provide Service from Anywhere: Contact Centers in the Ti...
Empowering Agents to Provide Service from Anywhere: Contact Centers in the Ti...Empowering Agents to Provide Service from Anywhere: Contact Centers in the Ti...
Empowering Agents to Provide Service from Anywhere: Contact Centers in the Ti...
 
Experimentation Everywhere: Create Exceptional Online Shopping Experiences an...
Experimentation Everywhere: Create Exceptional Online Shopping Experiences an...Experimentation Everywhere: Create Exceptional Online Shopping Experiences an...
Experimentation Everywhere: Create Exceptional Online Shopping Experiences an...
 
Building an Experiment Pipeline for GitHub’s New Free Team Offering
Building an Experiment Pipeline for GitHub’s New Free Team OfferingBuilding an Experiment Pipeline for GitHub’s New Free Team Offering
Building an Experiment Pipeline for GitHub’s New Free Team Offering
 
AMC Networks Experiments Faster on the Server Side
AMC Networks Experiments Faster on the Server SideAMC Networks Experiments Faster on the Server Side
AMC Networks Experiments Faster on the Server Side
 
Evolving Experimentation from CRO to Product Development
Evolving Experimentation from CRO to Product DevelopmentEvolving Experimentation from CRO to Product Development
Evolving Experimentation from CRO to Product Development
 
Overcoming the Challenges of Experimentation on a Service Oriented Architecture
Overcoming the Challenges of Experimentation on a Service Oriented ArchitectureOvercoming the Challenges of Experimentation on a Service Oriented Architecture
Overcoming the Challenges of Experimentation on a Service Oriented Architecture
 
How The Zebra Utilized Feature Experiments To Increase Carrier Card Engagemen...
How The Zebra Utilized Feature Experiments To Increase Carrier Card Engagemen...How The Zebra Utilized Feature Experiments To Increase Carrier Card Engagemen...
How The Zebra Utilized Feature Experiments To Increase Carrier Card Engagemen...
 
Making Your Hypothesis Work Harder to Inform Future Product Strategy
Making Your Hypothesis Work Harder to Inform Future Product StrategyMaking Your Hypothesis Work Harder to Inform Future Product Strategy
Making Your Hypothesis Work Harder to Inform Future Product Strategy
 
Kick Your Assumptions: How Scholl's Test-Everything Culture Drives Revenue
Kick Your Assumptions: How Scholl's Test-Everything Culture Drives RevenueKick Your Assumptions: How Scholl's Test-Everything Culture Drives Revenue
Kick Your Assumptions: How Scholl's Test-Everything Culture Drives Revenue
 
Experimentation through Clients' Eyes
Experimentation through Clients' EyesExperimentation through Clients' Eyes
Experimentation through Clients' Eyes
 
Shipping to Learn and Accelerate Growth with GitHub
Shipping to Learn and Accelerate Growth with GitHubShipping to Learn and Accelerate Growth with GitHub
Shipping to Learn and Accelerate Growth with GitHub
 
Test Everything: TrustRadius Delivers Customer Value with Experimentation
Test Everything: TrustRadius Delivers Customer Value with ExperimentationTest Everything: TrustRadius Delivers Customer Value with Experimentation
Test Everything: TrustRadius Delivers Customer Value with Experimentation
 
Optimizely Agent: Scaling Resilient Feature Delivery
Optimizely Agent: Scaling Resilient Feature DeliveryOptimizely Agent: Scaling Resilient Feature Delivery
Optimizely Agent: Scaling Resilient Feature Delivery
 

Recently uploaded

Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...shyamraj55
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
Unlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsUnlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsPrecisely
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr LapshynFwdays
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024BookNet Canada
 
Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraDeakin University
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxOnBoard
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Neo4j
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDGMarianaLemus7
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 

Recently uploaded (20)

Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
The transition to renewables in India.pdf
The transition to renewables in India.pdfThe transition to renewables in India.pdf
The transition to renewables in India.pdf
 
Unlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsUnlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power Systems
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
 
Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning era
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptx
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDG
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 

The Sky is Not Falling: Scaling Experimentation for Product Development

  • 1.
  • 2. The Sky is Not Falling: Scaling Experimentation for Product Development Bill Hinderman Web and Experimentation Engineering Lead, Vivid Seats
  • 3. Hi.
  • 4. Hi.
  • 5.
  • 10. In brief. What haven’t I done this? How do I start? How do I grow?
  • 11. What size of team needs to scale experimentation?
  • 12.
  • 13. Every techincal change is a political change. Every technical change is a political change.
  • 14. How do I start?. Pick your tools. Build a roadmap based off of learning.
  • 16.
  • 17. $
  • 18. Build tools that fit testing into your own workflow.
  • 19. Layer of abstraction allows us to skip call Rapid QA cycle Ability to share recipes with stakeholders Easy debugging Built into our own API ?experiment_key=recipe_key
  • 20. private Variation setForcedVariationFromRequest(..., String uuid, String experimentKey) { String parameter = OPTIMIZELY_FORCE_COOKIE_PREFIX + experimentKey; String variationKey = request.getParameter(parameter); if(variationKey == null) { variationKey = cookieService.getCookieValue(request, parameter); } return new Variation(uuid, variationKey); } ?experiment_key=recipe_key
  • 21. private Variation setForcedVariationFromRequest(..., String uuid, String experimentKey) { String parameter = OPTIMIZELY_FORCE_COOKIE_PREFIX + experimentKey; String variationKey = request.getParameter(parameter); if(variationKey == null) { variationKey = cookieService.getCookieValue(request, parameter); } return new Variation(uuid, variationKey); } ?experiment_key=recipe_key
  • 22. private Variation setForcedVariationFromRequest(..., String uuid, String experimentKey) { String parameter = OPTIMIZELY_FORCE_COOKIE_PREFIX + experimentKey; String variationKey = request.getParameter(parameter); if(variationKey == null) { variationKey = cookieService.getCookieValue(request, parameter); } return new Variation(uuid, variationKey); } ?experiment_key=recipe_key
  • 23. Align experiment environments with development environments.
  • 24.
  • 25.
  • 26. When am I done starting?. Testing is part of product & engineering KPIs. Team structure supports efficient experimentation. Experimentation tools become their own product.
  • 27. Decide upon a team structure.
  • 28. Experimentation Search App Checkout UI Components Supplier Integration Experimentation Team Experimentation Feature / Platform
  • 29. Experimentation Search App Checkout UI Components Supplier Integration Decentralized experimentation Experimentation Feature / Platform
  • 30. Experimentation Search App Checkout UI Components Supplier Integration Experimentation Feature / Platform
  • 31. Experimentation Search App Checkout UI Components Supplier Integration Experimentation Consultancy Experimentation Feature / Platform
  • 32. Black box your testing engine.
  • 33.
  • 34.
  • 35.
  • 36. public Variation getVariation(HttpServletRequest request, HttpServletResponse response, String uuid, String experimentKey, Map<String, String> attributes) { try { if (requestUtils.isCustomer(request)) { Cookie optimizelyCookie = cookieService.getCookie(request, OPTIMIZELY_COOKIE_PREFIX + experimentKey); Variation variation; if (attributes == null) { attributes = new HashMap<>(); } if (!attributes.containsKey("device_type")) { attributes.putAll(getBaseAttributes(request)); } String forceParameter = OPTIMIZELY_FORCE_COOKIE_PREFIX + experimentKey; if (request.getParameter(forceParameter) != null || cookieService.doesCookieExist(request, forceParameter)) { variation = setForcedVariationFromRequest(request, response, uuid, experimentKey); } else if (optimizelyCookie != null) { Optimizely optimizelyClient = getOptimizelyClient(); if (optimizelyClient == null) { return null; } String optimizelyUUID = initOptimizelyUUID(request, response, uuid); variation = optimizelyClient.getVariation(experimentKey, optimizelyUUID); if (variation == null) { cookieService.deleteCookie(response, optimizelyCookie.getName(), false); variation = activateExperiment(request, response, uuid, experimentKey, attributes); }
  • 37. public Variation getVariation(HttpServletRequest request, HttpServletResponse response, String uuid, String experimentKey, Map<String, String> attributes) { try { if (requestUtils.isCustomer(request)) { Cookie optimizelyCookie = cookieService.getCookie(request, OPTIMIZELY_COOKIE_PREFIX + experimentKey); Variation variation; if (attributes == null) { attributes = new HashMap<>(); } if (!attributes.containsKey("device_type")) { attributes.putAll(getBaseAttributes(request)); } String forceParameter = OPTIMIZELY_FORCE_COOKIE_PREFIX + experimentKey; if (request.getParameter(forceParameter) != null || cookieService.doesCookieExist(request, forceParameter)) { variation = setForcedVariationFromRequest(request, response, uuid, experimentKey); } else if (optimizelyCookie != null) { Optimizely optimizelyClient = getOptimizelyClient(); if (optimizelyClient == null) { return null; } String optimizelyUUID = initOptimizelyUUID(request, response, uuid); variation = optimizelyClient.getVariation(experimentKey, optimizelyUUID); if (variation == null) { cookieService.deleteCookie(response, optimizelyCookie.getName(), false); variation = activateExperiment(request, response, uuid, experimentKey, attributes); }
  • 38. private Map<String, String> getBaseAttributes(HttpServletRequest request) { Map<String, String> baseAttributes = new HashMap<>(); try { String userAgentString = request.getHeader("User-Agent") == null ? "" : request.getHeader("User-Agent"); UserAgent userAgent = UserAgent.parseUserAgentString(userAgentString); String browserType = userAgent.getBrowser().getName().toLowerCase(); if (browserType.contains("chrome") || browserType.contains("safari")) { browserType = browserType.replaceAll("[^a-z]",""); } String browserVersion = userAgent.getBrowserVersion().getMajorVersion(); String deviceType = userAgent.getOperatingSystem().getDeviceType() == eu.bitwalker.useragentutils.DeviceType.COMPUTER ? "desktop" : userAgent.getOperatingSystem().getDeviceType().getName().toLowerCase(); String platform = userAgent.getOperatingSystem().getName().toLowerCase(); OrderChannel orderChannel = channelService.getActiveChannel(request); String channel = "direct"; if (orderChannel != null && orderChannel.getH() != null) { channel = orderChannel.getH().toLowerCase(); } baseAttributes.put("browser_type", browserType); baseAttributes.put("browser_version", browserVersion); baseAttributes.put("channel", channel); baseAttributes.put("device_type", deviceType); baseAttributes.put("platform", platform); } catch (Exception e) { LOGGER.warn(e.getMessage(), e); } return baseAttributes; }
  • 39. public Variation getVariation(HttpServletRequest request, HttpServletResponse response, String uuid, String experimentKey, Map<String, String> attributes) { try { if (requestUtils.isCustomer(request)) { Cookie optimizelyCookie = cookieService.getCookie(request, OPTIMIZELY_COOKIE_PREFIX + experimentKey); Variation variation; if (attributes == null) { attributes = new HashMap<>(); } if (!attributes.containsKey("device_type")) { attributes.putAll(getBaseAttributes(request)); } String forceParameter = OPTIMIZELY_FORCE_COOKIE_PREFIX + experimentKey; if (request.getParameter(forceParameter) != null || cookieService.doesCookieExist(request, forceParameter)) { variation = setForcedVariationFromRequest(request, response, uuid, experimentKey); } else if (optimizelyCookie != null) { Optimizely optimizelyClient = getOptimizelyClient(); if (optimizelyClient == null) { return null; } String optimizelyUUID = initOptimizelyUUID(request, response, uuid); variation = optimizelyClient.getVariation(experimentKey, optimizelyUUID); if (variation == null) { cookieService.deleteCookie(response, optimizelyCookie.getName(), false); variation = activateExperiment(request, response, uuid, experimentKey, attributes); }
  • 40. public Variation getVariation(HttpServletRequest request, HttpServletResponse response, String uuid, String experimentKey, Map<String, String> attributes) { try { if (requestUtils.isCustomer(request)) { Cookie optimizelyCookie = cookieService.getCookie(request, OPTIMIZELY_COOKIE_PREFIX + experimentKey); Variation variation; if (attributes == null) { attributes = new HashMap<>(); } if (!attributes.containsKey("device_type")) { attributes.putAll(getBaseAttributes(request)); } String forceParameter = OPTIMIZELY_FORCE_COOKIE_PREFIX + experimentKey; if (request.getParameter(forceParameter) != null || cookieService.doesCookieExist(request, forceParameter)) { variation = setForcedVariationFromRequest(request, response, uuid, experimentKey); } else if (optimizelyCookie != null) { Optimizely optimizelyClient = getOptimizelyClient(); if (optimizelyClient == null) { return null; } String optimizelyUUID = initOptimizelyUUID(request, response, uuid); variation = optimizelyClient.getVariation(experimentKey, optimizelyUUID); if (variation == null) { cookieService.deleteCookie(response, optimizelyCookie.getName(), false); variation = activateExperiment(request, response, uuid, experimentKey, attributes); }
  • 41. String optimizelyUUID = initOptimizelyUUID(request, response, uuid); private String initOptimizelyUUID(HttpServletRequest request, HttpServletResponse response, String uuid) { String uuidCookie = cookieService.getCookieValue(request, OPTIMIZELY_COOKIE_PREFIX + "uuid"); if (StringUtils.isBlank(uuidCookie)) { cookieService.setCookie(response, OPTIMIZELY_COOKIE_PREFIX + "uuid", uuid, SECONDS_5_YEARS); return uuid; } return uuidCookie; }
  • 42. String forceParameter = OPTIMIZELY_FORCE_COOKIE_PREFIX + experimentKey; if (request.getParameter(forceParameter) != null || cookieService.doesCookieExist(request, forceParameter)) { variation = setForcedVariationFromRequest(request, response, uuid, experimentKey); } else if (optimizelyCookie != null) { Optimizely optimizelyClient = getOptimizelyClient(); if (optimizelyClient == null) { return null; } String optimizelyUUID = initOptimizelyUUID(request, response, uuid); variation = optimizelyClient.getVariation(experimentKey, optimizelyUUID); if (variation == null) { cookieService.deleteCookie(response, optimizelyCookie.getName(), false); variation = activateExperiment(request, response, uuid, experimentKey, attributes); } } else { variation = activateExperiment(request, response, uuid, experimentKey, attributes); } if (variation != null) { setLogContextData(request); return variation; } } } catch (Exception e) { LOGGER.warn("Unable get variation for experiment key: {}", experimentKey, e); } return null; }
  • 43. private Variation activateExperiment(HttpServletRequest request, HttpServletResponse response, String uuid, String experimentKey) { return activateExperiment(request, response, uuid, experimentKey, new HashMap<String, String>()); } private Variation activateExperiment(HttpServletRequest request, HttpServletResponse response, String uuid, String experimentKey, Map<String, String> attributes) { Optimizely optimizelyClient = getOptimizelyClient(); if (optimizelyClient == null) { return null; } String optimizelyUUID = initOptimizelyUUID(request, response, uuid); Variation variation = optimizelyClient.activate(experimentKey, optimizelyUUID, attributes); if (variation != null) { LOGGER.info("Optimizely SDK: activateExperiment (Java)"); cookieService.setCookie(response, OPTIMIZELY_COOKIE_PREFIX + experimentKey, variation.getKey(), 2592000); } return variation; }
  • 44. private Variation activateExperiment(HttpServletRequest request, HttpServletResponse response, String uuid, String experimentKey) { return activateExperiment(request, response, uuid, experimentKey, new HashMap<String, String>()); } private Variation activateExperiment(HttpServletRequest request, HttpServletResponse response, String uuid, String experimentKey, Map<String, String> attributes) { Optimizely optimizelyClient = getOptimizelyClient(); if (optimizelyClient == null) { return null; } String optimizelyUUID = initOptimizelyUUID(request, response, uuid); Variation variation = optimizelyClient.activate(experimentKey, optimizelyUUID, attributes); if (variation != null) { LOGGER.info("Optimizely SDK: activateExperiment (Java)"); cookieService.setCookie(response, OPTIMIZELY_COOKIE_PREFIX + experimentKey, variation.getKey(), 2592000); } return variation; }
  • 45. Web server-side Web client-side iOS client Android client Partner tools Java full-stack Java full-stack iOS full-stack Android full-stack Partner tools full-stack
  • 46. Web server-side Web client-side iOS client Android client Partner tools The experiment stuff
  • 47. How do I grow?. Grow your appetite. Improve your efficiency.
  • 48. We don’t have the appetite for more experimentation.
  • 49. Create a rubric for good experiment hypothesis.
  • 50. M V M R C Adding a ticket details screen between the tickets list and checkout screen will
 aid users to find a more desirable ticket
 which will ultimately improve conversion.
  • 53.
  • 54.
  • 55. Measurable Visible Meaningful Realistic Cheap Adding a ticket details screen between the tickets list and checkout screen will
 aid users to find a more desirable ticket
 which will ultimately improve conversion.
  • 56. Measurable Visible Meaningful Realistic Cheap Adding a ticket details screen between the tickets list and checkout screen will
 aid users to find a more desirable ticket
 which will ultimately enjoy their event more.
  • 57. Measurable Visible Meaningful Realistic Cheap Adding a ticket details screen between the tickets list and checkout screen will
 aid users to find a more desirable ticket
 which will ultimately improve conversion.
  • 58. Measurable Visible Meaningful Realistic Cheap Adding a ticket details link in the post-checkout receipt email will aid users to find a more desirable ticket which will ultimately improve conversion.
  • 59. Measurable Visible Meaningful Realistic Cheap Adding a ticket details screen between the tickets list and checkout screen will
 aid users to find a more desirable ticket
 which will ultimately improve conversion.
  • 60. Measurable Visible Meaningful Realistic Cheap Adding a ticket details screen between the tickets list and checkout screen will
 aid users to view the venue’s code of conduct which will ultimately improve conversion.
  • 61. Measurable Visible Meaningful Realistic Cheap Adding a ticket details screen between the tickets list and checkout screen will
 aid users to find a more desirable ticket
 which will ultimately improve conversion.
  • 62. Measurable Visible Meaningful Realistic Cheap Adding a ticket details screen that offers free tickets will aid users to find a more desirable ticket which will ultimately improve conversion.
  • 63. Measurable Visible Meaningful Realistic Cheap Adding a ticket details screen between the tickets list and checkout screen will
 aid users to find a more desirable ticket
 which will ultimately improve conversion.
  • 64. Measurable Visible Meaningful Realistic Cheap Building a ticket details app will aid users to view find a more desirable ticket which will ultimately improve conversion.
  • 65. Measurable Visible Meaningful Realistic Cheap Adding a ticket details screen between the tickets list and checkout screen will
 aid users to find a more desirable ticket
 which will ultimately improve conversion.
  • 66. We don’t have the bandwidth for more experimentation.
  • 67.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 78. private Resolution getTicketPageView(UserAgent userAgent) { try { getContext().getResponse().addHeader("Vary", "User-Agent"); String web4801VariationKey = initVariation(WEB_4801_EXPERIMENT_KEY); boolean isWEB4801Control = !"variant_1".equals(web4801VariationKey); boolean showMobileView = userAgent.getDeviceType() == DeviceType.MOBILE || userAgent.getDeviceType() == DeviceType.TABLET; if (!isMobileViewOverride() && showMobileView) { if(!isWEB4801Control) { initVariation(WEB_4304_EXPERIMENT_KEY); return new ForwardResolution(JSP_PATH + RESPONSIVE_TICKET_PAGE_VIEW); } return new RedirectResolution(MobileProductionAction.class).addParameter(RequestAttributeConstants.PRODUCTION_PRODUCTION_ID, production.getProductionId()); … String web4456Variation = initVariation(WEB_4456_EXPERIMENT_KEY); boolean isWEB4456Control = !"variant_1".equals(web4456Variation) && !"variant_2".equals(web4456Variation); if (!isWEB4456Control) { initVariation(WEB_4304_EXPERIMENT_KEY); return new ForwardResolution(JSP_PATH + RESPONSIVE_TICKET_PAGE_VIEW); }
  • 79. Empower & trust your center of excellence.
  • 80.
  • 81. Every techincal change is a political change. Every technical change is a political change.
  • 82. Let’s review. Define your team’s optimization goal Couple technology and process Mandate measurable hypotheses By hyperanalytical Trust your center of excellence
  • 88. Victor Forman linkedin.com/in/victor-forman/ Andy Caples linkedin.com/in/andy-caples-970b35/ John Banta linkedin.com/in/bantaj/ Shanu Pant linkedin.com/in/shanu-pant-093a807/ Smart people.