古語有云:"一鼓作氣,再而衰,三而竭"。現在我一鼓作氣,寫完最后兩篇。這篇是下篇,另外下周還要發一篇叫番外篇。插播一句自己喜歡的話:
設計軟件有兩種方法: 一種是簡單到極致而明顯沒有缺陷; 另一種是復雜到極致以至于沒有明顯的缺陷。前者要難得多! ?——C.A.R.Hoare
下篇開始!
灰度發布 和 A/B testing
如果看過中篇的朋友應該對于里面Facebook首頁和人人網首頁改版的例子印象深刻。這里Facebook使用的一大法寶就是灰度發布和A/B testing。這一利器像宙斯盾一樣(想不出好的比喻),多次將Facebook從出錯的懸崖上拉回來。就好像上一篇里面所說的那樣:
即使像 Facebook 這樣的航母,在創業的大海里還是猶如“盲人”一樣,很多產品的改動沒人真正知道方向到底在哪兒。所以這里采用的方式就是 "Everything must be tested"。在灰度發布后,data dashboard + A/B testing 就猶如航母上的雷達或者聲納一樣,對于方向和航線起到驗證作用。
所以來重點介紹一下Facebook的這臺雷達系統。
Facebook早在2007-2008年就在網頁服務端(PHP)上開發了這套 發布和測試系統,代號叫 GateKeeper(最早在Boz的文章中提到),本質上它就是一個開關,可以在一個admin page上定義一個個的開關,然后控制某些開關到底是開還是關。這些開關的屬性預先都緩存在內存中,所以讀取開關的操作不重。示例代碼如下:
主要的邏輯就在 if 中,判斷這個開關是否對相應的用戶開啟,如果是則跑實驗代碼,否則跑老代碼。非常直接和簡單對吧!后來,Facebook又陸陸續續對它進行了各種加強,讓其可以更加精細地分割和控制用戶,比如說 對于US的1%用戶開放,或者對于 日本的年齡25歲以下的男性用戶開放,等等。可以從時間,國家,加入日期,好友數,是否為FB員工,性別,年齡等等各種維度進行控制。
這極大方便了我們對于用戶分批進行 A/B testing。Gatekeeper(簡稱GK)對于Facebook的整個internal tools組來說一個很重要的基礎設施。隨著Facebook用戶量的增大,每個GK每日的被訪問量也大大增加;同時Facebook自己的功能和相應的GK項也不斷增加, 這對于整個GK的規模能力后來也提出了很大的挑戰。
到了移動時代后,iOS和Android的 core team 也相應地推出了比較強大的移動灰度發布和 A/B testing 工具,代號叫做 Airlock,其中我們的一位中國工程師也參與它的開發。當然,類似的工具還有不少,比如 Twitter 開源的 Clutch IO。移動上的 Airlock 系統稍微復雜一點:
1. 首先,用戶在手機上登錄或者打開Facebook App后,airlock會從FB server取得所有的GK值;然后在本地緩存起來;
2. 然后在 iOS 或者 Android 代碼里寫相同 if 判斷邏輯,來檢查當前用戶是否已經開啟這個屬性。是的話,跑試驗代碼;否則跑老代碼;
3. 然后app每隔一段時間去和server同步一次(FB用的是一個小時的間隔);當然app隨時也可以強制去取server上的最新值。
4. 最關鍵的是:移動端的logging會把當前用戶的每個GK的值記錄在logging中,這樣當這些logs被上傳到server后, server可以根據這些logs來統計用戶的GK值和相應的動作。
回顧來看,移動上的灰度發布和 A/B testing 本質就是要在本地代碼加入一個庫,來負責和server同步所有開關的值,以及在logs里記錄好相應的這些開關,便于后來分析用戶的行為,來了解此用戶是暴露在怎樣的開關組合中。
案例一:Facebook iOS app的演化
下面來說一下iOS下面的Facebook app界面演化過程。眾所周知,Zuck 和 Steve Jobs 的私交一直不錯,Zuck 也把 喬幫主 當做自己的模樣,私下里經常去喬老爺家里共進晚餐和請教 run company 的竅門。所以,用 iPhone 第一版SDK開始,Facebook就有iOS原生應用開始入駐App Store:
可以看到iOS還是擬物化的風格,Facebook用的是當年紅極一時的九宮格首頁。消息提示在首頁的最下面,當時Facebook還沒有 Like button,只可以comment。此版本的Facebook app為最初一版,由一大牛獨自完成。此大牛把后來的常用組件開源成 Three20 庫。
后來Facebook經過一次大的UI改變:
最大的變化就是九宮格改為了左側抽屜式的導航欄,左上角出現著名的”Hanburger”按鈕。
于是來到2013年,Facebook app準備進行新一輪的大改版,由左側抽屜式改為 Tab bar 格式:
這一版的改動,在意義上主要是讓用戶可以更加方便切換到news feed以外的其他功能區;可是卻引發了另外一個問題:到底下面的tab bar放幾個按鈕?每一個位置上應該放什么?
此時已經是2013年,前一年在Facebook在WWW首頁的改版失敗依然影響著公司的engineering team,于是在iOS app決定更偏好保守和務實。Airlock在這次的改版上起到巨大的作用。Facebook iOS core team的人寫好了tab bar的代碼后,并沒有馬上發布給所有用戶,而是開始了長達4個月的灰度發布和A/B testing;測試了下面 tab bar 各種可能的情況:比如 5個tab項 或者 4個?
第二個放 Requests or Messages? Notifications 或者 Groups 暴露在外面? 同時對于右上角的按鈕的功能和樣式也進行了測試,比如是放 通訊錄 還是 Messages? 是放一個 圖標 還是 直接寫字 等等。一度因為出現新的測試組合,以及對于好幾個組合的測試結果在數據上的比較模棱兩可而把發布時間一拖再拖。整個iOS app界面重組的項目由 Mick Johnson 主導,他是我見過執行力最強的Facebook的幾個PM之一。
他認真審視了各種組合的數據后,結合Facebook當時要大力推行Messenger的大背景,最后確定上圖的組合順序。這個組合在各種測試中數據的綜合表現最好,能夠有效地讓用戶查看news feed,增加用戶的好友數(好友數是從Facebook data組里試驗出來的一個非常重要的指標,這在文章最后可以和大家介紹),方便地收發消息,以及查看新消息,但是 groups,events,還有其他一些輔助功能被藏入了 “more” 之中。
案例二:Voice message 的發布過程
下面拿我之前負責的 voice message(語音消息)功能在Facebook messenger中的發布來分解一下整個Facebook 灰度發布和 A/B testing 的過程:
同理,在 iOS messenger 中,用戶一登錄后(以及以后的每一個小時),iOS messenger client都會和server通信一次,拿到所有 gatekeeper(控制屬性)的值,然后緩存在本地。Messenger上重要的新功能在發布之前都要放在一個GK(gatekeeper)后面,根據Server端的設置來控制每個功能在 網頁,iOS和Android端 的打開或者關閉,然后通過控制GK開啟的范圍(用戶的百分比)來實現功能逐步開放給所有的用戶。
整個功能的發布過程分解如下:
1.準備階段:寫好之后代碼都已經在App里,且提交給app store審核通過,但是GK為關閉狀態。等到上線后,先看擁有版本X的代碼的App在關閉情況下的表現;這時程序的這塊功能邏輯應該和上一個版本保持一致,并且沒嚴重的不穩定性。
2. 員工測試階段:先將其GK開啟到 Employee 20%~50% (對于普通用戶仍然關閉),看在員工群體里新功能的表現情況。一般這個過程是幾天,甚至是幾周。我們把功能開啟的員工群叫做 實驗組,功能仍然關閉的叫做 參照組。對比兩組群體的核心數據表現,比如Facebook App的話一般是看用戶的session length(App使用時長),news feed engagement(like或者comment數量),廣告顯示時長和收入指標 等;而Messenger則是看 平均發消息數,消息收發的耗時;另外對于性能方面相關的功能還會看:App啟動時間,核心功能打開時間和App耗電量等數值。一般說來,對于重要功能,會在發布前就會對這些數據的變化進行一個預測,對于不符合預期的變化會重點排查。
3. 小范圍發布階段:等到在員工群體里,新功能被證明是有效,穩定,無害的時候,Facebook會將GK開啟到1%~5%的用戶范圍。這里主要是兩點考慮:
a) 對系統進行壓力測試,對于一些新的后臺功能(比如:語音消息,VoIP電話,視頻聊天,或者 轉賬功能),看服務器是否能承受地住這么大的用戶流量。一般來說:5%,10%,30%,50% 是常見的幾個壓力測試坎,過了50%基本上沒有問題;
b) 看用戶和媒體對于這個功能的評價。一般來說,PM會收集TechCrunch,VentureBeat,Wired上面的媒體評測,發到內部群里給大家分享;也有在Twitter上面搜索用戶對此功能的討論。
4. 全部發布階段:等到服務器確認能抗住所有流量,這時會將GK開到95~98%的用戶,同時依然保留2%的參照組作為對比用。這個階段最重要的是看 data dashboard 上的各種監控數據,看是否和其他預期的一樣,至少一些關鍵的指標不能出現下滑。
5. 收尾階段:技術人員開始修改程序代碼,把相應的GK和舊功能代碼刪除,這樣下一個或者下下個新版本將擁有純粹的新功能X代碼。這一步,一般會經常1個月或者幾個月的時間,而且最終純粹的X代碼發布會很謹慎,因為一旦上線給用戶,功能X的出現會不受Facebook server的控制;假設突然出現App端的惡性bug,Facebook除了馬上發布一個新版App,等待App Store審核,同時拜托用戶馬上升級之外沒有任何辦法。
綜合來看,灰度發布(shadow release)和 A/B testing 的特點和注意事項:
1. 前端代碼預先發布;且有可能同時多套不同版本的代碼存在;
2. 移動端或者前端MVC的代碼需要按照一定頻率獲取來自服務器的控制信息,從而展示相應的形態;
3. 后臺可以對發布進行精密的控制。比如說:發布給多少比例的用戶,用戶所在區域等;并且即使在開放給100%用戶后,只要 GK檢查 沒有清楚,則server仍然可以遠程控制(或回滾)試驗中的功能。在某些嚴重bug出現的情況下,可以完全回滾某一功能。
4. 注意:由于有A/B testing的原因,不同用戶A和B坐在一起,都剛剛下載和安裝了最新的Facebook App,但是打開app后所看到的功能(甚至UI布局)卻可能不一樣;并且對于同一用戶,今天看到的app里展示的功能可能和明天展示的也不一樣,即使他并沒有更新自己的app。
其實,微信早在幾年前就已經借助此方式來進行測試和數據采集(微信團隊的主力在2012年時來Facebook訪問,當年我們FB的一些工程師和他們的一些工程師+張小龍有對于產品和技術上的深度討論)。比如說微信的一大特點是右上角的快捷菜單:
上面的選項和順序就是在server端進行控制,如果它想,隨時就可以改變。而且也有可能不同人的不一樣(比如我的微信是在北美下載和注冊的,我的列表進行少一些,另外我的發現里面的京東購物也比中國區的朋友晚出現了將近一年;另外我從來沒看到過任何朋友圈廣告)。
5. Apple官網其實不支持這種server遠端控制app client的邏輯,因為它對于App store的審核有很大的阻礙。但是Apple一直對此一直睜一只眼閉一只眼。
6. iOS程序員都知道,Objective C是純動態語言,所有的函數都是虛函數,也就是說任何函數調用都可以在runtime時改變(特別有變態的 method swizzling 機制)。這就導致了當用戶下載了你的app之后,可以本地hack你的GK開關。之前就有很多次,Facebook發布了一個新版本后,那些iPhone界的破解高手就開始研究Facebook app,然后找到好些GK值,人為地把它們打開,然后嘗鮮未開放的新功能;有時還會把一些功能的使用體驗寫到博客里,或者發給 TechCrunch 用與報道。后來使得Facebook內部把比較重要有戰略意義的功能,直接用 compile flag 將其從 product build 中剝離。
最后 ——Don't reinvent the role
最后也是最重要的一點:從上面可以看出,對于整個灰度發布還有A/B testing,不管是產品本身還是做灰度發布的系統,都有著不小的開發量。也就是說:灰度發布+ A/B test一方面 會減慢產品迭代速度,另一方面會大大提高迭代穩定性的。這對于特別是初創公司(成立半年以內,用戶量不大的)要慎用(或者明確說來:禁用)。
然而對于發展得不錯的創業公司,特別是在同質化競爭比較嚴重(中國特色)的時候,要盡快地開始使用 A/B test。比如:Nice和In,去哪兒和攜程(合并之前)這種。在做 A/B test 方面,我覺得國內做得很不錯的創業公司公司是 AppAdhoc;創始人王曄有著很強的技術背景,之前一直在 Google mountainview 效力。公司網站:吆喝科技 AppAdhoc。 我之前去看過他們產品的界面和使用,和FB的inhouse系統相似度很大;個人還覺得他們的UI更加接近于大家習慣的 bootstrap 風格,而不是 FB 自己的藍色風:吆喝科技 AppAdhoc(感興趣的公司,可以我直接幫你們推薦 :D)
(未完待續 -- 番外篇:Facebook里的一些關于 growth hacking 的趣事、囧事、以及那些 Facebook 曾經踩過的坑)
--- END ---
公眾號: qc_empire
- Do have the faith in what you love