敏捷開發(fā)流程中的質(zhì)量控制:Feature Flag

現(xiàn)在都提倡敏捷開發(fā),基本上已經(jīng)成了現(xiàn)代軟件開發(fā)的尤其是移動互聯(lián)網(wǎng)app開發(fā)的標準模式??焖俚⒖焖僭囧e成了每個人開口閉口都在談的東西,但是問題是,如何在快速迭代中保證產(chǎn)品質(zhì)量,如何在快速試錯的同時盡量避免你的“錯”去影響到實際用戶,以及如何知道是“對”還是“錯”?實際上,這才是區(qū)分一個開發(fā)團隊優(yōu)秀與否的關(guān)鍵,如果只是求快,有大把外包公司排著隊。。。?

在Agile Development中如何保持質(zhì)量以及如何快速測試功能是個復(fù)雜的話題,涉及到A/B test, User tracking, unit test, code review flow, CI及Analytics等等多個方面。在這里 鵝廠Turing?Lab副總監(jiān)?張力柯?先從一個概念的實施談起:Feature Flag.


1)What is Feature Flag??

我們假設(shè)現(xiàn)在你的app已經(jīng)完成核心功能,打個比方說你是一個交友軟件能夠用兩個用戶賬號互相發(fā)消息了,現(xiàn)在你在考慮是不是要加入微信登錄或者QQ登錄,你不知道是不是兩者都要還是只需要一個看起來比較簡潔,或者是不是年輕人更喜歡QQ登錄,或者要不要加個facebook登錄看起來更國際范。。。這時候你很可能就需要實現(xiàn)Feature Flag并以此作為數(shù)據(jù)驅(qū)動的來源。

啥叫feature flag?也就是功能開關(guān)標志,顧名思義,很簡單,無非是有個后臺控制去開啟/關(guān)閉某個功能。簡單吧?然而,在開發(fā)/測試人員一天埋沒在各種bug里面,一天在發(fā)布新版本上線時徹夜不眠時,是否想過其實你缺的就是個功能開關(guān)。實際上,F(xiàn)eature Flag在硅谷FLAGUAPS等一線互聯(lián)網(wǎng)公司及大大小小startup的開發(fā)流程中,早已成為標配。這不僅僅只是為了開關(guān)某個功能,而是要在敏捷開發(fā)/快速試錯/數(shù)據(jù)驅(qū)動決策這整個流程中不可缺少的一環(huán)。

首先來說數(shù)據(jù)驅(qū)動,這個詞語已經(jīng)流傳了很多年,各家公司都號稱自己是data driven. 那么到底是不是,很簡單,看他們的產(chǎn)品和代碼是否支持A/B Test. 如何看?很簡單:一個新功能,能否只對Android用戶開放而不對iOS用戶開放?能否收集兩天數(shù)據(jù)后別換版本,直接后臺控制對iOS開放而對Android用戶關(guān)閉?當然,這一般還會對用戶群比例作出限制,這是另外一個細節(jié),在這里先就不談,大家應(yīng)該對A/B test的概念不陌生。技術(shù)實施上可以復(fù)雜可以簡單,但是區(qū)分一個團隊是否是做到了data driven(或者拍腦袋driven),就要看A/B test是否普及。在國外FLAGUAPS等公司,A/B測試是任何新功能發(fā)布(哪怕改一個按鈕位置)必須加入的環(huán)節(jié)。當然,這也可能是資本主義的糟粕。。。

至于Feature Flag,一個軟件是由各種功能組合起來,那么對于每一個功能,理論上是應(yīng)該能夠隨時開啟或關(guān)閉的。這里又涉及到一個core flow的問題,就是一個軟件的核心最基本的流程是可以沒有關(guān)閉選項的,打個比方說微信的功能入口,對“看一看”這些功能在初上線的時候,是應(yīng)該能后臺控制關(guān)閉或開啟,并能針對不同用戶/設(shè)備/地域等進行控制,但對于聊天這個選項,是可以沒有關(guān)閉選項的(這就看設(shè)計者的要求了,原理上唯一不能關(guān)閉的就是進入app,其它都該能夠有開關(guān)選項)。


2) Who should be using feature flag?

必須強調(diào)一點,F(xiàn)eature Flag這個概念并非程序員專用,這實際上是個產(chǎn)品開發(fā)設(shè)計流程的概念,PM/client developer/backend developer/test engineer/data engineer/data scientist都必須熟悉這個概念并在日常工作中保持同步,各方的職責大致如下:?

-?PM: 必須知道當前哪些新功能處于Feature Flag 的保護下,哪些已經(jīng)經(jīng)過反復(fù)驗證,不再需要保護;一般來說一個簡單的規(guī)則是,從第一次正式發(fā)布開始(beta之后),所有新功能都必須用feature flag保護

