題幹與適用情境
設計一個多租戶 Feature Flag(功能旗標)系統,供 3 個地區的 20,000 個伺服器端執行個體使用。系統跨專案儲存 50,000 個旗標,每秒處理 500 萬次行程內評估,新增延遲 p99 低於 1 毫秒,已發布變更在 5 秒內傳播到 p99 執行個體,並在控制平面中斷 15 分鐘期間繼續評估。系統需要支援有型別的變體、目標規則、確定性百分比漸進推出、稽核紀錄、核准和緊急關閉。
這些數字是面試假設,不是產品基準。假設每個執行個體最多訂閱 500 個相關旗標,單一旗標定義序列化後平均 2 KB,控制平面寫入峰值為每秒 100 次,伺服器端評估規則可能包含敏感屬性。用戶端與行動端交付、實驗統計和通用設定服務屬於追問,不納入基礎範圍。
這道題適用於資深後端、平台、基礎設施、發布工程和系統設計職位。可重複使用的判斷是:管理路徑可以採取強治理,請求路徑仍應維持本機與高可用。後續大部分設計都由這項分離決定。
面試官考察重點
第一個訊號是能否分開部署與發布以及控制平面與資料平面。控制台和資料庫管理旗標定義,應用程式 SDK 在行程內對版本化規則集進行評估。如果每次檢查旗標都遠端呼叫服務,管理系統的故障和網路延遲就會進入每個產品請求。
第二個訊號是語意是否精確。一次評估需要旗標鍵、環境、有型別的備援值和評估上下文。順序規則、明確目標、百分比分配與預設規則必須具有確定優先順序。「每個請求隨機抽 10% 開啟」會讓同一使用者跨請求和地區得到不穩定體驗。
第三個訊號是故障設計。最後已知正確版本可以在控制平面中斷時維持評估,同時也讓陳舊設定成為明確風險。高品質回答會定義首次啟動、最大可接受陳舊時間、版本缺口修復、無效更新拒絕、緊急關閉,以及展示類變更和危險寫入路徑採用不同預設值的規則。
第四個訊號是營運歸屬。旗標編輯就是正式環境變更。驗證身分、授權、環境隔離、樂觀並行控制、驗證、核准政策、不可變稽核、分階段發布、復原、負責人和下線流程都屬於系統設計。
回答前需要釐清的問題
- 在哪裡執行評估? 伺服器端 SDK 可以接收完整規則並在本機評估。瀏覽器和行動端不能安全接收敏感目標規則,需要過濾後的規則或遠端評估模型。
- 5 秒目標從哪裡量到哪裡? 定義為發布成功到 99% 的健康已訂閱伺服器端執行個體套用該版本。離線執行個體和超出支援新鮮度窗口的用戶端需要另行定義語意。
- 備援契約是什麼? SDK 從未載入有效快照時,回傳應用程式碼提供的有型別預設值。完成初始化後,可以在約定陳舊窗口內使用最後已知正確版本。
- 同一主體是否必須留在同一群組? 百分比漸進推出需要穩定目標鍵和確定性雜湊輸入。需要穩定體驗的匿名工作階段也要有持久識別碼。
- 規則能否包含敏感資料? 優先使用客群 ID 和非敏感屬性。伺服器端交付可以攜帶受保護規則,瀏覽器交付不能暴露金鑰、內部允許清單或授權邏輯。
- 緊急關閉有多關鍵? 5 秒傳播 SLO 仍不等於瞬時完成。真正危險的操作還需要伺服器端授權和獨立安全控制。
30 秒回答架構
「我會把平台拆成受治理的控制平面和本機評估平面。控制平面對變更進行驗證、版本化和稽核,再發布不可變快照與修補。伺服器端 SDK 保留最後有效規則,並在行程內執行明確目標、順序規則、漸進推出和預設值;初始化前或狀態無效時回傳程式碼備援值。確定性漸進推出把穩定主體鍵與環境、旗標鍵和鹽值一起雜湊。串流推送更新,輪詢修復缺口,控制平面中斷不會進入請求路徑。最後用故障注入驗證傳播、跨 SDK 結果、群體穩定性、陳舊行為和復原。」
分步深入解答
第一步:把題目轉換為明確契約
功能需求包括建立、編輯、核准、發布、關閉和下線旗標;定義布林、字串、數值或結構化變體;依主體或客群設定目標;進行百分比分配;回傳便於診斷的評估詳細資料。非功能需求包括本機 p99 低於 1 毫秒、傳播 p99 為 5 秒、跨地區結果確定,以及管理系統中斷 15 分鐘仍能評估。
畫元件前先確定三條邊界:
- 已發布版本不可變,後續編輯會產生新的草稿與版本。
- 傳播 SLO 約束健康且已連線的伺服器端執行個體,不包括離線裝置。
- 最終備援值由應用程式負責;型別驗證、初始化或評估失敗時,平台不能自行猜值。
基礎設計中,最後已知正確狀態至少在題目要求的 15 分鐘內有效。超過旗標發布的 staleafter 邊界後,staleaction 決定繼續使用最後已知正確版本或回傳程式碼備援值,SDK 同時回傳陳舊原因。安全敏感或破壞性路徑必須選擇備援,不能在沒有提示的情況下執行舊的開啟規則。
這樣可以避免把產品政策藏進基礎設施。結帳遷移可以預設走舊路徑,安全敏感能力可以預設關閉。兩者使用同一平台,但應用契約不同。
第二步:分離控制平面和評估資料平面
控制平面包含管理 API 與控制台、身分與角色驗證、schema 和規則驗證器、核准工作流程、關聯式事實來源、追加式稽核紀錄、快照建構器和發布器。寫入流量遠低於評估流量,因此正確性、可審查性和可復原性比微秒級延遲更重要。
資料平面包含地區更新中繼、快照儲存、輪詢端點,以及 SDK 本機儲存和評估器。伺服器端 SDK 連線到鄰近地區串流,安裝不可變快照,套用帶版本的修補,並且不經網路即可評估。串流中斷後繼續使用最後有效狀態,並用帶抖動的輪詢復原。如果修補跳過版本或校驗碼失敗,SDK 捨棄該修補並請求完整快照。
資料平面交付不能依賴控制平面的主要資料庫。發布器先寫入持久的版本化成品,再通知中繼。這樣資料庫或控制台故障只會阻止新編輯,不會停止現有評估。
第三步:定義資料模型和 API
旗標定義至少需要:
FlagDefinition {
tenant_id, project_id, environment, flag_key
version, value_type, variations[], off_variation
ordered_rules[], default_rule, salt
stale_after, stale_action
state, owner, expires_at
}
Rule {
rule_id, conditions[], outcome
}
Outcome = fixed_variation | weighted_variations[]客群需要獨立版本,因為多個旗標可能引用同一組成員。稽核項目記錄操作者、時間、理由、預期舊版本、變更前後參照、核准和發布結果。草稿與已發布成品分離,避免未核准編輯洩漏到評估路徑。
代表性 API 為:
PUT /v1/projects/{project}/environments/{env}/flags/{key}
body: draft definition, expectedVersion
POST /v1/projects/{project}/environments/{env}/flags/{key}:publish
body: draftVersion, reason, approvalToken
GET /v1/sdk/bootstrap?project={project}&env={env}&after={version}
GET /v1/sdk/stream?project={project}&env={env}預期版本可以防止兩位編輯者在沒有提示的情況下覆寫彼此。管理憑證不能同時作為 SDK 憑證。租戶、專案、環境和允許的交付模式都必須參與授權判斷。
第四步:讓評估和百分比漸進推出保持確定性
所有 SDK 使用一套公開順序:
- 檢查旗標是否存在、環境、型別以及目標功能是否啟用。
- 套用明確主體或客群目標。
- 依序執行規則,第一個符合項目獲勝。
- 解析固定變體或加權漸進推出。
- 沒有規則符合時使用預設規則。
- 無法得到合法型別結果時,回傳應用備援值與錯誤原因。
加權漸進推出從穩定輸入計算分桶:
bucket = H(tenant_id || environment || flag_key || salt || targeting_key) mod 100000把分桶對應到變體的累計區間。相同輸入在每個執行個體和地區都會得到相同結果。把鹽值與演算法版本寫進已發布定義,才能重現行為。擴大既有連續區間時可以保留原來已進入該區間的主體,但任意重新分配權重或修改雜湊輸入都可能移動使用者;審核時必須顯示這項後果。
兩個具有相同比例的獨立旗標不一定選擇同一批主體,因為旗標鍵參與雜湊。多個旗標需要共同移動時,使用共享版本化客群或明確實驗鍵。存在穩定主體 ID 時,不能用會變動的電子郵件等欄位進行雜湊。
第五步:安全交付快照與增量變更
快照建構器依專案和環境解析範圍,驗證引用的客群與前置旗標,以確定順序排列規則,序列化成規範成品,並附加版本與校驗碼。一個交易提交已發布中繼資料和 outbox 紀錄;非同步發布器儲存成品並通知地區中繼,避免旗標已提交卻沒有安排交付。
SDK 啟動順序為:
- 已設定持久儲存時,載入有效的最後已知正確快照。
- 從最近中繼取得最新完整成品或增量成品。
- 通過型別、版本和校驗碼檢查後,原子替換記憶體快照。
- 把提供者標為就緒,再提供本機評估。
- 保持更新串流連線,並以輪詢作為修復路徑。
不能原地修改使用中的規則集。建構新的不可變快照並原子替換一個參照,使並行請求只會看到完整舊版本或完整新版本。診斷詳細資料需要記錄實際套用版本與評估原因。
第六步:先設計故障語意
| 故障 | 評估行為 | 復原方法 | |---|---|---| | 控制 API 或資料庫無法使用 | 在 stale_after 前使用最後有效快照,之後執行該旗標的陳舊政策 | 阻止編輯,復原控制平面,驗證後再發布新版本 | | 更新串流中斷 | 使用本機狀態並標記更新狀態陳舊 | 帶抖動重新連線;輪詢並在發現缺口時請求完整快照 | | SDK 啟動時沒有快照 | 回傳程式碼中的有型別備援值 | 重試啟動載入,但不能無限阻塞應用程式其他啟動流程 | | 修補無效或亂序 | 保留目前版本 | 拒絕修補、發出警示並取得規範快照 | | 錯誤規則已發布 | 評估內部一致,但業務結果錯誤 | 停止漸進推出,把已知正確定義作為新的已稽核版本發布 | | 地區中繼無法使用 | 繼續本機評估,並嘗試其他中繼或輪詢端點 | 限制重新連線流量,避免同步啟動風暴 |
緊急關閉應走同一條持久發布路徑,只是使用優先通道,不能另建無稽核的可變旁路。預先授權的 break-glass 政策可以跳過一般核准等待,但仍要記錄操作者、理由、舊版本和結果。5 秒傳播目標不能保證所有斷線執行個體已經切換,危險操作仍需要獨立強制控制。
第七步:防止設定平台變成授權漏洞
依租戶、專案和環境拆分管理角色。正式環境編輯可以要求第二人核准,開發環境可直接發布。憑證加密並輪替,SDK 金鑰不能編輯旗標,管理呼叫需要限流。追加式稽核紀錄和不可變成品用於還原事故過程。
用戶端交付與伺服器端交付要分開。瀏覽器或行動端只接收明確允許公開的旗標,最好只取得針對目前上下文評估後的值。使用者可以檢查或修改用戶端狀態,所以旗標可以改變顯示,不能授予權限、繞過付款或取代伺服器端權益驗證。評估上下文應儘量減少個人資料,遙測主體識別碼需要假名化。
每個暫時發布旗標都要有負責人和下線條件。漸進推出完成後,先把勝出路徑固定,再停止評估,等程式碼參照消失後刪除定義。否則舊分支和旗標組合會無限擴張測試矩陣。
第八步:重算容量並證明設計
每個執行個體訂閱 500 個平均 2 KB 的旗標時,單份快照約 1 MB。20,000 個執行個體同時啟動會傳輸約 20 GB,尚未計算協定和副本開銷。規範成品需要由地區中繼或物件分發承載,重新連線要加入抖動並限制啟動並行,同時透過條件版本和增量交付減少流量。單一 2 KB 變更扇出到 20,000 個執行個體的邏輯負載約 40 MB,遠低於完整重新整理。
每秒 500 萬次評估不能同步傳送 500 萬個網路事件。即使每個只有 200 位元組,也約為每秒 1 GB,複寫前每天 86.4 TB。一般診斷應緩衝、批次、取樣或彙總。只有實驗契約確實需要時才保存完整曝光事件;遙測使用有界佇列,並允許在不延遲評估的前提下捨棄非關鍵事件。
驗證不能只看吞吐量:
- 用相同規則與評估上下文的黃金案例執行所有 SDK,比對值、變體、原因和錯誤行為。
- 對分桶確定性、近似分布以及擴大既有漸進推出區間時的穩定性進行性質測試。
- 依規則數和客群規模量測本機 p50 與 p99,另行量測發布到套用的傳播延遲。
- 中斷更新串流、停止控制資料庫、破壞修補、跳過版本、讓憑證過期,並讓所有執行個體同時重新啟動。
- 先向小型金絲雀群體發布錯誤規則,觸發健康警示,驗證復原會產生並傳播新的已稽核版本。
- 驗證租戶隔離、正式環境核准、用戶端暴露過濾、稽核完整性和旗標下線。
高品質示範回答
「我先把基礎範圍限定為伺服器端評估。系統有 3 個地區的 20,000 個執行個體,但控制平面峰值只有每秒 100 次寫入,兩條流量路徑不應共享可用性依賴。管理服務把草稿和不可變已發布版本儲存在關聯式資料庫。每次發布都要驗證型別、規則引用和權限,檢查預期舊版本,寫入稽核與 outbox,再建構規範的專案與環境成品。
地區中繼分發成品。SDK 載入最後已知正確快照,透過串流接收更新,並用輪詢修復缺口。它原子安裝新的不可變快照,在本機評估,使控制平面中斷時仍能滿足低於 1 毫秒的 p99 目標。最後已知正確狀態至少有效 15 分鐘;超過已發布陳舊邊界後,旗標依風險政策繼續使用該值或改用有型別的程式碼備援值。本機版本、新鮮度和評估原因都可觀察。
評估順序是關閉狀態、明確目標、順序規則、加權結果和預設規則。百分比漸進推出把租戶、環境、旗標、鹽值和穩定目標鍵雜湊到 100,000 個桶,使所有執行個體得出相同選擇。修改雜湊輸入屬於遷移;多個旗標需要同一群體時使用共享客群,不能依賴比例剛好相同。
最大突發是執行個體集體啟動:1 MB 範圍快照乘以 20,000 個執行個體約為 20 GB。我會在各地區提供快照、傳送增量、打散重新連線並限制重試。評估遙測必須批次與取樣,因為每秒同步記錄 500 萬個事件會威脅產品路徑。最後驗證跨 SDK 一致性、傳播 p99、啟動風暴、陳舊評估、損壞與缺失版本、金絲雀復原、緊急稽核、租戶隔離和用戶端暴露。成功標準是請求評估維持本機與確定,同時每次設定變更都受治理且可復原。」
常見錯誤
- 每次評估都呼叫中央服務 → 產品延遲和可用性依賴旗標服務 → 把版本化規則交付給伺服器端 SDK,在行程內評估。
- 每個請求重新隨機選擇使用者 → 同一使用者跨變體跳轉,實驗資料被污染 → 用穩定目標鍵和明確的旗標專屬輸入進行雜湊。
- 把最後已知正確版本視為永久正確 → 斷線執行個體可能無限期執行危險舊規則 → 定義陳舊狀態觀察、重新連線修復和應用安全備援。
- 把伺服器端規則集傳給瀏覽器 → 使用者可以查看敏感客群與憑證 → 過濾用戶端安全旗標或遠端評估,並在伺服器端強制授權。
- 原地更新共享規則物件 → 並行請求可能看到只套用一部分的設定 → 驗證完整不可變快照,再原子替換參照。
- 同步記錄每次評估 → 遙測成為請求路徑最高流量依賴 → 批次、取樣、彙總並允許捨棄非關鍵事件。
- 緊急變更不留稽核 → 最快復原路徑變成不可追蹤的正式環境後門 → 使用預先授權 break-glass 政策和帶不可變稽核的優先發布通道。
- 永不下線旗標 → 過期分支與旗標組合持續增加測試成本 → 指定負責人,在勝出路徑固定後刪除定義。
追問及應對
追問一:如何支援瀏覽器和行動端 SDK?
不要交付完整伺服器端規則集。標記哪些旗標允許用戶端暴露,驗證應用程式但不向其提供管理憑證,回傳過濾後的值或目前上下文的已評估結果。離線快取需要帶版本與新鮮度狀態。用戶端旗標只能作為體驗提示,伺服器端仍要獨立檢查權限、購買狀態、配額和其他安全決策。
追問二:多個旗標如何維持完全相同的漸進推出群體?
每個旗標鍵參與雜湊時,相同比例並不足夠。建立共享版本化客群或以共同實驗 ID 建立實驗分配,再由每個旗標引用。客群與依賴旗標應一致發布,擴大暴露前先驗證群體成員保持穩定。
追問三:一個客群有一千萬成員怎麼辦?
不能把完整成員清單嵌進每份快照。可以把成員關係表示為緊湊版本化成品,依主體雜湊分片,或預先計算評估器可使用的屬性。布隆過濾器可以減少負查詢,但存在誤判,不能單獨承擔授權。記憶體、查詢延遲、更新放大和成員陳舊需要與一般規則分別量測。
追問四:緊急關閉能做到瞬時生效嗎?
不能。任何分散式發布都無法瞬時到達斷線行程。緊急變更走優先通道,保持地區串流連線,統計確認並對落後版本發出警示。危險操作還要疊加伺服器端強制控制,例如關閉寫入端點、撤銷能力或在閘道阻止工作。旗標可以加快復原,不能取代硬安全邊界。
追問五:如何遷移雜湊演算法?
每個已發布旗標都保存演算法版本和鹽值。先以影子方式同時執行新舊評估器,量測分組移動比例。移動可接受時分階段發布;不可接受時,把既有主體分配保存在客群或遷移表中直到漸進推出結束。不能在沒有提示的情況下修改 SDK 雜湊實作,否則不同版本會產生分歧。
追問六:如何防止旗標相依形成環?
發布時建立前置相依圖,以深度優先走訪偵測到環就拒絕該版本。還要限制最大相依深度和總評估工作量,避免無環但病態的圖破壞延遲目標。成品中包含被引用旗標版本,跨 SDK 測試評估順序,並在診斷詳細資料中顯示相依鏈。