攻城师,设计师和产品经理每天为了达到一些KPI做一些盲目的决策。我们想去重新设计一个页面因为我们相信这样可以提升用户体验和提高用户的留存。我们想要去增加一个品牌的新特性或者扩展一个已有的特性因为我们相信通过使产品对用户更有价值可以提高产品的可用性。
每一个决策最终归结于这个问题:这个变更真的会带来预期的效果吗?这又可以归结为因果性推断:决策X能导致结果Y的发生吗?在Web应用中,我们可以使用金本位制(以黄金为本位币的货币制度)用来回答因果性推断的问题:随机化实验(英文解释)。
在大型的公司里面,市场,销售和售前售后支持都能影响的用户的行为,这样把实验当作一个工具去做产品级的决策尤为重要。这套实验方法,从某种程度上来讲,可以帮你把其他各式各样的因素给排除掉。最终表明,这套实验方法在Sidekick小组(这篇文章出自于Hubspot Sidekick小组)中处于产品的核心地位。我们在不断迭代的使用这种方式这样我们的用户能容易的理解而且从Sidekick获取价值。这套实验方法帮助我们实现快速变革而且对于一个功能的生命周期的各个阶段都有更多的自信。它还使我们更理解我们的用户,而且能把这些理解应用于该产品的其他部分。最重要的是,它帮助我们细致的打造着产品的未来。
找寻一套实验框架和解决方案
要开启这套实验进程,在需要通过公司实现它的规模和确保测试的可统计性的同时,还需要有工具让它容易的去安装,部署和分析实验结果。在我们看来,这套实验框架应该具有如下的特性:
- 针对一个特定的实验,用户总是收到相同的参数值。除了带来糟糕的用户体验,不同的会话指定不同的参数值也会使之从实验中获得有价值的东西变得非常困难。
- 关心该实验框架的全流程实验日志记录,这样实验的分析过程将是自然的,可预测的而且无Bug产生(这里的无Bug是指根据log很容易的查出Bug)。全流程日志记录用一种简单的记录方法用来记录参与实验的用户和用户在该实验中受到的对待。
- 实验规模取决于你实施了的实验数量和基于用户的产品增长。
- 实验的设计应该抛开复杂的设计,应该是可执行性强的而且能容易的被公司里的每个人理解。
- 这套框架要确保一些实验不会拖拉的时间太长导致代码库做无用的增长。
根据上述这些限制,我们开始使用Facebook的PlanOut实验框架(用Python编写)。我们发现PlanOut有着不可思意的作用,但是当我们慢慢的把基础框架从服务端实现转换成客户端渲染时,我们发现把实验信息,参数还有实验日志从服务端传输到客户端时,变得笨重,慢还导致产生了一些可避免的Bug。
为了适应框架的变化,我们决定放弃使用Python的部署,而使用基于JavaScript的部署。结果,我们做了一套基于JavaScript的PlanOut部署框架[Github],该框架能让我们在客户端定义实验和管理实验。这套部署框架相对于服务端的部署有很多的好处。第一,自从我们无需把实验参数从服务端传送到客户端后基本没有性能瓶颈。自从我们深刻的认识到性能上的下降能影响到我们的一些重要指标后这一点显得非常重要。
第二,基于同一套代码定义和实施用户界面的实验变得更容易而且很少出现Bug,我们现在运行的大部分实验都是这样。自从PlanOut部署更适应我们之后把实验集成在标准的单页面APP里面变得非常容易。每一个实验为了实施合适的随机化实验都需要一套输入的集合,但是如果在服务端部署PlanOut,在实验初始化时就需要把参数定义好。我们的部署允许输入在单页面程序引导时注册而非初始化时,这样可以允许我们的一些实验类的初始化和实验输入的注册进行分离。这样可以很容易的使各种扩展的服务相互作用,而且能最小化日志的重复。
用PlanOut去测试传播特性
一个案例是我们在SideKick上尝试去影响传播邀请发送的质量和数量。作为Sidekick的免费用户,如果他发送注册邀请给他的朋友,而且他的朋友接受了邀请,他们都会获得一个月的无限制的通知服务。一个使用户发送邀请的巨大驱动力是该组件(邀请发送组件)能让用户一键邀请。我们的这个实验是通过改变该组件的相关参数能否让用户发送更多的邀请。在该实验中我们不想让付费用户参与,所以我们把付费用户排除在外(也不会对他们进行日志记录)。我们对免费用户进行了2x3的实验(两个测试点,一个测试点有2种属性,另一个测试点有3种属性),这两个点是建议邀请发送的数目和显示给邀请方账户的建议话述。我们把建议邀请发送的数据做为测试点是为了去测试真正发送邀请的数据是否会按照建议的那样去增长还是是否存在一个点这个数字的建议对实际的发送量根本无影响或者还有负面的影响。另外一个测试点是在邀请按钮上显示两种不同的文字,上面显示一种是自私的动机(邀请)和无私的动机(礼物)。代码如下:
//InviteSuggestionsExp.js hosted with ❤ by GitHub
class InviteSuggestions extends Experiment {
//do some logger configuration
setup() {
this.setName(‘InviteSuggestions’);
}
assign(params, args) {
if (!args.paying_user) {
return false;
}
params.set(‘suggestionCount’, new PlanOut.Ops.Random.UniformChoice(choices=[3, 4, 5], unit=user_id));
params.set(‘inviteText’, new PlanOut.Ops.Random.UniformChoice(choices=[‘Invite’, ‘Gift’], unit=user_id));
}
};
执行一项测试需要定义实验和进行适当的部署,我们会需要一种方式自然的从实验定义到实验的部署。在HubSpot大部分的客户端Javascript应用我们使用的React框架实现其View层,自从我们开始实施一些实验,我们自然的找到了一种方式在React框架上无逢的实施实验。结果是产生了一个小型的库 [react-experiments]。
React实验系统介绍
这个库的核心是它建立一个单对单的映射,从PlanOut的实验参数映射到React组件的Props(React里从父节点传递到子节点的数据称为 props,是属性(properties)的缩写),而且为了一个特定的用户用随机化实验的分配当作合适的组件的props。这把PlanOut的参数当作变量优雅的集成了进来。这个结果让定义和部署间的连接变更更易理解,使得不至于有难以捉摸的Bug让实验失效,而且使实验不断的实施而不是不断的积累无用的东西。让我们来看看实施上文提到的实验有多容易用React。
这是实施实验之前的代码:
//invite-suggestions.jsx hosted with ❤ by GitHub
const InviteSuggestions = React.createClass({
getDefaultProps() {
return {
numSuggestionsToShow: 0,
inviteButtonText: null
};
},
renderInviteSuggestions() {
const suggestions = this.props.suggestions.slice(0, this.props.numSuggestionsToShow);
return suggestions.map((suggestion) => {
return (
<InviteSuggestion
inviteButtonText={this.props.inviteButtonText}
suggestion={suggestion}
/>
);
});
},
render() {
if (!this.props.numSuggestionsToShow) { return null; }
return (
<div>
{this.renderInviteSuggestions()}
</div>
);
}
});
export default InviteSuggestions;
现在,去实施该实验需要做的是用下面的代码替换上面代码的最后一行
//invite-suggestions-exp.jsx hosted with ❤ by GitHub
export default parametrize(InviteSuggestionsExperiment, ['suggestionCount', 'inviteText'], InviteSuggestions);
该库的基础是一个参数化的组件,它使其余的库的能力更有力。比如,一个高序的参数化组件当所有的props在同一个组件当中时是一个非常方便的封装。当你想在一个组件中实施一个实验而又发现感兴趣的props又在子组件当中时,你可以直接使用参数化组件的基类,联合子组件相关的高序附着实验参数组件一起使用去提供心要的props的参数化。下面是一个利用参数化组件随同高序附着实验参数组件的实例。
附着实验参数组件和高序参数化组件都为实验提供了强大的抽象性。在某些情况下,我们想去做完整的重新设计在不同的设计之间去设置不同区间的参数。对于这些实验,react实验系统提供了一套AB测试组件,提供一套方法在你的组件当中去定义不同的参数。这些开放的API能显而易见的让读者通过代码发现可以通过实验发现若干变量和这些变量导致的行为之间的关系。这个组件,类似于高序的组件,同时又是一个基于参数化组件的非常方便的封装。
带着实验性倾向的产品设计和研发对于任何产品团队都是一种资产,而且能让产品团队把关注点放在正确的事情上还能快速的进行迭代研发。我们发现用PlanOut.js和react实验系统实施实验有很多的优点,我们希望你能用我们提供的开源项目做和我们一样的事情。[在GitHu上的项目]
译者述
React是一套Facebook研发的前端开源框架,只负责View层
PlanOut是Facebook研发的一套AB测试框架,也是开源项目[GitHub PlanOut]
本人非前端攻城师,所以可能有些地方翻译的有些问题欢迎指出,非常感谢。AB测试这个概念很早就已提出,但是是Facebook把他用得炉火纯青,现在个人有点崇拜Facebook,今天还看了覃超的文章,觉得Facebook已经把数据化驱动做得非常好,是大部分公司都应该学习的榜样。