【譯】用React和PlanOut來做產品的AB測試

React

攻城師,設計師和產品經理每天為了達到一些KPI做一些盲目的決策。我們想去重新設計一個頁面因為我們相信這樣可以提升用戶體驗和提高用戶的留存。我們想要去增加一個品牌的新特性或者擴展一個已有的特性因為我們相信通過使產品對用戶更有價值可以提高產品的可用性。

每一個決策最終歸結于這個問題:這個變更真的會帶來預期的效果嗎?這又可以歸結為因果性推斷:決策X能導致結果Y的發生嗎?在Web應用中,我們可以使用金本位制(以黃金為本位幣的貨幣制度)用來回答因果性推斷的問題:隨機化實驗英文解釋)。

在大型的公司里面,市場,銷售和售前售后支持都能影響的用戶的行為,這樣把實驗當作一個工具去做產品級的決策尤為重要。這套實驗方法,從某種程度上來講,可以幫你把其他各式各樣的因素給排除掉。最終表明,這套實驗方法在Sidekick小組(這篇文章出自于Hubspot Sidekick小組)中處于產品的核心地位。我們在不斷迭代的使用這種方式這樣我們的用戶能容易的理解而且從Sidekick獲取價值。這套實驗方法幫助我們實現快速變革而且對于一個功能的生命周期的各個階段都有更多的自信。它還使我們更理解我們的用戶,而且能把這些理解應用于該產品的其他部分。最重要的是,它幫助我們細致的打造著產品的未來。

找尋一套實驗框架和解決方案

要開啟這套實驗進程,在需要通過公司實現它的規模和確保測試的可統計性的同時,還需要有工具讓它容易的去安裝,部署和分析實驗結果。在我們看來,這套實驗框架應該具有如下的特性:

  1. 針對一個特定的實驗,用戶總是收到相同的參數值。除了帶來糟糕的用戶體驗,不同的會話指定不同的參數值也會使之從實驗中獲得有價值的東西變得非常困難。
  2. 關心該實驗框架的全流程實驗日志記錄,這樣實驗的分析過程將是自然的,可預測的而且無Bug產生(這里的無Bug是指根據log很容易的查出Bug)。全流程日志記錄用一種簡單的記錄方法用來記錄參與實驗的用戶和用戶在該實驗中受到的對待。
  3. 實驗規模取決于你實施了的實驗數量和基于用戶的產品增長。
  4. 實驗的設計應該拋開復雜的設計,應該是可執行性強的而且能容易的被公司里的每個人理解。
  5. 這套框架要確保一些實驗不會拖拉的時間太長導致代碼庫做無用的增長。

根據上述這些限制,我們開始使用Facebook的PlanOut實驗框架(用Python編寫)。我們發現PlanOut有著不可思意的作用,但是當我們慢慢的把基礎框架從服務端實現轉換成客戶端渲染時,我們發現把實驗信息,參數還有實驗日志從服務端傳輸到客戶端時,變得笨重,慢還導致產生了一些可避免的Bug。

為了適應框架的變化,我們決定放棄使用Python的部署,而使用基于JavaScript的部署。結果,我們做了一套基于JavaScript的PlanOut部署框架[Github],該框架能讓我們在客戶端定義實驗和管理實驗。這套部署框架相對于服務端的部署有很多的好處。第一,自從我們無需把實驗參數從服務端傳送到客戶端后基本沒有性能瓶頸。自從我們深刻的認識到性能上的下降能影響到我們的一些重要指標后這一點顯得非常重要。

第二,基于同一套代碼定義和實施用戶界面的實驗變得更容易而且很少出現Bug,我們現在運行的大部分實驗都是這樣。自從PlanOut部署更適應我們之后把實驗集成在標準的單頁面APP里面變得非常容易。每一個實驗為了實施合適的隨機化實驗都需要一套輸入的集合,但是如果在服務端部署PlanOut,在實驗初始化時就需要把參數定義好。我們的部署允許輸入在單頁面程序引導時注冊而非初始化時,這樣可以允許我們的一些實驗類的初始化和實驗輸入的注冊進行分離。這樣可以很容易的使各種擴展的服務相互作用,而且能最小化日志的重復。

React Experiments

用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已經把數據化驅動做得非常好,是大部分公司都應該學習的榜樣。

[查看原文]

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容