計(jì)文檔怎么寫:用接口契約與狀態(tài)機(jī)把“詳細(xì)”落到實(shí)處)
簡介模塊設(shè)計(jì)文檔是軟件開發(fā)中用于明確系統(tǒng)結(jié)構(gòu)、模塊職責(zé)與協(xié)作方式的關(guān)鍵產(chǎn)物。這份模板面向項(xiàng)目經(jīng)理、系統(tǒng)架構(gòu)師、開發(fā)與測試人員適合在項(xiàng)目初期統(tǒng)一設(shè)計(jì)溝通語言有效規(guī)避模塊邊界模糊、接口依賴混亂等問題。壓縮包內(nèi)僅一個(gè)doc格式文件容量約93KB輕量且便于直接改編為實(shí)際項(xiàng)目的文檔底稿。文檔章節(jié)完整且可操作覆蓋文檔目的與范圍、讀者對象、參考文獻(xiàn)、術(shù)語與縮寫解釋、模塊命名規(guī)則、模塊匯總表與關(guān)系圖并延伸到子系統(tǒng)A/B的模塊設(shè)計(jì)細(xì)則包括功能描述、接口屬性、數(shù)據(jù)結(jié)構(gòu)與算法、補(bǔ)充說明等。目前已有665人學(xué)習(xí)下載能為團(tuán)隊(duì)提供清晰的模塊設(shè)計(jì)指導(dǎo)也為后期維護(hù)、升級和知識(shí)傳遞打下扎實(shí)基礎(chǔ)。1. 模塊設(shè)計(jì)文檔寫不到位的“詳細(xì)”就是給項(xiàng)目埋雷我見過太多項(xiàng)目死在一種奇怪的節(jié)奏里架構(gòu)評審時(shí)大家把模塊邊界畫得漂漂亮亮等代碼跑到第三個(gè)迭代新人接手一個(gè)模塊讀代碼讀了兩天依然不知道這個(gè)模塊到底對外承諾了什么、邊界在哪里、哪些異常是“設(shè)計(jì)內(nèi)允許”的。這時(shí)候回頭找設(shè)計(jì)文檔要么只有半頁概要要么是一堆照著敲鍵盤也能想到的流水賬。這個(gè)標(biāo)題里的“很詳細(xì)的”四個(gè)字恰恰是區(qū)分文檔能不能在項(xiàng)目中期救命的分水嶺。詳細(xì)不是字?jǐn)?shù)多而是把接口契約、狀態(tài)流轉(zhuǎn)、異常分支、數(shù)據(jù)歸屬這些“容易在代碼里藏雷”的東西提前用白紙黑字釘死。這篇筆記面向的讀者是正在寫模塊文檔的人或者被“文檔沒什么用”拖累過的開發(fā)者我把一線實(shí)際在用的文檔骨架、寫法邊界和踩坑路徑拆開講照著搭你的模塊文檔至少能在項(xiàng)目中期救人一次。2. 先說清楚“詳細(xì)”到底指什么以及文檔寫給誰看2.1 詳細(xì)不等于長篇大論是把決策場景寫進(jìn)文檔很多開發(fā)者一聽到“詳細(xì)文檔”第一反應(yīng)是把方法名、變量名、函數(shù)調(diào)用關(guān)系全部抄一遍。這種文檔寫到一半自己就想撕掉因?yàn)榇a一改文檔就得跟著改根本維護(hù)不動(dòng)。我理解的“詳細(xì)”是圍繞“設(shè)計(jì)決策”做完整展開為什么這個(gè)模塊的緩存放在這一層為什么錯(cuò)誤碼要定義成三段式為什么超時(shí)閾值默認(rèn)配 3 秒這些決策在代碼里是看不出來的或者說只能通過反編譯式的考古才能還原而一份詳細(xì)文檔要做的就是把這些決策連同背后的備選方案、權(quán)衡過程一并記錄下來。寫法上我常用“決策模板”來約束自己先寫“這里做了什么決定”再寫“當(dāng)時(shí)有哪兩個(gè)備選方案”最后補(bǔ)一段“為什么沒有選另一個(gè)”。不要小看這段“為什么”項(xiàng)目到第三輪重構(gòu)時(shí)新人問得最多的恰恰是“這里為什么要這樣”。如果文檔里只有結(jié)論沒有理由那和代碼注釋里寫“這里不要?jiǎng)印睕]有任何區(qū)別都屬于沒法驗(yàn)證的黑匣子。實(shí)際操作時(shí)我會(huì)給每個(gè)重要設(shè)計(jì)點(diǎn)分配一個(gè)編號比如 MDD-INT-001 表示“對外接口設(shè)計(jì)第 1 條”MDD-CACHE-002 表示“緩存策略第 2 條”。后續(xù)代碼評審、缺陷單、變更記錄里都可以引用這個(gè)編號關(guān)聯(lián)到具體設(shè)計(jì)決策。這種做法最大的好處是當(dāng)某段邏輯被改出問題時(shí)可以順編號查回設(shè)計(jì)初衷判斷到底是“實(shí)現(xiàn)偏離了設(shè)計(jì)”還是“設(shè)計(jì)本身就該被推翻”。2.2 一份模塊文檔的四個(gè)真實(shí)讀者以及他們各自要什么寫文檔之前先想清楚給誰看文檔的顆粒度和語言風(fēng)格完全不一樣。在我?guī)н^的項(xiàng)目里模塊設(shè)計(jì)文檔至少面對四類讀者新加入的開發(fā)者、負(fù)責(zé)聯(lián)調(diào)的上下游同事、做代碼評審的資深工程師、半年后的自己。新開發(fā)者需要文檔回答“這個(gè)模塊的入口在哪里、狀態(tài)有幾種、我改哪一塊不會(huì)炸”上下游同事需要文檔回答“接口的字段含義、超時(shí)和重試機(jī)制、錯(cuò)誤碼在什么場景出現(xiàn)”評審專家需要文檔回答“這個(gè)設(shè)計(jì)有沒有遺漏的并發(fā)場景、有沒有不一致的狀態(tài)”半年后的自己需要文檔回答“當(dāng)初為什么做了這個(gè)看起來很奇怪的決定”。這四類訴求落到實(shí)際寫作上會(huì)直接推導(dǎo)出文檔的結(jié)構(gòu)比重。比如接口契約部分面向上下游的部分要寫得像一份正式的協(xié)議字段類型、取值范圍、默認(rèn)值、異常分支一個(gè)都不能少而面向新人的部分則要在模塊概述里用一段話講清“這個(gè)模塊本質(zhì)上是做什么的”這句話寫不好新人就會(huì)把讀文檔變成盲目翻代碼。我見過最失敗的模塊概述是“本模塊負(fù)責(zé)訂單模塊與支付模塊之間的交互”這句話毫無信息量等于沒說。因此我會(huì)在文檔開頭強(qiáng)制要求寫一段“模塊的一句話職責(zé)”并配上“本模塊不做什么”的負(fù)面清單。負(fù)面清單非常有用它劃定邊界的方式比正面描述更硬——比如“本模塊不做支付通道的業(yè)務(wù)校驗(yàn)”這句話能直接攔住一堆需求外溢。四個(gè)讀者的訴求對應(yīng)到文檔里就是四個(gè)必備板塊一句話職責(zé)、接口契約表、狀態(tài)機(jī)表、決策記錄表。這四塊占滿文檔基本就到了“詳細(xì)”的水位線。3. 詳細(xì)版模塊設(shè)計(jì)文檔的骨架從概述到上線清單3.1 文檔骨架與章節(jié)驗(yàn)收標(biāo)準(zhǔn)我日常會(huì)維護(hù)一份模塊設(shè)計(jì)文檔模板按固定順序排列章節(jié)每章都有明確的驗(yàn)收標(biāo)準(zhǔn)。下面這張表是我在當(dāng)前團(tuán)隊(duì)里推行的版本章節(jié)順序嚴(yán)格按照“先講清為什么 → 再講怎么對外 → 再講內(nèi)部怎么轉(zhuǎn) → 最后講怎么上線”的邏輯推進(jìn)。章節(jié)必填內(nèi)容驗(yàn)收標(biāo)準(zhǔn)模塊概述一句話職責(zé)、負(fù)面清單、模塊邊界圖文字版新人讀 5 分鐘能說出“這模塊干什么、不干什么”術(shù)語與縮寫模塊內(nèi)專用詞、業(yè)務(wù)黑話、簡稱全稱對照新人對接口描述里的縮寫不再產(chǎn)生歧義接口設(shè)計(jì)每個(gè)對外接口的入?yún)⒊鰠?、錯(cuò)誤碼、超時(shí)、重試上下游同事能直接按文檔寫聯(lián)調(diào)代碼不用再來問數(shù)據(jù)設(shè)計(jì)模塊持有的數(shù)據(jù)表或緩存鍵、數(shù)據(jù)歸屬、生命周期評審時(shí)能說清“這份數(shù)據(jù)歸誰管什么時(shí)候刪”狀態(tài)機(jī)設(shè)計(jì)核心狀態(tài)列表、狀態(tài)遷移表、非法遷移說明所有狀態(tài)流轉(zhuǎn)都覆蓋無非法遷移關(guān)鍵流程時(shí)序主流程、分支流程、異常流程的時(shí)序描述流程圖缺失時(shí)靠文字也能還原完整鏈路異常與兜底系統(tǒng)級異常、依賴不可用、數(shù)據(jù)不一致的兜底策略每個(gè)異常分支都有明確響應(yīng)動(dòng)作不是“報(bào)錯(cuò)就行”配置項(xiàng)開關(guān)、閾值、超時(shí)參數(shù)及其默認(rèn)值、修改影響運(yùn)維能不看代碼就知道改哪個(gè)配置有什么影響上線與回滾清單依賴發(fā)布順序、數(shù)據(jù)庫變更、回滾步驟按清單操作可獨(dú)立完成上線不需要再找研發(fā)這是整體框架實(shí)際寫的時(shí)候每一節(jié)都要落到具體對象上不要出現(xiàn)“保證系統(tǒng)穩(wěn)定”“提高用戶體驗(yàn)”這種空話。比如接口設(shè)計(jì)里寫“查詢訂單詳情”要寫成“根據(jù) orderId 查詢訂單主檔返回訂單狀態(tài)、金額、支付時(shí)間orderId 為空時(shí)返回 PARAM_ERROR 錯(cuò)誤碼”這才是能指導(dǎo)聯(lián)調(diào)的細(xì)節(jié)。3.2 每一個(gè)章節(jié)里的“細(xì)節(jié)密度”怎么控制框架定了關(guān)鍵是怎么填。我的經(jīng)驗(yàn)是按“場景密度”來控制一個(gè)章節(jié)如果描述了超過 3 個(gè)分支場景就要考慮拆分或配表格不要連續(xù)寫大段散文。比如接口設(shè)計(jì)中入?yún)⑿r?yàn)、業(yè)務(wù)校驗(yàn)、依賴超時(shí)、依賴返回異常這四個(gè)場景可以拆成一張四列的表每列寫各自的返回碼和預(yù)期行為這樣寫比純文字節(jié)省一半篇幅信息量卻多一倍。另一個(gè)密度控制原則是“能畫表就不寫句子能用句子就不堆術(shù)語”。數(shù)據(jù)設(shè)計(jì)也不例外每個(gè)緩存鍵的格式、TTL、失效策略、寫入時(shí)機(jī)都要寫全。舉一個(gè)真實(shí)發(fā)生過的案例某個(gè)模塊的緩存鍵由“userId 商品渠道”拼接而成設(shè)計(jì)文檔里只寫了“緩存商品信息”沒寫鍵的拼接規(guī)則結(jié)果另一個(gè)團(tuán)隊(duì)復(fù)用這個(gè)緩存服務(wù)時(shí)按自己的鍵格式去查查不到數(shù)據(jù)最后排查了兩天才發(fā)現(xiàn)是鍵格式不一致。這個(gè)坑完全可以用文檔里加一行“緩存鍵規(guī)則order_channel:{channel}:{userId}”來避免。配置項(xiàng)章節(jié)往往是被低估的。詳細(xì)文檔里要給每個(gè)配置項(xiàng)配一張小表寫清楚配置名、默認(rèn)值、單位、取值范圍、變更后是否需要重啟、影響面。尤其要寫“這個(gè)配置對什么業(yè)務(wù)有影響”這句話在線上緊急調(diào)參時(shí)能救命。比如某個(gè)模塊的超時(shí)配置默認(rèn) 3000ms變更影響寫“影響到所有通過該模塊轉(zhuǎn)發(fā)請求的鏈路調(diào)低會(huì)加劇超時(shí)錯(cuò)誤調(diào)高會(huì)拖慢上游響應(yīng)”運(yùn)維一看就知道不能隨便動(dòng)。最后上線與回滾清單要寫得像手術(shù)前核對單發(fā)布這個(gè)模塊前必須先發(fā)布依賴模塊、數(shù)據(jù)庫變更腳本要在什么階段執(zhí)行、失敗了第一步做什么第二步做什么。不少團(tuán)隊(duì)把回滾寫成語焉不詳?shù)摹盎貪L到上一版本”這是不合格的?;貪L清單必須具體到“執(zhí)行哪個(gè)腳本、需要改哪個(gè)配置開關(guān)、依賴方是否需要同步切回”每一條都寫成可直接執(zhí)行的命令級描述。4. 把關(guān)鍵設(shè)計(jì)視圖寫厚時(shí)序、契約、狀態(tài)機(jī)三板斧4.1 時(shí)序流程用“誰在什么條件下調(diào)用了誰”替代流程圖很多模塊文檔里貼一張非常漂亮的時(shí)序圖但看懂圖的人不多。圖不是不能放而是必須搭配文字化的“調(diào)用鏈描述”。我習(xí)慣用的是“起點(diǎn) → 觸發(fā)條件 → 調(diào)用方 → 被調(diào)方 → 返回路徑 → 失敗分支”這個(gè)六要素模板每個(gè)流程寫一段結(jié)構(gòu)化文字比圖更容易在評審里被挑出漏洞。舉個(gè)例子寫“退款發(fā)起流程”時(shí)完整描述應(yīng)該是起點(diǎn)是用戶在退款頁點(diǎn)擊提交觸發(fā)條件是訂單狀態(tài)為已支付且退款單未存在調(diào)用方是退款服務(wù)被調(diào)方是支付網(wǎng)關(guān)退款接口正常返回路徑是拿到退款受理號將退款單狀態(tài)置為“受理中”失敗分支是網(wǎng)關(guān)返回余額不足此時(shí)將退款單狀態(tài)置為“失敗”同時(shí)記錄失敗原因。這段文字里任何一個(gè)環(huán)節(jié)缺失都會(huì)導(dǎo)致聯(lián)調(diào)時(shí)發(fā)現(xiàn)狀態(tài)錯(cuò)亂或分支遺漏。異常流程的時(shí)序描述最為重要也是詳細(xì)文檔拉開差距的地方。寫超時(shí)場景時(shí)不能只寫“超時(shí)則失敗”要寫清楚“調(diào)用方超時(shí)設(shè)置是多少超時(shí)后是立即重試還是進(jìn)入重試隊(duì)列重試次數(shù)上限幾次最終失敗后是否有補(bǔ)償任務(wù)掃單”。這一層寫明白后續(xù)做穩(wěn)定性建設(shè)時(shí)根本不用再考古代碼查文檔就能批量梳理薄弱點(diǎn)。需要明確的是時(shí)序章節(jié)要為主流程和分支流程分別編號比如 FLOW-01 是主流程FLOW-01-EXC-01 是第一個(gè)異常分支評審和排障時(shí)直接引用編號溝通效率會(huì)明顯提升。這個(gè)編號習(xí)慣能倒逼寫文檔的人把流程想全因?yàn)榫幉幌氯ヌ柾ǔ>褪橇鞒虥]想清楚。4.2 接口契約表字段、錯(cuò)誤碼、超時(shí)與重試一起定接口設(shè)計(jì)是模塊文檔里對外價(jià)值最高的部分一定要用表格呈現(xiàn)。我常用的接口契約表字段包括接口名稱、方向?qū)?nèi)/對外、入?yún)⒚c類型、出參名與類型、錯(cuò)誤碼、超時(shí)默認(rèn)值、是否允許重試、冪等設(shè)計(jì)。下面是一張簡化示例接口入?yún)⒊鰠㈠e(cuò)誤碼超時(shí)重試與冪等createTasktaskName, planIdtaskIdPARAM_ERROR / DUP_TASK / SYSTEM_ERROR2000ms不帶重試調(diào)用方需傳 requestId重復(fù)提交返回原 taskIdqueryTaskStatustaskIdstatus, percentTASK_NOT_FOUND1000ms允許重試一次查詢天然冪等cancelTasktaskId, reasoncancelIdTASK_DONE / TASK_CANCELED3000ms不重試cancelId 用于對賬這張表里最有價(jià)值的兩個(gè)字段是“重試與冪等”和“錯(cuò)誤碼”。很多系統(tǒng)出故障問題不在功能邏輯而在調(diào)用方不知道“能不能重試、重試會(huì)不會(huì)重復(fù)創(chuàng)建數(shù)據(jù)”。文檔里這個(gè)字段寫清楚直接省去大量聯(lián)調(diào)摩擦。我見過某公司的內(nèi)部接口沒有冪等設(shè)計(jì)調(diào)用方按慣性重試了三次結(jié)果創(chuàng)建了三條重復(fù)訂單最后靠人工對賬才清理掉。這種問題在接口契約表里加一行“冪等要求requestId 唯一重復(fù)請求返回原結(jié)果”就能從源頭上避免。錯(cuò)誤碼的定義也要細(xì)化。三段式錯(cuò)誤碼是比較通用的做法第一位表示錯(cuò)誤來源參數(shù)、業(yè)務(wù)、依賴、系統(tǒng)第二位表示具體模塊后兩位表示錯(cuò)誤細(xì)項(xiàng)。比如“業(yè)務(wù)錯(cuò)-訂單模塊-訂單不存在”的編碼是 20001。文檔里要列出每個(gè)錯(cuò)誤碼對應(yīng)的 HTTP 狀態(tài)碼如果有、對外提示文案、日志打印級別、是否需要告警。錯(cuò)誤碼表寫不全線上排障時(shí)最痛苦因?yàn)殄e(cuò)誤信息往往只有一個(gè)編號翻代碼才知道是什么意思。4.3 狀態(tài)機(jī)表把最容易吵架的地方提前釘死狀態(tài)機(jī)是模塊文檔里最容易寫飄的部分也是評審時(shí)最容易產(chǎn)生爭議的部分。我的做法是不畫狀態(tài)圖直接畫狀態(tài)遷移表因?yàn)楸砀癖葓D形更不容易遺漏非法遷移。狀態(tài)遷移表至少包含四列當(dāng)前狀態(tài)、觸發(fā)動(dòng)作、目標(biāo)狀態(tài)、前置條件。如果動(dòng)作涉及外部依賴還要再加一列“失敗時(shí)保持原狀還是進(jìn)入特殊狀態(tài)”。以任務(wù)調(diào)度模塊為例核心狀態(tài)表大致長這樣當(dāng)前狀態(tài)觸發(fā)動(dòng)作目標(biāo)狀態(tài)前置條件待執(zhí)行調(diào)度器掃單命中執(zhí)行中任務(wù)未過期且執(zhí)行節(jié)點(diǎn)空閑執(zhí)行中執(zhí)行成功回調(diào)已完成回調(diào)簽名校驗(yàn)通過執(zhí)行中執(zhí)行超時(shí)待重試已超過單次執(zhí)行時(shí)長上限待重試重試次數(shù)未達(dá)上限且間隔到期執(zhí)行中重試次數(shù) 最大次數(shù)待重試重試次數(shù)已達(dá)上限失敗重試次數(shù) 最大次數(shù)這張表每個(gè)開發(fā)者的理解成本都很低評審時(shí)大家盯著表逐行過很快就能發(fā)現(xiàn)遺漏。比如“取消中的任務(wù)”這個(gè)狀態(tài)如果表里沒有就會(huì)在后端代碼里出現(xiàn)“已取消的任務(wù)被調(diào)度器再次拉起執(zhí)行”這種詭異缺陷。狀態(tài)遷移表寫完之后我還會(huì)加一個(gè)“非法遷移清單”明確列出不允許發(fā)生的遷移路徑例如“已完成 → 執(zhí)行中”“失敗 → 待執(zhí)行”這相當(dāng)于給狀態(tài)機(jī)上了約束后續(xù)代碼實(shí)現(xiàn)時(shí)可以直接對照校驗(yàn)。數(shù)據(jù)設(shè)計(jì)章節(jié)放在狀態(tài)機(jī)之后也很自然。模塊持有的數(shù)據(jù)表、緩存鍵、消息隊(duì)列主題都應(yīng)該有歸屬聲明。一條數(shù)據(jù)如果多個(gè)模塊都能改必須寫明“寫權(quán)限僅歸本模塊其他模塊只能通過接口修改”這是預(yù)防數(shù)據(jù)混亂的最強(qiáng)聲明。5. 模塊設(shè)計(jì)文檔的四個(gè)常見坑從寫不出來到寫了沒人看5.1 坑一文檔寫完就“過期”代碼一改文檔成廢紙現(xiàn)象文檔里描述的接口和實(shí)際代碼對不上新人看文檔寫聯(lián)調(diào)代碼一調(diào)就報(bào)錯(cuò)最后只能放棄文檔直接翻代碼。原因?qū)懳臋n的人把文檔當(dāng)“一次性交付物”發(fā)布后就不再維護(hù)團(tuán)隊(duì)也沒有把文檔更新納入迭代完成的定義。解決把“文檔同步更新”寫進(jìn)迭代完成條件只要接口參數(shù)、狀態(tài)機(jī)、超時(shí)配置變了對應(yīng)文檔必須同 PR 提交評審。沒有這個(gè)硬約束再詳細(xì)的文檔也會(huì)在三個(gè)月內(nèi)變成坑。我個(gè)人的習(xí)慣是代碼里定義了新錯(cuò)誤碼或改了字段名那天的文檔修改一定和代碼改動(dòng)同時(shí)提交這個(gè)習(xí)慣養(yǎng)成了文檔的保鮮期會(huì)大幅延長。5.2 坑二文檔寫成了代碼的“鏡像”翻文檔等于翻代碼現(xiàn)象模塊文檔把類名、方法名、字段名全部抄一遍代碼怎么組織文檔就怎么寫讀者看完依然不知道模塊的邊界和取舍。原因作者誤以為“詳細(xì)”就是“全面”沒有識(shí)別哪些信息是能從代碼里直接讀出來的哪些信息是代碼里讀不出來的。解決文檔只寫“代碼里讀不到的信息”——設(shè)計(jì)決策、備選方案、邊界原因、異常權(quán)衡、配置影響。類名和方法名在 IDE 里一鍵就能看到寫進(jìn)文檔純屬浪費(fèi)紙張。我寫文檔時(shí)會(huì)先自問一句這一段內(nèi)容新人讀代碼能不能自己發(fā)現(xiàn)如果能刪掉如果不能認(rèn)真寫。用這個(gè)標(biāo)準(zhǔn)過濾文檔篇幅會(huì)縮掉一半但信息濃度反而更高。5.3 坑三狀態(tài)機(jī)和異常分支“寫不全”評審沒人看出來現(xiàn)象文檔里的狀態(tài)機(jī)缺了一個(gè)“重試中”狀態(tài)異常分支只寫了成功路徑和參數(shù)錯(cuò)誤缺了依賴超時(shí)和依賴返回異常。評審會(huì)上大家掃一眼沒人深究結(jié)果上線后狀態(tài)卡死只能臨時(shí)補(bǔ)代碼。原因?qū)懳臋n的人按“腦子里已有的實(shí)現(xiàn)”來寫沒按“系統(tǒng)在極端條件下所有可能路徑”來寫評審的人則默認(rèn)作者已經(jīng)想全了不做窮舉式挑戰(zhàn)。解決評審文檔時(shí)按“惡意用戶 故障注入”的心態(tài)逐行拷問某個(gè)外部接口超時(shí)了怎么辦消息消費(fèi)失敗了會(huì)導(dǎo)致哪兩個(gè)狀態(tài)不一致重復(fù)推送同一事件會(huì)觸發(fā)什么遷移每問一個(gè)就往表里補(bǔ)一行。這個(gè)過程沒有捷徑只有用窮舉法把狀態(tài)遷移表、異常分支表寫到?jīng)]有空行。我自己的經(jīng)驗(yàn)是狀態(tài)表和分支表至少各寫兩輪第一輪按正常邏輯寫第二輪按故障假設(shè)寫。5.4 坑四文檔寫成“散文”關(guān)鍵細(xì)節(jié)淹沒在大段文字里現(xiàn)象接口描述藏在第三段話的中間錯(cuò)誤碼表放在附錄里新人找“超時(shí)時(shí)間是多少”找了兩分鐘沒找到最后選擇不看了。原因作者沒有按用途組織信息也沒有為高頻查詢場景設(shè)置目錄錨點(diǎn)和表格索引。解決把“對外接口”“配置項(xiàng)”“錯(cuò)誤碼”三類高頻查詢信息分別用獨(dú)立表格呈現(xiàn)并在文檔開頭的目錄說明里加一句“查接口看第 3 節(jié)、查配置看第 7 節(jié)、查錯(cuò)誤碼看附錄 A”。一張能 30 秒定位到答案的文檔比一篇寫滿兩萬字但找不到出口的文檔有用得多。我現(xiàn)在寫每個(gè)模塊文檔時(shí)都要求自己做到一個(gè)只帶著“線上報(bào)錯(cuò)錯(cuò)誤碼 20001”訴求來的值班同學(xué)能在 1 分鐘內(nèi)查到錯(cuò)誤碼含義和對應(yīng)動(dòng)作。這個(gè)標(biāo)準(zhǔn)不達(dá)標(biāo)就不算寫完。6. 一個(gè)能讓文檔真正值錢的技巧用“新人自測”檢驗(yàn)信息完整度我堅(jiān)持用了很久的文檔驗(yàn)收方法是“新人自測”找一個(gè)對這個(gè)模塊完全不了解的同事只給文檔、不給代碼讓他按文檔描述實(shí)現(xiàn)一個(gè)最小調(diào)用程序或者模擬一次完整聯(lián)調(diào)流程。自測目標(biāo)不是讓他能寫出生產(chǎn)級代碼而是看他能否回答三個(gè)問題——模塊的入口在哪、錯(cuò)誤碼去哪查、狀態(tài)流轉(zhuǎn)里哪一步我可能會(huì)踩坑。如果這些問題文檔回答不了說明信息密度和結(jié)構(gòu)都還有缺口需要補(bǔ)的不是字?jǐn)?shù)而是關(guān)鍵索引。這個(gè)技巧會(huì)反向塑造寫文檔的習(xí)慣因?yàn)橹牢臋n會(huì)被別人當(dāng)“唯一資料”來用寫的時(shí)候就會(huì)主動(dòng)把字段表寫全、把狀態(tài)表寫到無遺漏、把異常分支補(bǔ)齊、把配置說明獨(dú)立成節(jié)。我過去寫模塊文檔總是先寫流程再補(bǔ)接口現(xiàn)在改成先列接口和狀態(tài)再穿插流程后一種順序明顯更容易暴露“邏輯斷點(diǎn)”——比如發(fā)現(xiàn)某個(gè)流程里調(diào)用了 “queryInfo” 接口但接口表里根本沒有這個(gè)接口那要么是流程圖錯(cuò)了要么是接口表漏了當(dāng)場就能修正。另一個(gè)值得堅(jiān)持的習(xí)慣是“變更留痕”。每次因?yàn)榫€上問題修改了某個(gè)設(shè)計(jì)決策例如重試閾值從 3 次改成 1 次我都在文檔對應(yīng)位置加一行變更記錄寫明變更時(shí)間、原因、影響范圍。這不會(huì)花費(fèi)太多時(shí)間但半年后復(fù)盤穩(wěn)定性問題時(shí)這些記錄會(huì)自動(dòng)變成一張“決策演變時(shí)間線”比靠人腦回憶可靠得多。我也經(jīng)歷過不看文檔直接改代碼的時(shí)期直到有一次重構(gòu)時(shí)誤刪了一個(gè)關(guān)鍵狀態(tài)分支線上數(shù)據(jù)錯(cuò)亂靠著一張舊文檔才反推出被誤刪的邏輯從那以后我再也不敢把文檔當(dāng)擺設(shè)。這不只是給團(tuán)隊(duì)寫更是給一個(gè)月后的自己寫。詳細(xì)文檔真正的價(jià)值是當(dāng)你已經(jīng)忘了這段代碼的時(shí)候它還能準(zhǔn)確告訴你“當(dāng)初是怎么想的、什么不能動(dòng)、為什么”。希望這篇拆解能幫到你也幫到你團(tuán)隊(duì)的下一任接手人。本文還有配套的精品資源點(diǎn)擊獲取