-?客戶端開發(fā):在初期比較簡單,但往往在產(chǎn)品進入中期而且開發(fā)團隊擴大后,各種feature flag容易混合在一起,有可能造成嚴重的邏輯問題,需要特別注意。這跟multi-threading programming有點相像,互相l(xiāng)ock導(dǎo)致邏輯異常

-?后端開發(fā)/Data Engineer: 兩者往往要共同配合來實現(xiàn)Feature Flag的后端設(shè)計及控制界面,尤其是和用戶畫像(user profile)的關(guān)聯(lián)

-?Data Scientist: Data Scientist在其中的作用更多是在系統(tǒng)已經(jīng)搭好后,根據(jù)業(yè)務(wù)特定來設(shè)計Feature Flag的設(shè)定策略,比如到底對哪些用戶開放功能,要track哪些行為等,然后在搜集到一定數(shù)據(jù)后進行分析,再決定是否調(diào)整。


3) How Feature Flag works?

如果想體驗,可以嘗試launchdarkly.com的例子,其邏輯也簡單:


如上面代碼所示,對客戶端來說很簡單的東西,只是獲得該功能是否應(yīng)該啟用,然后執(zhí)行不同代碼去實現(xiàn)。?

那么你說了,這不就是直接在MySQL里面設(shè)一個key名字再設(shè)一個boolean field就完了么,分分鐘搞定 ----- 很遺憾,這種處理,相當于你只打出一個Hello World就說實現(xiàn)了起點文學(xué)。。。

在面向大中型團隊(如Linkedin/Facebook/Uber/Airbnb等等)的產(chǎn)品開發(fā)環(huán)境中,F(xiàn)eature Flag是一個整體的系統(tǒng),包括在提交時對Feature Flag的檢查處理,監(jiān)控Feature Flag的生命周期,和其它服務(wù)的對接等等。具體說的話,一個用于中型團隊的Feature Flag系統(tǒng)應(yīng)該能實現(xiàn)以下功能:

簡化的客戶端代碼實現(xiàn)=>提交時的相關(guān)代碼檢查修改(避免命名沖突)=>merge后在后端系統(tǒng)的腳本自動生成該Flag=>和各種其它service(用戶畫像、版本系統(tǒng)等等)的關(guān)聯(lián)=>對Flag本身的監(jiān)控和統(tǒng)計=>一定時間后(比如3個月后)對該flag是否應(yīng)該移除發(fā)出通知=>移除flag或繼續(xù)監(jiān)控


4) Feature Flag的應(yīng)用目標

Feature Flag 的意義并不是簡單的啟用和停止某個功能,而是針對不同人群來啟用功能并搜集數(shù)據(jù)進行分析,其背后的支撐是海量的用戶數(shù)據(jù)和精準的用戶畫像。利用Feature Flag,我們其實希望達到這些目的:

- A/B Test

能夠進行簡單的用戶二分類,對不同類的群體試驗不同的功能。這是最常見也是最基礎(chǔ)的功能。一般我們稱用作基準的用戶群為control group,用于試驗新功能的群體為treatment group. 在此不多說,網(wǎng)上例子很多。

-?灰度測試

如果我們能夠獲得客戶端用戶信息、設(shè)備信息、地域信息、時間信息等,我們可以進行更細致的群體劃分來進行灰度測試。但是這一點則對客戶端的數(shù)據(jù)收集及后端用戶畫像等數(shù)據(jù)提出了更多要求,這就不再是什么在數(shù)據(jù)庫中設(shè)一個開關(guān)的問題,而是一個完整的后端服務(wù)來返回True|False,而這個后端服務(wù)會跟用戶信息、用戶畫像、設(shè)備信息、功能設(shè)定等多個數(shù)據(jù)源或服務(wù)進行交互。

舉個例來說,我們可以對游戲中的某個道具外觀做調(diào)整看是不是更吸引用戶去購買,但我們想做灰度測試,這個灰度測試就可以是比如:在北京地區(qū)的20~25歲男性且手機內(nèi)存在4G以上的Android用戶,和西藏地區(qū)20~25歲使用Android手機且內(nèi)存不到2G的男性用戶(用于比較大城市和邊遠地區(qū)用戶的行為差異)?

難點:需要完善細致的用戶畫像和其他相關(guān)數(shù)據(jù)服務(wù)的支撐,并需要數(shù)據(jù)分析人員對具體灰度測試的目標和結(jié)果制定相關(guān)方案。打個比方說你可能發(fā)現(xiàn)測試群體的行為和你預(yù)料的不一樣,那么你是馬上修改功能呢,還是換個群體再測試,這都是需要經(jīng)驗的積累和理性的計劃。

-?UI設(shè)計的A/B Test

