什麼是功能旗標 (feature flags)? 為什麼要用功能旗標?
2025年1月20日
AWS 的 S3 是全世界最多人使用的網路服務之一,而也正是因為非常多服務依賴 S3,在 2017 年 S3 發生的一次重大事故,讓全世界許多網站無法被使用,造成 AWS 損失超過 1.5 億美元。
你可能會想,是什麼重大技術困難,導致如此高額損失的事故?
事實上,該次重大事故的原因是打錯字 (typo)。你沒有看錯,該事故不是什麼重大技術難題導致的,而是因為多數人認為應該要能避免的錯字問題造成的。
雖然很多人可能會把打錯字當成低級錯誤,但事實上打錯字、差一個空格、差一行等看似簡單的問題,假如當到了一個完全不熟悉,且有複雜歷史程式碼的程式碼庫中,即使是很細心的人,也可能會出錯。
因此,從工程的角度來看,該思考的是如何讓相同的錯誤「不可能」再發生。只要有足夠完善的機制,很多人為的錯誤將變得不可能。對此,如果想要成為資深工程師,從這個視角出發思考是很重要的。
在 S3 當年的事故檢討中,針對這個打錯字的問題,總結了幾個最關鍵且能夠透過工程手段避免的點,分別如下:
- 輸入框沒有做校驗 (input not validated)
- 配置的更動一次全量推送 (config change pushed out everywhere at once)
- 回滾沒有自動化 (rollback not automatic)
試想,假如今天輸入框有校驗,這樣打錯字就能夠在配置階段就被發現。再退一步,假如配置不是一次全部推送,而是逐步推送給終端使用者,這樣可以在一開始只推送給小量使用者時就發現,能避免一次推送給全世界,導致一出問題影響範圍就遍及全世界。最後,如果出事故時能有自動偵測並回滾,那將能夠讓問題影響立即止損。
這一期的主題文,我們將會專注在上述三個解決方案中的第二個,來討論如何做到讓更動可以避免一次全部推送。而如同這期主題文標題,具體要實現這個方案的常見做法,會是透過功能旗標 (feature flags)。
什麼是功能旗標 (feature flags)?
所謂的功能旗標,是現代軟體經常會使用的技術,該技術做的事情很簡單,透過某個旗標 (flag),讓軟體在不需用重新部署的狀況下,能夠切換開啟或關閉某個功能。在業界有另一個詞叫功能切換 (feature toggle) 也是指同樣的概念,但由於近年來相關的技術,已經發展到不只是切換開與關,所以目前更多人會用功能旗標這個詞。
這樣講起來很抽象,讓我們透過一個具體的例子來了解。假如今天你在開發一個餐廳論壇的應用程式,想要在該應用程式中,加上一個 AI 聊天機器人,讓造訪的使用者,可以直接問 AI 機器人問題,例如可以問「請告訴我大阪最熱門的 10 間大阪燒店」。
但在上線這個聊天機器人前,假如不想要一次讓所有使用者使用 (因為擔心 AI 的幻覺問題,想要先小部分使用者測試),這時可以透過功能旗標,僅對部分的使用者開啟該 AI 聊天機器人的功能,以下我們以圖示的方式解說。
首先,從程式碼的角度,功能旗標的概念就像下面這段程式碼,透過一個布林的判斷,來決定是否展示某個功能。特別注意,這邊的 user.shouldSee("aiChatbot")
的值,不會是寫死在程式碼庫,而會是寫在某個設定檔 (config file),然後透過功能旗標的管理平台 (例如 LaunchDarkly、PostHog 這種平台,或是 AWS 的 AppConfig) 來管理,所以不需用動程式碼,也可以透過平台去改變 user.shouldSee("aiChatbot")
的值。
if (user.shouldSee("aiChatbot")) {
showAIChatbot();
} else {
showOldFeature();
}
當功能旗標的設定檔中把 AI 聊天功能關上,使用者進到網站就看不到 AI 聊天功能
+------------------------------------+
| 雲端 |
| |
| +------------------------+ |
| | 功能旗標服務 | |
| | +------------------+ | |
| | | 設定檔 | | |
| | | AI 聊天功能:關 | | |
| | +------------------+ | |
| +------------------------+ |
| ^ | |
| | v |
| 2. 確認功能設定 3. 回應設定結果 |
+------------------------------------+
^ |
| v
使用者進入網頁 AI 聊天功能未顯示
然而,可以針對部分使用者,把設定檔中的 AI 聊天功能開啟,這樣這些使用者就能看到
+------------------------------------+
| 雲端 |
| |
| +------------------------+ |
| | 功能旗標服務 | |
| | +------------------+ | |
| | | 設定檔 | | |
| | | AI 聊天功能:開 | | |
| | +------------------+ | |
| +------------------------+ |
| ^ | |
| | v |
| 2. 確認功能設定 3. 回應設定結果 |
+------------------------------------+
^ |
| v
使用者進入網頁 AI 聊天功能顯示
金絲雀部署
如前面對功能旗標的介紹,在有了功能旗標後,新的功能就能做到部分上線。舉例來說,可以先把使用者分群,在最開始時只有內部使用者 (例如開發人員、產品經理、 QA 測試人員) 可以看到。
在這個階段,雖然程式碼已經部署到線上環境,但因為有功能旗標,多數使用者不會真的能用到功能,因此不會真的對使用者有影響。等到測試完後,就可以開始逐步擴量。
逐步擴量時,可以透過功能旗標,開始可以先對 5% 使用者展示功能,然後 10%、30%、50% 到最後才 100% 全量上線。在這個過程中,開發者可以持續監控,假如在 5% 或 10% 放量時,發現監控中的指標有異常,這時就可以即時停止繼續放量,甚至回滾原本的少部分放量。
透過這種做法,可以有效避免一次全上線,結果出問題會導致所有線上使用者都受影響的狀況。以上面提到的例子來說,如果在 10% 放量時,就觀測到指標有異常,這時及時介入,異常就能限縮在 10% 的使用者,這大大降低了爆炸半徑。
事實上,這樣的做法,在業界普遍會用金絲雀 (canary) 這個名詞來描述。這種方式之所以叫金絲雀部署,是源自早年礦場為了確保礦坑沒有毒氣,在礦工實際進入礦坑前,會先放金絲雀去探勘,假如金絲雀能順利飛回來,代表礦坑沒有毒氣;反之,如果金絲雀沒有飛回來,那估計因為礦坑中的毒氣導致金絲雀死亡,這時礦工團隊就知道不該繼續往下挖礦。
而對應到程式碼的部署,金絲雀部署就像放出金絲雀,先以金絲雀去探勘,而不是直接全量上線,這樣遇到問題的話,也只有前面有被放出的小流量會被影響,而不會造成過於巨大的損失。在早期許多團隊也會用一種叫藍綠部署的方式,也是先有測試後,直接把所有流量都切到最新的版本。然而這種方式的靈活性相對低,因此目前業界更偏向金絲雀部署。
因此,回到最開始的問題,如果在上線時,想要避免一次推全量,可以怎麼做呢? 一句話總結來說,我們可以透過功能旗標,來做到金絲雀部署,這樣就能避免一次全量。
閱讀更多
如果你對「功能旗標」這主題感興趣,我們在 E+ 有更深入的討論。有興趣的讀者,歡迎加入 E+ 成長計畫。我們在 E+ 有更深入的內容,包含透過功能旗標做到在生產環境測試 (testing on production)、 主幹導向的開發 (Trunk-based Development)、透過功能旗標的分群部署做到 A/B 測試 等不同議題
本文為 E+ 成長計畫的深度內容,截取段落開放免費閱讀。歡迎加入 E+ 成長計畫閱讀完整版本 (點此了解 E+ 的詳細介紹)。