計拷問與文檔沉淀)
1. 從一次真實的“設(shè)計拷問”說起第一次接觸 grill-with-docs 這個技能是在一個訂單履約系統(tǒng)的重構(gòu)項目里。當時團隊已經(jīng)連續(xù)開了三次設(shè)計評審會每次都是兩小時起步白板上畫滿了架構(gòu)圖會議紀要寫了七八頁但真正落到代碼里的決策卻少得可憐。更麻煩的是兩周之后有人問“為什么當時不用事件溯源而選了狀態(tài)機”在場的人面面相覷誰也說不清楚。這種場景我相信做過中大型系統(tǒng)的人都遇到過——設(shè)計討論的過程信息量極大但沉淀下來的往往只有結(jié)論甚至只有代碼本身。grill-with-docs 解決的正是這個問題。它的核心思路很直接在一次會話里先對設(shè)計方案進行高強度的“拷問”grilling把每個決策背后的假設(shè)、權(quán)衡、風險都逼出來然后立刻把這些討論結(jié)果沉淀成結(jié)構(gòu)化的領(lǐng)域文檔包括 CONTEXT.md 和 ADRArchitecture Decision Record架構(gòu)決策記錄。整個過程不跨會話、不切換工具、不需要事后補文檔一次坐下來就把“想清楚”和“寫下來”兩件事一起干了。這個技能適合誰我認為三類人收益最大。第一類是技術(shù)負責人或架構(gòu)師需要頻繁做設(shè)計決策并且對決策質(zhì)量負責第二類是剛接手復雜系統(tǒng)的工程師需要通過文檔快速理解既有設(shè)計的來龍去脈第三類是團隊里負責技術(shù)寫作或知識管理的人一直在找一種“不額外增加負擔”的文檔沉淀方式。哪怕你只是一個人做 side project這套方法也能幫你把腦子里的設(shè)計思路理清楚避免三個月后自己都忘了當初為什么這么寫。接下來我會把這套技能的完整實操過程拆開講包括每一步的設(shè)計意圖、具體怎么問、文檔怎么寫、踩過哪些坑以及我實測下來最穩(wěn)的一套流程。2. grill-with-docs 的整體設(shè)計與核心思路2.1 為什么是“拷問”而不是“討論”普通的設(shè)討論和 grilling 有本質(zhì)區(qū)別。討論是發(fā)散性的大家你一言我一語很容易跑偏到實現(xiàn)細節(jié)或者無關(guān)話題上。而 grilling 是有結(jié)構(gòu)的、帶壓力的追問它的目標不是達成共識而是暴露分歧和盲區(qū)。我在實踐中總結(jié)了一個判斷標準如果一場設(shè)計討論結(jié)束后沒有人感到“被問住了”或者“有個地方我之前沒想清楚”那這場討論大概率是低效的。grill-with-docs 里的 grilling 環(huán)節(jié)通常圍繞幾個固定維度展開這個設(shè)計的核心假設(shè)是什么如果假設(shè)不成立會怎樣有沒有更簡單的方案邊界條件在哪里失敗模式有哪些每個維度都會追問到具體場景和具體數(shù)據(jù)不接受“一般來說”“應該沒問題”這種模糊回答。這種壓力測試的價值在于很多設(shè)計缺陷在寫代碼之前就能被發(fā)現(xiàn)而不是等到線上出問題才回頭改。2.2 文檔沉淀為什么必須“當場完成”我試過很多種文檔沉淀方式會后補、周末補、迭代結(jié)束補結(jié)論是——只要不是當場寫信息損耗至少 40%。原因很簡單討論過程中的猶豫、爭論、被否決的方案這些“負空間”信息在事后回憶時幾乎必然丟失。而恰恰是這些信息對后來接手的人最有價值。比如“我們考慮過用消息隊列解耦但考慮到運維復雜度放棄了”這句話如果不在當場記下來三個月后新來的工程師一定會再提一次同樣的方案。grill-with-docs 把文檔沉淀嵌入到會話流程里grilling 結(jié)束之后立刻進入文檔生成環(huán)節(jié)中間不隔夜、不切工具。CONTEXT.md 負責記錄領(lǐng)域模型和上下文邊界ADR 負責記錄每個關(guān)鍵決策的背景、選項、結(jié)論和后果。兩者配合形成一個完整的決策檔案。2.3 CONTEXT.md 和 ADR 的分工邏輯很多人會把這兩個東西搞混或者干脆只寫一個。我的經(jīng)驗是它們解決的是不同層次的問題。CONTEXT.md 回答的是“這個系統(tǒng)里有哪些核心概念它們之間是什么關(guān)系”偏向靜態(tài)的領(lǐng)域知識。ADR 回答的是“我們?yōu)槭裁醋隽诉@個選擇當時還考慮了哪些方案”偏向動態(tài)的決策過程。舉個例子在一個支付系統(tǒng)里CONTEXT.md 會定義“支付單”“交易流水”“結(jié)算批次”這些概念及其關(guān)系而 ADR 會記錄“為什么選擇最終一致性而不是強一致性”“為什么把對賬邏輯放在獨立服務而不是主流程里”。前者是地圖后者是旅行日志。兩者缺一不可只有地圖不知道路是怎么走出來的只有日志又缺乏全局視角。2.4 一次會話完成的流程設(shè)計整個流程我實測下來最順的是四段式準備階段10 分鐘、grilling 階段40-60 分鐘、文檔生成階段20-30 分鐘、校驗階段10 分鐘。準備階段主要是明確本次要拷問的設(shè)計范圍把相關(guān)的代碼、舊文檔、會議記錄快速過一遍。grilling 階段是核心按照預設(shè)維度逐項追問每個問題都要落到具體場景。文檔生成階段把討論結(jié)果結(jié)構(gòu)化CONTEXT.md 和 ADR 同步產(chǎn)出。校驗階段檢查文檔之間是否一致、是否有遺漏的關(guān)鍵決策。這個時間分配不是死的復雜系統(tǒng)可以拉長到兩小時簡單模塊可以壓縮到四十分鐘。關(guān)鍵是不要跳過任何一個階段尤其是校驗階段我見過太多因為文檔之間自相矛盾導致后來人誤入歧途的案例。3. 核心細節(jié)解析與實操要點3.1 grilling 環(huán)節(jié)的提問框架提問框架是整個技能的靈魂。我經(jīng)過多次迭代固定下來一套“五層追問法”每一層都有明確的意圖。第一層是事實層這個設(shè)計涉及哪些核心實體它們的狀態(tài)如何流轉(zhuǎn)數(shù)據(jù)從哪里來到哪里去這一層的問題必須能用具體例子回答不能用抽象概念糊弄。比如問“訂單狀態(tài)有哪些”不能只說“待支付、已支付、已完成”要追問“退款中的訂單算哪個狀態(tài)”“部分支付怎么表示”。第二層是假設(shè)層這個設(shè)計成立的前提是什么比如“假設(shè)下游服務響應時間在 200ms 以內(nèi)”“假設(shè)用戶不會在支付過程中修改收貨地址”。每個假設(shè)都要評估如果被打破會怎樣以及有沒有監(jiān)控手段能及時發(fā)現(xiàn)假設(shè)被打破。第三層是權(quán)衡層為什么選 A 不選 B當時考慮了哪些替代方案每個方案的優(yōu)缺點是什么這一層最容易挖出隱藏的決策因為很多選擇是“默認”做的當事人自己都沒意識到做了選擇。第四層是邊界層什么情況下這個設(shè)計會失效極端流量、數(shù)據(jù)量增長十倍、依賴服務不可用這些場景下系統(tǒng)會怎么表現(xiàn)這一層的問題往往能直接轉(zhuǎn)化為 ADR 里的“后果”部分。第五層是演進層如果未來要改改哪里改動成本有多大有沒有預留擴展點這一層幫團隊判斷當前設(shè)計是否具備足夠的彈性。3.2 CONTEXT.md 的結(jié)構(gòu)與寫法CONTEXT.md 不是 README不需要寫安裝步驟和 API 文檔。它的核心是領(lǐng)域模型和上下文邊界。我通常按四個部分組織核心概念、關(guān)系圖、上下文邊界、術(shù)語表。核心概念部分用一句話定義每個實體然后列出它的關(guān)鍵屬性和狀態(tài)。比如“支付單一次支付請求的聚合根包含金額、幣種、支付方式、狀態(tài)四個核心屬性狀態(tài)流轉(zhuǎn)為 created → processing → succeeded/failed/refunded”。關(guān)系圖部分用文字描述實體之間的關(guān)聯(lián)比如“一個訂單可以對應多個支付單一個支付單只能屬于一個訂單”。上下文邊界部分說明這個領(lǐng)域模型適用于哪些場景、不適用于哪些場景。術(shù)語表統(tǒng)一團隊內(nèi)部的叫法避免“支付單”和“交易單”混用。寫 CONTEXT.md 最容易犯的錯誤是寫得太細把字段類型、索引設(shè)計都塞進去。我的原則是只寫“如果不知道就會理解錯”的信息。字段類型看代碼就知道但“為什么支付單和訂單是一對多而不是一對一”這種信息代碼里看不出來必須寫進文檔。3.3 ADR 的模板與關(guān)鍵字段ADR 我用的模板包含六個字段標題、狀態(tài)、背景、決策、替代方案、后果。標題用“動詞對象原因”的格式比如“選擇狀態(tài)機而非事件溯源來管理訂單狀態(tài)”。狀態(tài)標記為 proposed、accepted、deprecated、superseded 四種。背景部分說明為什么需要做這個決策通常是一個具體的業(yè)務或技術(shù)問題。決策部分寫最終選了什么。替代方案部分列出考慮過但沒選的方案及原因。后果部分寫這個決策帶來的正面和負面影響。這里有個細節(jié)很多人忽略ADR 要編號并且不可修改。一旦 accepted后續(xù)要改只能新建一個 ADR 并把舊的標記為 superseded。這樣做的好處是保留了完整的決策歷史后來人能看到設(shè)計是怎么一步步演變的。我見過團隊直接改舊 ADR 的內(nèi)容結(jié)果導致不同時期的人對同一個決策的理解完全對不上。3.4 兩個文檔之間的交叉引用CONTEXT.md 和 ADR 不是孤立的它們之間需要交叉引用。CONTEXT.md 里每個核心概念如果涉及關(guān)鍵決策要鏈接到對應的 ADR。ADR 里如果引用了領(lǐng)域概念要鏈接回 CONTEXT.md 的對應章節(jié)。這種雙向鏈接在文檔數(shù)量少的時候感覺多余但一旦超過十個 ADR沒有交叉引用就會變成災難。我通常會在 CONTEXT.md 的每個概念定義后面加一行“相關(guān)決策ADR-003、ADR-007”在 ADR 的背景部分加一行“涉及概念支付單、結(jié)算批次”。這樣無論是從概念出發(fā)還是從決策出發(fā)都能快速找到關(guān)聯(lián)信息。3.5 實操中的三個關(guān)鍵注意事項第一個注意事項grilling 階段一定要有記錄員。如果只有一個人既問又記注意力會被打斷追問的深度會下降。我的做法是讓一個人主導提問另一個人專門記錄關(guān)鍵點和待確認項。記錄不需要完整只需要記下“問題-回答-待定”三要素。第二個注意事項不要試圖一次覆蓋所有設(shè)計。一個會話聚焦一個模塊或一個決策域貪多嚼不爛。我試過在一次會話里同時拷問訂單和支付兩個模塊結(jié)果兩個都問得不深文檔也寫得含糊。后來改成一次只搞一個模塊質(zhì)量明顯提升。第三個注意事項文檔生成后一定要讓參與者過一遍。不是走形式而是逐條確認“這是不是當時討論的意思”。我遇到過好幾次記錄員理解偏差導致文檔寫錯的情況如果沒人校驗錯誤信息就會一直傳下去。4. 實操過程與核心環(huán)節(jié)實現(xiàn)4.1 準備階段十分鐘快速對齊準備階段的目標是讓所有參與者對本次拷問的范圍和背景有共同理解。我通常做三件事第一用一句話說明本次要拷問的設(shè)計是什么比如“訂單狀態(tài)機的狀態(tài)定義和流轉(zhuǎn)規(guī)則”。第二快速過一遍相關(guān)代碼和舊文檔標記出有疑問的地方。第三確認參與者的角色分工誰提問、誰記錄、誰負責最終文檔。這個階段不需要深入討論就是對齊信息。我習慣在白板上畫一個簡單的上下文圖標出本次涉及的實體和外部依賴讓所有人一眼就能看到邊界在哪里。如果之前有相關(guān)的 ADR也在這個階段快速回顧一下避免重復討論已經(jīng)決策過的事情。4.2 grilling 階段逐層追問的完整記錄grilling 階段我按五層追問法逐層推進每層大概十到十五分鐘。下面用一個真實案例來展示追問過程。案例背景是一個優(yōu)惠券系統(tǒng)設(shè)計目標是支持多種優(yōu)惠券類型和疊加規(guī)則。事實層的追問從“優(yōu)惠券有哪些類型”開始回答是“滿減券、折扣券、立減券”。追問“滿減券的滿和減分別是什么含義”回答“滿 100 減 20”。繼續(xù)追問“如果訂單金額是 99.5 元能不能用”回答“不能必須大于等于 100”。再追問“如果訂單里有退款商品金額怎么算”這時候回答開始猶豫了說明這里存在未定義的行為。假設(shè)層的追問圍繞“優(yōu)惠券疊加規(guī)則”展開。假設(shè)“同一訂單最多用兩張券”追問“如果兩張券都是滿減券門檻怎么算”回答“分別計算”。追問“如果第一張券使用后金額降到門檻以下第二張券還能用嗎”這個問題直接暴露了設(shè)計缺陷——原設(shè)計沒有考慮券使用后的金額變化對后續(xù)券的影響。權(quán)衡層的追問聚焦“為什么選擇在訂單創(chuàng)建時鎖定優(yōu)惠券而不是支付時”?;卮鹗恰氨苊庵Ц稌r券已被用完”。追問“鎖定后用戶不支付怎么辦”回答“設(shè)置過期時間”。追問“過期時間設(shè)多長依據(jù)是什么”回答“15 分鐘參考了行業(yè)慣例”。這里就挖出了一個隱藏假設(shè)——15 分鐘是否適合所有場景比如大促期間支付擁堵時是否夠用。邊界層的追問針對“優(yōu)惠券系統(tǒng)的并發(fā)處理”。追問“同一張券被兩個請求同時使用時怎么保證不超發(fā)”回答“數(shù)據(jù)庫唯一索引”。追問“如果用了緩存呢”回答“緩存和數(shù)據(jù)庫的一致性怎么保證”。這一層的問題往往需要具體的技術(shù)方案不能停留在“應該沒問題”。演進層的追問關(guān)于“未來要支持跨店優(yōu)惠券怎么改”?;卮稹靶枰氲赇伨S度”。追問“改動涉及哪些模塊”回答“券模板、券實例、訂單計算、結(jié)算分賬”。追問“有沒有預留擴展點”回答“沒有需要改表結(jié)構(gòu)”。這個回答直接轉(zhuǎn)化為 ADR 里的“后果”部分——當前設(shè)計不支持跨店場景未來擴展成本較高。整個 grilling 階段我建議全程錄音征得同意的前提下因為追問節(jié)奏很快記錄員不可能記下所有細節(jié)。錄音可以在文檔生成階段回聽確保沒有遺漏關(guān)鍵信息。4.3 文檔生成階段從討論記錄到結(jié)構(gòu)化文檔文檔生成階段我通常分三步走。第一步把 grilling 階段的記錄整理成“決策清單”每條包含決策內(nèi)容、背景、替代方案、后果四個要素。第二步從決策清單中提取領(lǐng)域概念更新或新建 CONTEXT.md。第三步把每個決策寫成獨立的 ADR編號并建立交叉引用。以優(yōu)惠券系統(tǒng)為例決策清單里有一條“選擇在訂單創(chuàng)建時鎖定優(yōu)惠券”。背景是“避免支付時券已被用完”。替代方案是“支付時校驗并扣減”。后果是“需要處理鎖定后未支付的過期釋放增加了狀態(tài)管理的復雜度”。這條決策對應一個 ADR同時在 CONTEXT.md 的“優(yōu)惠券實例”概念下添加鏈接。CONTEXT.md 的更新要特別注意術(shù)語一致性。grilling 階段大家可能用不同的詞指代同一個東西比如“券模板”和“券定義”、“券實例”和“用戶券”。文檔生成階段必須統(tǒng)一術(shù)語并在術(shù)語表里記錄曾用名方便后來人搜索。4.4 校驗階段一致性檢查與遺漏補全校驗階段我固定做四項檢查。第一項CONTEXT.md 里的每個概念是否都有定義有沒有出現(xiàn)未定義的術(shù)語。第二項每個 ADR 是否都有編號、狀態(tài)、背景、決策、替代方案、后果六個字段有沒有缺項。第三項CONTEXT.md 和 ADR 之間的交叉引用是否雙向可達有沒有斷鏈。第四項決策清單里的每條決策是否都有對應的 ADR有沒有遺漏。我還會做一個“新人測試”找一個沒參與 grilling 的同事讓他只看文檔回答幾個關(guān)鍵問題比如“優(yōu)惠券疊加時金額怎么算”“鎖定后未支付怎么處理”。如果他能答對說明文檔合格如果答錯或答不上來說明文檔有歧義或遺漏需要補充。4.5 一個完整的 ADR 示例下面是我在實際項目中寫的一個 ADR脫敏后分享出來。標題是“ADR-012選擇狀態(tài)機而非事件溯源管理訂單狀態(tài)”。狀態(tài)為 accepted。背景是“訂單狀態(tài)流轉(zhuǎn)復雜涉及支付、退款、履約多個環(huán)節(jié)需要保證狀態(tài)變更的可追溯性和一致性”。決策是“采用有限狀態(tài)機管理訂單狀態(tài)狀態(tài)變更通過顯式的事件觸發(fā)每次變更記錄操作日志”。替代方案有兩個一是事件溯源優(yōu)點是天然可追溯、可重放缺點是查詢需要投影、運維復雜度高二是直接更新狀態(tài)字段優(yōu)點是簡單缺點是丟失變更歷史、無法審計。后果是“狀態(tài)機實現(xiàn)簡單、查詢直接但變更歷史需要額外日志表記錄且不支持狀態(tài)回放”。這個 ADR 寫完之后后來接手訂單模塊的工程師反饋說這是他理解訂單狀態(tài)設(shè)計最快的一次因為背景和替代方案把“為什么不用事件溯源”講清楚了他不用再自己猜。5. 常見問題與排查技巧實錄5.1 grilling 冷場或跑偏怎么辦冷場通常是因為問題太抽象參與者不知道怎么回答。我的應對方法是立刻降維把抽象問題換成具體場景。比如“這個設(shè)計有什么風險”沒人回答就換成“如果數(shù)據(jù)庫掛了這個流程會怎樣”。跑偏則是因為有人開始討論實現(xiàn)細節(jié)這時候要果斷拉回來說“這個點我們記下來grilling 結(jié)束后單獨討論”。我還會準備一些“萬能追問句”來救場比如“能舉個例子嗎”“如果反過來呢”“最壞情況是什么”“有沒有更簡單的做法”。這幾句話幾乎適用于任何設(shè)計討論能快速把話題拉回正軌。5.2 文檔寫得太空或太細怎么平衡太空的典型表現(xiàn)是“本系統(tǒng)采用微服務架構(gòu)具有高可用、可擴展的特點”這種廢話。太細的典型表現(xiàn)是把數(shù)據(jù)庫字段類型都寫進 CONTEXT.md。我的平衡標準是文檔應該回答“為什么”和“是什么”不回答“怎么做”?!霸趺醋觥笨创a“為什么”和“是什么”代碼里看不出來。具體操作上我會在文檔生成階段問自己三個問題這條信息如果刪掉后來人會不會理解錯這條信息如果保留會不會很快過時這條信息能不能在代碼里直接看到如果答案是“不會理解錯”“很快過時”“能直接看到”那就刪掉。5.3 ADR 編號沖突和狀態(tài)管理多人協(xié)作時 ADR 編號容易沖突。我的做法是每個 ADR 文件用“ADR-編號-標題.md”命名編號由一個人統(tǒng)一分配其他人新建 ADR 時先申請編號。如果團隊用 Git可以在合并時檢查編號是否重復重復的重新編號并更新所有引用。狀態(tài)管理方面我建議只保留 accepted 和 superseded 兩種狀態(tài)proposed 和 deprecated 在實際使用中容易造成混亂。一個 ADR 要么生效要么被取代沒有中間狀態(tài)。如果某個決策還在討論中那就不要寫 ADR等確定了再寫。5.4 常見問題速查表問題現(xiàn)象可能原因排查方法解決技巧grilling 問不出深度問題太抽象或參與者準備不足檢查是否提前發(fā)了背景材料降維到具體場景用萬能追問句CONTEXT.md 和 ADR 對不上文檔生成階段沒有交叉校驗逐條檢查交叉引用建立雙向鏈接校驗階段做新人測試ADR 編號重復多人同時新建 ADR檢查文件名和內(nèi)容編號統(tǒng)一分配編號合并時檢查文檔寫完沒人看文檔太長或找不到問團隊成員最近是否查閱過控制篇幅建立索引在代碼注釋里鏈接grilling 時間失控話題跑偏或追問太細記錄每個話題的耗時設(shè)定每層追問的時間上限跑偏就記下來決策清單遺漏記錄不完整回聽錄音對照決策清單記錄員只記“問題-回答-待定”不追求完整5.5 我踩過的三個坑第一個坑是第一次用這個技能時我試圖在一次會話里同時產(chǎn)出 CONTEXT.md 和 ADR結(jié)果兩個都寫得很粗糙。后來我調(diào)整了順序先集中精力把決策清單整理清楚再從清單派生兩個文檔質(zhì)量明顯提升。第二個坑是忽略了 ADR 的“替代方案”字段。一開始我覺得既然已經(jīng)選了 A寫 B 和 C 為什么沒選是浪費時間。后來發(fā)現(xiàn)替代方案恰恰是后來人最關(guān)心的部分因為它回答了“我想到的方案是不是已經(jīng)被考慮過了”?,F(xiàn)在我會強制要求每個 ADR 至少寫兩個替代方案。第三個坑是文檔生成后沒有讓參與者校驗。有一次記錄員把“最終一致性”寫成了“強一致性”導致后來人在設(shè)計對賬系統(tǒng)時基于錯誤前提做了決策。從那以后我堅持文檔生成后必須讓至少一個參與者逐條確認。6. 工具鏈與協(xié)作方式的選型建議6.1 文檔存放位置的選擇CONTEXT.md 和 ADR 放哪里我試過三種方案。第一種是放在代碼倉庫的 docs 目錄下優(yōu)點是版本管理和代碼同步缺點是產(chǎn)品經(jīng)理和設(shè)計師不方便看。第二種是放在 Wiki 里優(yōu)點是訪問方便缺點是容易和代碼脫節(jié)。第三種是放在獨立的文檔倉庫優(yōu)點是專注缺點是又多了一個地方要維護。我最終選擇放在代碼倉庫的 docs 目錄下因為這套文檔的主要讀者是工程師而且和代碼一起做 code review 能保證文檔更新及時。如果團隊有非技術(shù)角色需要查閱可以用 CI 自動同步到 Wiki 或內(nèi)部文檔平臺。6.2 會話記錄工具的選擇grilling 階段的記錄我推薦用最簡單的工具一個共享文檔加一支錄音筆。共享文檔用來實時記錄關(guān)鍵點錄音用來回聽細節(jié)。不要用太復雜的工具否則記錄員會花時間在工具操作上而不是記錄內(nèi)容上。如果團隊是遠程協(xié)作共享文檔加錄屏就夠了。我試過用專門的會議記錄工具自動轉(zhuǎn)錄的準確率在技術(shù)討論場景下并不理想專業(yè)術(shù)語經(jīng)常識別錯誤反而增加校對成本。6.3 與現(xiàn)有工作流的集成grill-with-docs 不需要獨立的工作流它可以嵌入到現(xiàn)有的設(shè)計評審或迭代規(guī)劃里。我的做法是在每個迭代的設(shè)計階段安排一次 grilling 會話產(chǎn)出文檔后直接進入開發(fā)。如果團隊有設(shè)計評審流程可以把 grilling 作為評審的前置步驟評審時直接看文檔而不是聽講解效率更高。對于緊急需求可以簡化流程只做 grilling 和 ADRCONTEXT.md 后續(xù)補充。但我不建議長期這樣因為缺少 CONTEXT.md 會導致領(lǐng)域概念逐漸模糊ADR 也會失去上下文。6.4 團隊推廣的實操建議推廣這套方法最大的阻力是“沒時間”。我的應對策略是先在一個小模塊上試點用實際效果說話。試點完成后把產(chǎn)出的文檔給團隊看特別是讓后來接手的人反饋“看文檔省了多少時間”。有了具體案例推廣就容易多了。另外不要把 grilling 搞成正式會議那樣會增加心理負擔。我通常把它包裝成“設(shè)計沖刺”或“架構(gòu)工作坊”氛圍輕松一點參與者更愿意說真話。記錄員也不要固定一個人輪流擔任能讓每個人都熟悉這套方法。7. 從單次會話到持續(xù)沉淀的擴展思路7.1 建立 ADR 索引和檢索機制當 ADR 數(shù)量超過二十個之后查找就變成一個問題。我的做法是維護一個 ADR 索引文件按模塊和狀態(tài)分類每條包含編號、標題、狀態(tài)、涉及概念四個字段。索引文件放在 docs 目錄的根目錄下方便快速瀏覽。檢索方面我依賴代碼倉庫的搜索功能在 ADR 標題和背景里埋關(guān)鍵詞。比如所有涉及支付的 ADR 標題里都包含“支付”二字搜索“支付”就能找到所有相關(guān)決策。CONTEXT.md 里的概念定義也包含關(guān)鍵詞搜索同一個詞能同時找到概念和決策。7.2 定期回顧與文檔更新文檔不是寫完就完了需要定期回顧。我通常在每個季度末花半天時間過一遍所有 ADR檢查有沒有狀態(tài)需要更新、有沒有被新決策取代、有沒有內(nèi)容過時。CONTEXT.md 則跟著領(lǐng)域模型的變化走每次有新的核心概念加入或舊概念廢棄時更新?;仡檿r我會特別關(guān)注“superseded”狀態(tài)的 ADR看看被取代的原因是什么有沒有形成模式。比如如果多個 ADR 都是因為性能問題被取代說明當初的性能評估方法有問題需要改進。7.3 新人上手場景的應用這套文檔對新人上手特別有價值。我通常讓新人第一周只做一件事讀 CONTEXT.md 和所有 accepted 狀態(tài)的 ADR然后回答幾個問題比如“這個系統(tǒng)的核心領(lǐng)域概念有哪些”“為什么訂單和支付是分開的”“如果要加一個新支付方式需要改哪些地方”。能答對這些問題的基本就理解了系統(tǒng)的設(shè)計思路。我還會讓新人在讀完文檔后提三個問題這些問題往往能發(fā)現(xiàn)文檔的盲區(qū)。新人沒有歷史包袱能看到老人忽略的模糊之處。把這些問題補進文檔文檔質(zhì)量會持續(xù)提升。7.4 多模塊協(xié)作時的文檔組織當系統(tǒng)有多個模塊時我建議每個模塊有自己的 CONTEXT.md但 ADR 是全局共享的。模塊間的交互在各自的 CONTEXT.md 里說明跨模塊的決策寫在全局 ADR 里。這樣既保持了模塊的獨立性又保證了決策的全局一致性。如果模塊之間有概念重疊比如訂單模塊和支付模塊都涉及“金額”我會在全局 CONTEXT.md 里定義“金額”的統(tǒng)一含義各模塊引用這個定義而不是各自解釋。這樣可以避免同一個詞在不同模塊里含義不同導致的溝通成本。7.5 我個人的使用體會用了大半年之后我最大的體會是這套方法的價值不在于文檔本身而在于 grilling 過程逼著團隊把問題想清楚。很多時候文檔寫不出來不是因為不會寫而是因為設(shè)計本身還有模糊地帶。grilling 把這些模糊地帶暴露出來文檔只是順帶的結(jié)果。另一個體會是不要追求一次完美。我早期的 CONTEXT.md 和 ADR 寫得很粗糙但后來不斷迭代現(xiàn)在回頭看第一版和最新版的差距非常大。重要的是開始做然后在實踐中調(diào)整。如果等到“準備好”再開始大概率永遠不會開始。最后分享一個小技巧每次 grilling 結(jié)束后花五分鐘讓參與者用一句話總結(jié)“今天最大的收獲是什么”。這句話往往能捕捉到最有價值的洞察而且適合放在文檔的開頭作為摘要。我試過很多次這句話的質(zhì)量比事后寫的總結(jié)高得多。