我們甚至可以通過功能開關(guān)來實現(xiàn)對不同用戶群的不同UI設(shè)計。然而,這對代碼實現(xiàn)提出了較高標準,在iOS開發(fā)中很多公司不允許使用Storyboard進行界面設(shè)計,而強制要求所有UI都要用代碼實現(xiàn),一則是方便code review,二則就是為了做UI上的A/B test。又打個比方說,我們可能會糾結(jié)某個道具列表是橫向滾動還是縱向滾動,那么就可以通過Feature Flag來控制,例如對50%的用戶是橫向,對剩下的是縱向滾動。

難點:這種需求對代碼實現(xiàn)要求極高,要求程序員能設(shè)計出能支持不同布局的局部UI框架,一般只在時間比較充裕時可以采用。

-功能回滾(Rollback)

這是Feature Flag的主要應(yīng)用場景,通常發(fā)生在新版本發(fā)布后出現(xiàn)的非核心功能bug,為了不影響主干新功能,我們必須把出現(xiàn)bug的功能給關(guān)掉。在缺乏Feature Flag的開發(fā)中,我們對每一個新功能的發(fā)布都提心吊膽,開發(fā)者被埋沒在已有的無數(shù)bug中,只知道bug越來越多,哪些優(yōu)先哪些不優(yōu)先全憑策劃一張嘴。測試人員提心吊膽生怕漏測背鍋,一旦有問題沒發(fā)現(xiàn)影響了核心流程,或者導(dǎo)致crash,搞不好就是百萬千萬的收入損失,然后迎來新版本上線前后的徹夜不眠。

有了Feature Flag,盡管發(fā)現(xiàn)了Bug仍然需要緊急修復(fù),但至少在能保證核心流程不crash情況下,新增功能如果出了問題,關(guān)閉掉就是,壓力大幅度降低,尤其對于測試一方,只需要做好bug的上報和功能的關(guān)閉,至于定位問題,在壓力不那么大情況下就可以交由開發(fā)人員去完成。在現(xiàn)在的新一代互聯(lián)網(wǎng)公司中,凡是能關(guān)掉而不影響核心流程的功能bug,統(tǒng)統(tǒng)可以作為次優(yōu)先級問題,而開發(fā)經(jīng)理、測試經(jīng)理和PM在聽聞某功能出現(xiàn)bug尤其是會導(dǎo)致crash的bug時,往往會第一時間詢問是否能關(guān)閉此功能,是否已經(jīng)通過feature flag進行控制,如果答案是Yes,大家都會松一口氣,好歹有時間來修復(fù)。

難點:通過Feature Flag來進行功能回滾,看似只是一個控制方式,實質(zhì)是對開發(fā)/測試的一次重新分工。在傳統(tǒng)測試體系中,由于沒有全面普及Feature Flag,凡是會導(dǎo)致較嚴重后果(程序crash,金錢交易問題等)的bug都會成為緊急問題,為了提高效率,測試人員往往要負擔重現(xiàn)bug、bug定位甚至是對代碼debug的工作,以便交到開發(fā)人員手中的是一份完善的包括具體bug復(fù)現(xiàn)、問題根源及代碼位置的報告,以加快開發(fā)人員的bug fix速度。

在應(yīng)用Feature Flag之后,由于我們能夠?qū)Ξa(chǎn)生問題的新功能直接關(guān)閉或者有針對性關(guān)閉,為開發(fā)人員贏來大量的bug fix時間,那么對測試人員在bug fix上的要求就不再那么急迫,測試人員可以把時間用于更完善的bug記錄、跟蹤、統(tǒng)計、上報等功能上,而無需花大量時間去研究如何重現(xiàn)bug或者對其進行代碼定位。而對于開發(fā)人員,他們就要更多地去考慮如何對自己所負責的模塊進行細粒度的Feature Flag保護,以便在發(fā)生問題時不至于把自己的大模塊都整個關(guān)閉,例如:?

在上面這幾行代碼中,各種Feature Flag非常容易糾纏在一起,這時候需要開發(fā)人員非常小心去理順其中邏輯,能避免這種情況就盡量避免。

這里要非常強調(diào)一個概念:如果說10年前大家關(guān)注的是如何快速定位bug并快速fix,那么經(jīng)過這么些年的敏捷開發(fā)和mobile app經(jīng)驗,現(xiàn)代軟件開發(fā)的關(guān)注重點在于如何能提前預(yù)防和減少bug所帶來的影響,因為無數(shù)事實已經(jīng)證明,急急推出一個含有bug的新功能所造成的損失遠大于延遲推出。

-?Analytics(數(shù)據(jù)分析)

實際上,在Feature Flag所控制的代碼中,由于是新功能,通常需要對其作比較詳細的記錄,也就是data tracking(埋點),方便數(shù)據(jù)科學(xué)家對該功能所帶來的效果和潛在的問題進行全面的分析。這通常需要和數(shù)據(jù)科學(xué)家根據(jù)業(yè)務(wù)來共同確定。關(guān)于tracking在開發(fā)過程中何時進行以及如何進行,則是另外再談的問題。


