系統設計面試:如何設計一致且可回滾的 Feature Flag 評估平台?
題幹與適用場景
平台要為多租戶服務提供布林、字串和結構化 Feature Flag,規則可能依賴租戶、使用者、地區和應用程式版本。設定發布後,有些實例立即生效,有些延遲數分鐘;同時不能讓敏感上下文進入日誌。請設計控制面、資料面、評估上下文、快取、發布、故障和稽核閉環。
面試官考察點
- 能否區分控制面發布與資料面本地評估,並定義版本和一致性目標。
- 是否正確處理 evaluation context 的合併、覆蓋、傳播和隱私。
- 能否設計快取失效、離線快照、預設值和回滾,而不是依賴即時 RPC。
- 是否把曝光、錯誤、變更和規則命中做成可稽核且不洩露個資的遙測。
回答前需要釐清的問題
- Flag 評估必須強一致,還是允許短暫舊版本?最大陳舊時間是多少?
- 規則依租戶、使用者、裝置、地區和版本的優先順序如何定義?
- 服務在控制面不可用時必須繼續多久?預設值由誰批准?
- 評估上下文包含哪些個資,哪些欄位可以進入曝光日誌?
- 需要跨語言 SDK、本地離線評估,還是統一遠端評估服務?
30 秒回答框架
我會讓控制面負責規則驗證、版本化發布、審批和回滾,資料面 SDK 使用帶版本的本地快照評估。上下文按全域、請求和呼叫級合併,並明確覆蓋規則;只傳送最小必要欄位。實例透過串流或輪詢收到版本更新,快取帶 TTL 和版本號;控制面故障時繼續使用最後可信快照並暴露陳舊指標。每次評估記錄 flag、版本、結果和匿名關聯鍵,禁止記錄原始個資。
分步驟深入解答
第一步:定義物件和發布狀態機
Flag 包含型別、預設值、規則、變體、環境、版本和生效時間。發布流程經過草稿、驗證、審批、灰度、完成或回滾,每次變更產生不可變版本。規則編譯失敗、型別不匹配或缺少預設值時阻止發布,不把執行時錯誤推給所有服務。
第二步:設計評估上下文
把應用程式、主機、地區、租戶和使用者屬性作為結構化 context。全域、交易和呼叫級上下文按規範合併,重複鍵採用明確覆蓋順序;SDK 不應隱式讀取任意執行緒變數而讓結果不可解釋。敏感欄位在進入 SDK、傳輸和日誌前做白名單與脫敏。
第三步:選擇本地或遠端評估
低延遲和控制面短暫故障要求本地評估,因此下發規則快照和版本。遠端評估適合規則頻繁變更或邏輯集中,但每次請求增加網路和可用性依賴。可以讓 SDK 本地評估,複雜規則由 provider 提供,介面保持型別化並返回原因、版本和元資料。
第四步:建立快取與一致性目標
快照快取以環境、flag 集合和版本為鍵,帶校驗和與過期時間。更新通知遺失時由輪詢補償,實例啟動先載入最後可信快照,再非同步追趕新版本。定義「版本不回退」「最大陳舊秒數」和「回滾傳播時間」,並把它們作為 SLO。
第五步:處理故障與安全預設值
評估異常時返回型別匹配的預設值或上一個可信值,並標記 reason、error code 和 source。關鍵支付、權限或資料刪除開關不應靜默使用危險預設值,可選擇阻斷請求或人工批准的 fail-closed 策略。SDK 需要防止未知 flag、型別轉換和規則執行逾時擴散。
第六步:設計稽核和曝光遙測
控制面記錄誰在何時發布、審批、灰度和回滾。資料面記錄 flag key、版本、結果、規則分支、SDK 版本和匿名 subject hash;不寫入原始郵箱、IP 或完整 context。取樣、保留和存取權限按租戶隔離,結果可與指標關聯以識別灰度影響。
第七步:驗證回滾與遷移
用固定 context 回放規則版本,驗證不同語言 SDK 的結果一致。演練通知遺失、快取損壞、控制面不可用、時鐘偏差、部分實例回滾和 provider 升級。回滾必須產生新版本而不是改寫舊版本,完成後比較實例版本分布和業務指標。
高品質示範回答
控制面負責型別驗證、規則編譯、審批、版本和灰度;資料面 SDK 使用帶校驗和的本地快照評估,以滿足低延遲和離線運作。評估 context 按全域、交易、呼叫級合併,重複鍵覆蓋順序固定,敏感欄位白名單化。實例透過通知加輪詢取得版本,快取定義最大陳舊時間和不回退約束。異常返回型別匹配的預設或上個可信值,並區分關鍵開關的 fail-closed。稽核保留發布和回滾鏈路,曝光日誌只含版本、結果和匿名關聯鍵,回滾用新版本並透過多 SDK 回放與故障演練驗證。
常見錯誤
- 每次評估都同步呼叫控制面,令業務延遲和可用性依賴設定服務。
- 只快取值不快取版本,無法判斷實例是否回退或陳舊。
- 讓上下文合併順序依賴語言 SDK 的隱式行為,導致跨服務結果不同。
- 在日誌中記錄完整使用者屬性或郵箱,造成隱私洩露。
- 回滾直接覆蓋舊設定,失去稽核、重放和部分實例恢復能力。
追問及應對
追問一:設定服務掛了還能評估嗎?
使用最後可信快照繼續評估,並報告版本、陳舊時間和 source;超過最大陳舊閾值時按 flag 風險選擇預設、阻斷或人工處理,而不是無限期靜默運作。
追問二:如何保證多語言 SDK 一致?
定義規範化規則、型別、context 合併和錯誤原因,提供跨語言固定輸入/輸出向量與版本化測試。複雜規則由 provider 統一執行,SDK 只負責協定和生命週期。
追問三:灰度比例如何穩定?
使用穩定的匿名 subject key 和明確雜湊演算法分桶,規則版本固定後同一 subject 在不同實例得到同一變體。變更演算法或鹽值必須產生新版本並說明遷移影響。
追問四:為什麼需要 hooks?
Hook 可在評估前補充上下文、評估後驗證值或傳送遙測,但必須有明確順序、逾時和錯誤策略。它不能偷偷改變 flag 型別或繞過稽核。
追問五:如何安全刪除一個 flag?
先查程式碼引用、評估流量和預設分支,發布一個固定值版本,觀察一段時間後再刪除規則和 SDK 元資料。保留歷史版本和遷移記錄,避免舊實例請求未知 flag 時出現型別錯誤。