本文由本人翻译自Indeed官方blog,如果有转载请注明出处。
* 每个测试模型都有一个testType。testType最常用的类型是USER类型,后面会介绍更多关于testType的东西。
根据JSON定义的参数测试生成Java代码
Proctor的每个测试都是独立的,每个测试都会分配有一个salt(盐),这个salt和输入的测试类型(identifier)一起使用,通过一致性哈希算法分配到不同的测试中去。salt的使用,给我们带来两大特色:
原文地址:http://engineering.indeedblog.com/blog/2014/06/proctor-a-b-testing-framework/
写在阅读前,关于如何阅读本文
首先我们要对A/B Test有一个基本的了解,几个重要的概念是:标签项(测试要用到的测试项,也即根据什么来决定将当前请求分配到哪个测试中),测试模型(即共有几个测试,每个测试的分布比例是什么)
翻译本文的目的是为大家提供一种参考,
我们可以基于本文设计自己的A/B Test。我在小米就基于本文设计了使用于自己项目的A/B Test框架,以后有机会给大家分享一下。
正文:
Indeed公司的职责就是帮客户找到工作。我们也经常问自己一个问题:“对于我们的客户---求职者来说,怎么样做对他们才是最好的?” 我们的答案是:对于所有的事,都做出评测和测试,然后再给出答案。在Indeed的开发中,我们努力对每个新功能和新的改进做出评测,同时,我们还会评测这些改动带来的效果,以确保我们的产品会越来越好。
在2013年10月份,Tom Bergman 和 Matt Schemmel 在 IndeedEng展示会上向大家展示了Indeed的A/B Test框架。这个框架现在已经开源了,项目名字叫Proctor,大家可以在下面的链接看到:https://github.com/indeedeng/proctor ,同时我们还开源了Proctor Webapp, 这个web应用用来进行A/B Test 参数的配置。
在这个会议上,Tom 首先给出了一个使用A/B Test的例子,这个例子讲的是,在某个(Web)应用中改变一个按钮的颜色会不会提高用户体验。在下面的图中,大家可以看到,左边的A是没有改变颜色时的按钮,右边的B是改变颜色后的按钮。
在Indeed公司,我们把所有的用户行为都用日志记录下来,这样我们就可以分析、学习和提高我们的产品。对于上面提到的例子,我们会把用户访问Indeed后点击按钮前后的行为记录下来。然后,我们用分析工具进行分析,用数据来证明改变前后,是否带来了更多的搜索量,是否让用户使用时间更长等。
在上面的例子中,我们仅给出了一个可选择的颜色改变,但是在实际的测试中,通常会有多个可选项有待评测。接下来我们会扩展上面例子中的颜色,扩展成多种颜色的测试。
我们可以同时测试多种可选项,在下图中,我们同时测试按钮的文本和颜色。这中测试通常被成为“多元测试”。
我们在Indeed做A/B Test有好几年了,这其中积累的经验指导了Proctor项目的开发。在2013年10月份的会议中,我们不仅介绍了A/B Test,还详细介绍了A/B Test框架-----Proctor的设计决策以及如何使用它。在这篇博客中,我们集中介绍Proctor的关键问题和概念,以及如何使用Indeed A/B Test框架---Proctor。
Proctor的功能和概念
测试项的标准表示形式
Proctor 定义了一个标准的JSON格式来表示测试项(见下图),而且这些测试项的定义是和代码无关的。我们称呼这些测试项为测试模型。每一个测试模型可以以一个单独的文件的形式分布在不同的项目中,这中形式保证了多个项目之间的一致性和灵活性。下图展示了一个简单的按钮测试的测试模型。在这个测试模型中,50%的用户被分到测试A(bucket 0)中,另外50%的用户被分到测试B(bucket 1)中。
上图是Proctor的一个简单的测试模型。为了理解上面的模型,接下来我们会对它进行一个简要介绍以介绍Proctor的一些专有名词。"buttontst"
: {
"description"
:
"backgroundcolortest"
,
"salt"
:
"buttontst"
,
"buckets"
: [
{
"name"
:
"control"
,
"value"
: 0,
"description"
:
"current button treatment (A)"
},
{
"name"
:
"altcolor"
,
"value"
: 1,
"description"
:
"test background color (B)"
}
],
"allocations"
: [
{
"ranges"
: [
{
"length"
: 0.5,
"bucketValue"
: 0
},
{
"length"
: 0.5,
"bucketValue"
: 1
}
]
}
],
"testType"
:
"USER"
}
* 每个测试模型都是包括一个buckets(桶)列表和一个allocations(分布)列表。
* 每个bucket(桶)都是由一组变量组成的,包括name、value和description。
* allocations是对buckets中的每个区域分布的描述。在buckets中的每个区域中,分布的概率都介于0到1之间。buckets中的value和allocations中的bucketValue是对应的。allocations中所有的bucketValue的值的和必须等于1。我们可以基于一定的规则定义多个allocations,后面会详细介绍。
Proctor的Webapp
通过使用Webapp,我们可以在web浏览器中配置和部署测试模型。
我们可以有多种方式标准化我们的应用,可以将我们的应用和以下的系统集成:
* 版本控制系统,可以保证我们能跟踪测试的历史变更
* 问题跟踪系统,可以用来管理和改进测试的工作流程
* 其他系统,比如自动化部署系统
下图是Webapp的一个页面截图:
前面我们也提到了,测试的参数是在JSON文件中指定的,是跟应用独立存在的,应用可以指定使用那个JSON文件来定义buckets和测试项。在java代码build过程中生成java代码,并在运行时加载相应的测试模型的内容。
代码生成功能是可选的,它提供了编译时类型安全的功能,所以在我们的代码中不会出现随处定义的包含测试名称或者bucket名称的字符串。一些通用的类也让java中的测试或者一些模版语言的测试更简单,在下图中就展示了JSP的一个例子。而且,生成的java对象,支持测试模型的序列化,比如序列化成JSON或者XML。
基于上下文规则的allocations<
c:if
test
=
"${groups.buttontstAltColor}"
>
.searchBtn { background-color: #2164f3; color: #ffffff; }
</
c:if
>
Proctor的规则定义语言,提供了一个功能,我们可以在运行时根据上下文参数来确定测试模型的分布比例。例如,你可以定义,你的测试仅仅对一部分确定的用户有效,或者你可以让测试比例根据某一部分的比例自动变化。比如,在某个国家,你可以分配50%的用户在测试A中,另外50%的用户在测试B中;而在另一个国家,你可以设置测试A、测试B、测试C和测试D分别占25%的比例。基于规则的测试分配提供了很大的便利性。在下面的例子中,对于US English的用户,分布分别是50% / 50%,而对于其他用户分布是100%。
测试模型还可以装载其他有效信息(Payloads)"allocations"
: [
{
"rule"
:
"'US' == country && 'en' == userLanguage"
,
"ranges"
: [
{
"length"
: 0.5,
"bucketValue"
: 0
},
{
"length"
: 0.5,
"bucketValue"
: 1
}
]
},
{
"rule"
:
null
,
"ranges"
: [
{
"length"
: 1.0,
"bucketValue"
: -1
}
]
}
]
在测试模型的buckets中还可以定义payloads,来装载一些有效的信息,这些信息可以提供给我们的程序使用。在下面的两个图中,我们在buckets中指定了不同bucket中的按钮的颜色,而且在我们的模版中使用这个颜色。在这个代码中,总的颜色数量是没有减少的,但是却缩减了代码的数量。
"buckets"
: [
{
"name"
:
"control"
,
"value"
: 0,
"description"
:
"current button treatment (A)"
,
"payload"
: {
"stringValue"
:
"#dddddd"
}
},
{
"name"
:
"altcolor"
,
"value"
: 1,
"description"
:
"test background color (B)"
,
"payload"
: {
"stringValue"
: "
#2164f3″
}
}
]
灵活的测试类型(Identifier)<
style
>
.searchBtn { background-color: ${groups.buttontstPayload}; }
</
style
>
对于测试的类型,Proctor有非常灵活的支持。buckets的分布可以是基于user的(通常表示为cookie等)、id、email或者是基于完全随机的请求。你还可以基于Proctor自己扩展测试类型。通常用户自定义的测试类型是比较实用的,比如我们可能想使用上下文中的某个属性来作为测试类型,比如url或者类别。
无偏见的、独立测试
在一个测试中,新建一个bucket,Proctor能使用一致性哈希算法将输入的一个测试类型(比如user id)匹配到一个integer上。每个bucket的分配的integer的区间决定了每个bucket的分布区间。下图展示了一个简单的例子,在这个例子中,分为两个测试项,每一个的分布比例都是50%。
1. 通过共享salt,不同的测试可以实现人为的bucket对齐(共享salts时,必须以&开头)。
2. 通过改变salt的值,我们可以改变一个测试的分布的位置,使用不同的salt,可以对同样的输入产生完全不同的哈希结果。这也是为了保证每个测试分布的无偏见、公平性。
Proctor在Indeed的地位
在Indeed的产品开发过程中,Proctor是数据驱动方法中很重要的一部分。目前线上产品有100个测试,300个测试项存在。在接下来的博客中,我们还会介绍如何关于如何使用Proctor的详细步骤。