5)客戶端的設(shè)計

從軟件架構(gòu)來看,F(xiàn)eature Flag的實現(xiàn)上有很多可以挖掘和優(yōu)化。比如說下面的代碼:

if (FEATURE_ENALBED(WECHAT_LOGIN)) {

}

?其中FEATURE_ENABLED這個在C++中可以用Macro來實現(xiàn),或者封裝成別的功能,在大型客戶端項目中(如Linkedin/Uber/Airbnb等等),僅僅一個簡單的判斷是否enable是不夠的,還可以分作比如:

OPTIMISTIC_FEATURE_ENABLED(…) ??//return True if not defined in backend

DEBUG_FEATURE_ENABLED(…)? ????????// return True only in DEBUG mode

也就是實際上有些是缺省打開,有些是缺省關(guān)閉,還有些只對DEBUG起效果,通過客戶端的代碼來控制不同環(huán)境下的開關(guān);?

往往我們并不想用比如”WECHAT_LOGIN”這樣來顯式定義Feature,很容易遇到命名沖突的問題(注意,我們在考慮一個幾十人同時每天提交代碼的環(huán)境,命名問題是很容易發(fā)生的)。在實踐中,這個工作通常會在代碼提交前通過相關(guān)的代碼審核工具如Lint等結(jié)合腳本工具來進行自動修正,例如上 面例子,在提交代碼時會自動修改為類似下面的代碼:

直到這一步為止,所有工作都是客戶端代碼完成,因為代碼并未提交到主干,沒有必要在后端數(shù)據(jù)庫生成該Feature Flag。 一般來說,直到代碼提交并通過code review后,在merge入master之后才會由相關(guān)腳本文件去在特定的Feature Flag數(shù)據(jù)庫中寫入該標記。


6)后端系統(tǒng)

如前所述,F(xiàn)eature Flag的目標有很大部分是用于灰度發(fā)布測試和功能回滾。如果說功能回滾是為了隱藏bug,那么對于灰度發(fā)布測試,就需要能夠?qū)eature Flag做詳盡的用戶群體分類。用戶畫像的劃分說來話長,再說Talk is cheap,這里用某國外打車軟件的Feature Flag控制體系來舉個例,其中你需要設(shè)定以下參數(shù):

分組(缺省情況下你可以只設(shè)定treatment group,但是也可以自定義分組)

對每一組可以設(shè)定:

-?版本號(可以多個或者條件控制,例如>3.41之類的)

-?用戶選擇(特定UUID/只對員工開放/特定群體/etc.)

-?設(shè)備類型(ios/android/OS version/etc.)

-?地域控制(城市/省/國家)

-?IP控制(對移動app很少用)

基本上通過對這些參數(shù)的排列組合,已經(jīng)足夠獲取大量的分析數(shù)據(jù)。

另外客戶端通常也可以在測試版本中實現(xiàn)對Feature Flag的override開關(guān),方便開發(fā)人員的測試。


7) 主干功能

現(xiàn)在你已經(jīng)能夠通過Feature Flag來對不同用戶測試不同的設(shè)計,是不是急不可待準備發(fā)布了呢?

如果是一個幾十人上百人的開發(fā)團隊,現(xiàn)在還不要著急,注意到我在說功能回滾時反復(fù)提到核心主干功能,這些核心功能流程不應(yīng)受到feature flag的影響,是原型設(shè)計中就驗證無誤的。通常我們要確信這些核心功能會被unit test(單元測試)所覆蓋,并且驗證無論新增的feature flag是啟用了還是關(guān)閉,都不會對核心主干流程有影響(比如說不管支護寶怎么弄,轉(zhuǎn)賬付款等基本功能不能受影響),所以單元測試就顯得特別重要。

注意到一個問題,很多人會覺得單元測試很無聊,無非是驗證一些很簡單的邏輯。然而實際上單元測試的目的并非驗證你自己寫的某個功能是正確的,而是為了在團隊擴大后,為了防止別人的代碼對你這塊產(chǎn)生影響,避免大量新的其他代碼變化在某個時刻影響到該功能。換句話說,單元測試的目的是block potential changes,而不是僅僅在做verification. 之前km有帖子問現(xiàn)在開發(fā)是不是不需要寫大段注釋了,回答是Yes&No. 現(xiàn)在推行單元測試,從某個角度來說是代替了注釋(talk is cheap, show me the code),你能通過單元測試就說明你的代碼和別的部分的接口沒有問題,否則就是有問題。至于unit test這方面在現(xiàn)代軟件開發(fā)中的具體實施流程,則是另一個話題了。


想開始高效協(xié)作,請前往TAPD官網(wǎng)(https://www.tapd.cn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容