背景
產品在新功能釋出前,可能會採取小流量測試的方式,或者在確定方案前使用A/B測試來衡量。一般開發人員會跟運維同學合作,通過一些現有平臺切換機器或者流量來實現。本文介紹了另外一種簡便的方式,並解釋了其在持續整合上的應用,同時提供了現有的開發框架供快速使用。
Feature Flag VS Feature Branches
Feature Flag(又名 Feature Toggle、Flip等)是一種允許控制線上功能開啟或者關閉的方式,通常會採取配置檔案的方式來控制。提到Feature Flag一般都會跟Feature Branches進行比較。這兩個有什麼關聯與差別呢?可以通過一個簡單的示例來比較:
假設產品需要新增一個功能,如果你在主幹上進行開發,那麼通常的做法是在前端開發人員在介面上新增功能,然後可能會有其他同學來完成後端服務、安全保障,最後測試及Bug修復併發布上線。如下圖所示:
上圖中有個明顯的問題是主幹分支上在功能測試完畢之前是不能進行釋出的,因為功能已經在提供在介面中,必須完備之後才能釋出給使用者使用。
當然解決方法也很簡單,例如我們常見的是會使用功能分支(Feature Branches)來解決。在主幹上拉取一個分支,然後在分支上開完測試完之後在合併到主幹上,這樣就不會影響主幹的持續釋出了。如果有另外的新的功能那麼同樣拉取新的分支來解決。如下圖:
但這樣同樣存在問題,如果一個功能比較複雜,開發的週期較長,而在此期間主幹上已經多次修改程式碼,那麼等分支上開發完之後合併到主幹將是一個比較麻煩的工作。你必須去處理各種衝突,與其他開發人員溝通修改點。這是很多人不願意做的。
於是有人提供了新的方案來解決這個問題。例如將開發工作拆分成多個小塊,在各個分支上開發測試完成後及時合併到主幹中,並且可以先隱藏介面功能,直到所有的功能開發完成之後才展現。這樣每次合併的難度就小多了;或者每次將主幹上的修改都及時同步到分支上,這樣分支上開發完成之後合併到主幹上就簡單多了。
但如果釋出時出現bug怎麼辦?可能常見的是進行回滾重新上線。有什麼方式既能避免分支合併的麻煩、保持主幹快速迭代隨時釋出,又能更好的控制新功能的釋出、方便的進行小流量或快速回滾操作呢?答案就是Feature Flag。
Feature Flag允許關閉未完成的功能,你可以在主幹上進行迭代開發,新功能即便未開發完成也不會影響釋出,因為它對使用者是關閉
的。當功能開發完成之後,修改配置便可以讓功能釋出。這種操作甚至可以線上上進行,例如程式碼已經發布但功能不可見,你可以修改配置讓功能對特定的使用者(線上測試、小流量或者全量釋出等)可見。如果發現新功能存在問題,那麼可以通過配置檔案來迅速回滾,而必須重新分支上線。Feature Flag原理示意圖如下:
各自的優缺點
選擇合適的方案,而不拘泥於方式本身
並沒有萬能的方案,兩種方式都有各自的優缺點。
Feature Branches
優點:
- 同時開發多個功能分支不會影響主幹和線上程式碼
- 在分支上開發新功能時不用擔心對其他在開發的功能的影響
- 現有很多持續整合系統支援分支的構建、測試、部署等
缺點也很明顯,Martin Fowler的文章中已經做了全面的闡述:
- 分支分出去時間越長往往程式碼合併難度越大
- 在一個分支中修改了函式名字可能會引入大量編譯錯誤。這點被稱為語義衝突(semantic conflict)
- 為了減少語義衝突,會盡量少做重構。而重構是持續改進程式碼質量的手段。如果在開發的過程中持續不斷的存在功能分支,就會阻礙程式碼質量的改進。
- 一旦程式碼庫中存在了分支,也就不再是真正的持續整合了。當然你可以給每個分支建立一個對應的CI,但它只能測試當前分支的正確性。如果在一個分支中修改了函式功能,但是在另一個分支還是按照原來的假設在使用,在合併的時候會引入bug,需要大量的時間來修復這些bug。
Feature Toggle
優點:
- 避免了分支合併程式碼衝突的問題,因為是基於主幹的開發
- 每次提交都在主幹,迭代速度明顯有優勢
- 新功能的整個過程都持續整合
缺點:
- 未完成的功能可能會部署到線上,如果配置有誤可能將未完成的功能開啟。當然可以將介面層最後開發避免過早暴露。
- 主幹上擔心提交程式碼影響其他功能。
我們可以根據需要選擇合適的方案。Feature Flag在避免分支合併加快迭代上有優勢,另外Feature Flag除了主幹開發上的支援,還有什麼實用功能呢?下面來介紹。
Feature Flag種類與應用
一般Feature Flag可以分為兩類,見下所示:
釋出開關:
- 在釋出程式碼時關掉未完成的功能
- 生存期短
- 功能穩定就馬上刪除
- 在整個開發過程中有預定義的值
業務開關:
- 實現A/B測試
- 針對特定人群釋出功能儘早獲得反饋
- 針對特定條件開啟或者關閉功能。例如可以設定在指定時間點開啟,這樣新功能將按照設定自動上線下線,無需手動上線,適合專題等情況
- 能線上開啟或者關閉,實現快速回滾
釋出開關主要是為了隱藏未開發完成的功能,而業務開關則可以幫助我們快速滿足某些需求。例如A/B測試,Feature Flag可以輕鬆控制展現哪個功能,提升A/B測試的可維護性。我們也可以通過配置裡面的邏輯讓新功能針對小部分人群甚至是特定地域的人群釋出,儘早獲取功能的反饋。甚至是可以線上上開啟除錯,只讓新功能對除錯人員可見。而這些都只需要配置檔案和簡單的標記來實現。
誰在用Feature Flag
功能看起來很酷,但是不是新東西?有誰在用呢,我可不不願意承擔風險
事實上Feature Flag已經在國外網際網路公司中獲得廣泛的使用。例如FaceBook、Google等公司使用基於主幹的開發模式來持續整合開發,Feature Flag是其中一個基礎技術。下面這幅圖展現了FaceBook開發模式轉變歷程,可以看到幾年前facebook就開始使用Feature Toggle,使用了Feature Flag關閉主幹上未開發完成的功能來保證快速迭代和高頻率的釋出。
國外主幹開發中推薦這樣一種方式:trunk作為開發主線,所有開發人員完成開發後向及時向主幹提交程式碼,開發人員不允許在主幹上拉取分支。在釋出的時候由系統拉取分支釋出,主幹上的bug修復及時同步到釋出分支。開發人員可以本地使用git等工具進行版本管理。如下圖所示:
雖然基於主幹的開發模式已經成為國外的主流,但分支開發並不是不該使用。使用分支不推薦的是讓新功能程式碼在分支上長時間堆積,分支應當是生存週期短的。
實際應用中我們可以根據業務場景來選擇是否用功能分支還是Feature Flag,並且這兩者可以相互結合。例如在文章前面提到的示例中,可以使用分支來開發細分的子功能保持分支及時合併,同時使用Feature Flag來控制功能的釋出,提升工作效率。
最佳實踐
除了主幹開發,什麼情況下選擇使用Feature Flag呢?下面是使用Feature Flag的一些典型場景:
- 在 UI 中隱藏或禁用新功能
- 在應用程式中隱藏或禁用新元件
- 對介面進行版本控制
- 擴充套件介面
- 支援元件的多個版本
- 將新功能新增到現有應用程式
- 增強現有應用程式中的現有功能
可以看到,由於Feature Flag本身是對業務功能的控制,所以不適於功能大範圍的改動等情況。另外使用過程中需要注意一些問題:
- 只在需要的地方建立開關。美酒雖豪,不可貪杯。濫用任何技術都會出現問題。
- 控制開關的數量。同上,開關應按需使用並及時清除。
- 開關之間程式碼保持獨立。如果程式碼存在依賴就沒法刪除,最終維護性反而變差
- 清除釋出開關和廢棄程式碼。釋出開關應當在功能穩定後刪除,舊程式碼也是。
- 介面層最後暴露。
如何實現
實現這套東西複雜嗎?下面以php和smarty模板為例來介紹。
首先需要一套控制程式碼邏輯的工具,雖然開源的框架有在後端程式碼層的支援,但推薦在模板層使用Feature Flag,因為模板直接跟功能掛鉤,維護起來更加直觀方便。
例如我們會提供一個smarty外掛,讓你控制相應的展現:
這個程式碼的意思是如果common模組的featureA命中,則展現下面程式碼,否則展現另外一套程式碼,展現程式碼由於與功能相關,所以就相當於控制了展現哪個功能。當然你也可以不用featureelse
只控制功能的開啟或者關閉。
另外我們需要一個配置檔案,對應featureA的配置,如下所示:
1 2 3 4 5 6 7 8 9 |
{ "features" : { "featureA" : { "type" : "switch", "value" : "on", "desc" : "test switch feature work or not" } } } |
featureA配置的value是on
,開關型別是switch
。也就是說這個功能是開啟的。與switch類似的可以實現多個feature型別,例如抽樣控制、日期控制、地域控制等,程式碼邏輯只需要根據value的設定判斷是true還是false。例如抽樣型別,value設定0.5,那麼對應的型別邏輯只需要判斷隨機數是否在0-0.5範圍內而已。
部署中我們只需要修改featureA的配置就可以控制功能的釋出,是不是so easy!
開發框架
有哪些相應的開源框架呢?幾乎各種語言都有相應的實現。例如FEX FIS小組提供了基於php和node.js的框架。此外還有多種語言的開源實現:
語言 | Feature Flag框架 |
---|---|
php | 基於smarty的Feature Flag框架 |
NodeJs | 基於Node前後端解決方案Yogurt的Feature Flag框架 |
java | Togglz |
.NET | FeatureToggle |
Ruby | Rollout、Degrade |
Python | Gargoyle、Nexus admin |
Groovy | GrailsFeatureToggle |
總結
- Feature Flag與Feature Branches各有優勢,結合使用能發揮更大作用
- 結合業務場景選擇合適方案
- Feature Flag能支援主幹開發,並在控制功能釋出上有獨特優勢