Feature Toggles
什麼是 feature toggle?
若一個功能要成功上線需有:
一般開發方式大致分成下面幾種:
One branch (最簡單的方式一定是通通在 master!)
請在自己 side_project 上這樣做
只在 master 上開發,程式必須等到功能開發完全才能 release,中途 release 會影響到現有、或別人做的功能。
開發複雜度小很多,但真的沒有公司這樣用。
Feature Branch
為了協作開發,開發 A 功能的時候也能同時開發 B 功能,也不會影響到其他 release 的東西。
開另一支分支,全部完成再 release,中途 release master 也不會影響到現有功能,但隨著功能的開發時間拉長,merge 回 master 的時候衝突可能很多,code_review 也會很大包,也有可能有重工的狀況。
trunk_base_flow
降低每個 commit 粒度,branch 的週期,merge 的時候更 easy
merge 回 master 時不會一次要解很多衝突,code_review 方便,持續整合,加快迭代。
Q:但這樣不就把未完成的功能一起 release 出去了嗎?
A:所以我們搭配 Feature Toggles/Flags ,來隱藏尚未完成的功能!
rails 的 Feature Toggles
Unleash
👆官方文件,請點標題
文件詳細,但複雜度也很高,要另架一個 server,自己串接 API
Flipper and Rollout
👆官方文件,請點標題
rollout 是仰賴 redis 去做開關的控制,而 Flipper 是可以指定你要用什麼方式去存取開關的資料。
基本做法都差不多,也都有另外的 ui 可以用,甚至可以指定誰可以執行、百分比釋出
ps. Flipper 支援 rollout,也就是你可以用 Flipper 把 Rollout 包起來
開關有分兩種,一種就是很簡單的開跟關,另一種是可以依照百分比釋出的。
實作(以 active_record
為例)
install
1 | # In Your Gemfile.rb |
請記得 bundle
,接著使用rails g flipper:active_record
這會幫你 create 兩個 table
flipper_features
flipper_gates
記得 migration!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class CreateFlipperTables < ActiveRecord::Migration[6.1]
def self.up
create_table :flipper_features do |t|
t.string :key, null: false # feature 的名稱
t.timestamps null: false
end
add_index :flipper_features, :key, unique: true
create_table :flipper_gates do |t|
t.string :feature_key, null: false # feature 的名稱
t.string :key, null: false # 控制開關的 type
t.string :value # 隨著 type 不一樣有不一樣的值
t.timestamps null: false
end
add_index :flipper_gates, [:feature_key, :key, :value], unique: true
end
def self.down
drop_table :flipper_gates
drop_table :flipper_features
end
endflipper_features
只記有哪些開關,flipper_gates
記你要怎麼控制這個開關
到這邊其實已經可以直接在 console 使用 feature_toggle 的功能了。
1 | # rails console |
不過要在 controller 中調用 Flipper 的方法,必須先 initialize,或者直接建立一份檔案,放在 lib/
中
1 | # config/initializers/flipper/feature.rb |
UI
1 | # In Your Gemfile.rb |
記得
bundle
,接著設定你的 router!1
2
3
4# config/routes.rb
YourRailsApp::Application.routes.draw do
mount Flipper::UI.app(Flipper) => '/flipper'
end若要依賴 devise 設定權限,example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14# initializers/admin_access.rb
class CanAccessFlipperUI
def self.matches?(request)
current_user = request.env['warden'].user
current_user.present? && current_user.respond_to?(:admin?) && current_user.admin?
end
end
# config/routes.rb
constraints CanAccessFlipperUI do
mount Flipper::UI.app(Flipper) => '/flipper'
end或是直接使用 devise_group 做輔助
1
2
3
4
5devise_scope :admin do
authenticated :admin do
mount Flipper::UI.app(Flipper) => '/flipper'
end
endUI 的部分可以只能稍微客製化,彈性不大
1
2
3
4
5
6
7# config/initializers/flipper_ui_config.rb
require 'flipper/ui'
Flipper::UI.configure do |config|
config.banner_text = 'Production Environment'
config.banner_class = 'danger'
end由於沒有生出任何的檔案讓你做修改,所以要客製化,只能在
config/initializers/
裡新增檔案,在檔案裡require flipper/**
的相關檔案才能進行設定。也就是改一次就要關一次 server 喔
Flipper 控制開關的方法 Gates
flipper_gates 的 key
Boolean
- 只有開跟關的選項
1
2Flipper.enable(:search)
Flipper.enabled?(:search) # truegroup
- 先製作好 group
- 使用
Flipper.register(:admins) { |thing| thing.admin? }
,所以你的 thing 要有 admin? 這個方法可以用 - 就可以直接使用
Flipper.enabled?(:feature_name, current_user)
去做判斷。1
2
3
4Flipper.register(:admins) { |thing| thing.admin? }
Flipper.enabled?(:feature_name, admin_user) # true
Flipper.enabled?(:feature_name, non_admin_user) #false
- 使用
actor
- 直接使用
flipper_id
指定誰(ex.user
、company
)可以過開關,請記得一定要有 flipper_id 這個方法可以用,因為 gates 是認這個!1
2# model/user.rb
alias_method :flipper_id, :id實作起來的實際狀況:
就算沒有設定flipper_id
這個方法,預設好像也是抓id
,但 value 那一格存的會是User;1
,有設定的話就會是你設定的 value
百分比模式
percentage_of_actors
- 設定為整個系統中,多少百分比 的 actors 打開
- 不過要這樣用的話一定要加
alias_method :flipper_id, :id
,在你要控制的 model 裡 - 每個功能應該要只有一種 actor
1
2
3Flipper.enable :feature_name, Flipper.actors(10)
# or
Flipper.enable_percentage_of_actors :feature_name, 10
percentage_of_time
- 用機率的方式決定是否開啟功能。
1
2
3
4
5# 不論是否有給第二個參數,皆有 25% 機率為 true
Flipper.enable_percentage_of_time(:new_feature, 25)
Flipper.enabled?(:new_feature) # 25% 機率為 true
Flipper.enabled?(:new_feature, user)# 25% 機率為 true
總結
feature_toggle 雖然好用,但請記得:
- toggle 太多,會不好維護 => 你可能要維護兩版。
- toggle 的功能粒度太大,但能控制的細節是有限的,請維持適中的粒度大小
- 每個 toggle 的功能,盡量不要耦合到其他 toggle 的功能,請別搞死自己XD”
- 還是會有搞爆 production 的風險啊!