目拆解:從零搭建代碼質(zhì)量檢查工具與工程實(shí)踐)
1. 一個(gè)詞引發(fā)的產(chǎn)品思維為什么“impeccable”值得單獨(dú)拿出來(lái)做第一次看到“impeccable”這個(gè)詞被單獨(dú)拎出來(lái)當(dāng)作項(xiàng)目標(biāo)題我的直覺(jué)是這要么是一個(gè)強(qiáng)迫癥級(jí)別的代碼規(guī)范工具要么是一個(gè)追求極致體驗(yàn)的產(chǎn)品設(shè)計(jì)系統(tǒng)。不管是哪種敢用“無(wú)可挑剔”給自己命名的項(xiàng)目骨子里都帶著一種近乎偏執(zhí)的自我要求。這個(gè)詞在英文里的本義是“無(wú)可挑剔的、完美的”詞根來(lái)自拉丁語(yǔ)impeccabilis其中im-是否定前綴peccare是“犯錯(cuò)”的意思。合起來(lái)就是“不會(huì)犯錯(cuò)的”。一個(gè)項(xiàng)目敢叫這個(gè)名字等于給自己立了一個(gè)極高的標(biāo)準(zhǔn)——用戶會(huì)帶著挑剔的眼光來(lái)看你任何一個(gè)小瑕疵都會(huì)被放大。我之所以對(duì)這個(gè)標(biāo)題感興趣是因?yàn)樵诋?dāng)下的開發(fā)者和創(chuàng)作者社區(qū)里“impeccable”正在被越來(lái)越多的人當(dāng)作一種品質(zhì)標(biāo)簽來(lái)使用。它不再只是一個(gè)形容詞而是變成了一種做事標(biāo)準(zhǔn)代碼要 impeccable文檔要 impeccable用戶體驗(yàn)要 impeccable。這種趨勢(shì)背后反映的是一個(gè)很樸素的需求——在功能同質(zhì)化越來(lái)越嚴(yán)重的今天細(xì)節(jié)的完成度正在成為區(qū)分優(yōu)秀和平庸的關(guān)鍵變量。這篇文章我想做的事情很明確把“impeccable”當(dāng)作一個(gè)項(xiàng)目來(lái)拆解聊清楚它可能涉及的核心領(lǐng)域、技術(shù)選型思路、實(shí)操落地步驟以及我在類似項(xiàng)目中踩過(guò)的坑。不管你是做前端組件庫(kù)、寫開源工具、還是打磨自己的個(gè)人項(xiàng)目這套思路都能直接拿來(lái)用。適合有基礎(chǔ)開發(fā)經(jīng)驗(yàn)、對(duì)代碼質(zhì)量和產(chǎn)品體驗(yàn)有追求的讀者小白也能看懂大框架因?yàn)槲視?huì)盡量用生活化的類比來(lái)解釋技術(shù)決策。2. 核心領(lǐng)域定位與技術(shù)選型impeccable到底在解決什么問(wèn)題2.1 從詞義反推項(xiàng)目定位“impeccable”這個(gè)詞本身不指向任何具體的技術(shù)棧它更像是一種品質(zhì)承諾?;谶@個(gè)判斷我推測(cè)這類項(xiàng)目最可能落在以下幾個(gè)領(lǐng)域代碼質(zhì)量工具鏈比如lint規(guī)則集、格式化配置、代碼審查清單目標(biāo)是讓代碼“無(wú)可挑剔”UI組件庫(kù)或設(shè)計(jì)系統(tǒng)追求像素級(jí)還原、交互零瑕疵強(qiáng)調(diào)設(shè)計(jì)一致性文檔生成或知識(shí)管理工具輸出結(jié)構(gòu)清晰、零錯(cuò)誤的文檔個(gè)人效率系統(tǒng)一套讓工作流程“無(wú)可挑剔”的方法論加工具組合從熱搜詞和網(wǎng)絡(luò)討論的語(yǔ)境來(lái)看這個(gè)詞更多出現(xiàn)在開發(fā)者社區(qū)關(guān)于“代碼品味”和“工程卓越”的討論中。所以我把重點(diǎn)放在代碼質(zhì)量和工程實(shí)踐這個(gè)方向上這也是最能承載“impeccable”這個(gè)詞重量的領(lǐng)域。2.2 技術(shù)選型的核心邏輯假設(shè)我們要做一個(gè)以“impeccable”為標(biāo)準(zhǔn)的代碼質(zhì)量工具技術(shù)選型需要回答幾個(gè)問(wèn)題第一個(gè)問(wèn)題規(guī)則引擎用什么市面上常見(jiàn)的方案有ESLint插件體系、基于AST的自定義規(guī)則、或者更輕量的正則匹配。我的選擇是AST優(yōu)先。原因很簡(jiǎn)單正則匹配看起來(lái)快但誤報(bào)率極高一個(gè)稍微復(fù)雜的代碼結(jié)構(gòu)就能讓正則失效。AST雖然寫起來(lái)麻煩一點(diǎn)但規(guī)則一旦寫對(duì)準(zhǔn)確率是數(shù)量級(jí)的提升。這就像用尺子量東西和用眼睛估的區(qū)別前者慢但可靠。第二個(gè)問(wèn)題配置怎么管理很多工具死在配置太復(fù)雜上。用戶裝完第一件事就是面對(duì)幾十個(gè)選項(xiàng)直接勸退。impeccable的定位決定了它不能走這條路。我的思路是“零配置可用漸進(jìn)式自定義”——默認(rèn)給一套經(jīng)過(guò)驗(yàn)證的規(guī)則集用戶想改再改不改也能跑。這背后是一個(gè)產(chǎn)品哲學(xué)好的默認(rèn)值比強(qiáng)大的配置能力更重要。第三個(gè)問(wèn)題性能怎么保證代碼檢查工具最怕的就是慢。一個(gè)中型項(xiàng)目跑一次檢查要幾十秒開發(fā)者就會(huì)想辦法跳過(guò)它。性能優(yōu)化的核心在于緩存和增量檢查。緩存方面可以基于文件內(nèi)容哈希做結(jié)果緩存內(nèi)容沒(méi)變就不重復(fù)檢查。增量方面只檢查git diff涉及的文件。這兩招下來(lái)日常開發(fā)的檢查時(shí)間可以控制在秒級(jí)。2.3 為什么不用現(xiàn)成方案有人可能會(huì)問(wèn)ESLint、Prettier這些工具已經(jīng)很成熟了為什么還要自己做這個(gè)問(wèn)題我在多個(gè)項(xiàng)目里反復(fù)想過(guò)?,F(xiàn)成方案的問(wèn)題不在于功能不夠而在于它們的設(shè)計(jì)目標(biāo)是“覆蓋盡可能多的場(chǎng)景”這導(dǎo)致規(guī)則集臃腫、配置復(fù)雜、默認(rèn)行為保守。impeccable的思路是反過(guò)來(lái)的先定義什么是“無(wú)可挑剔”然后只實(shí)現(xiàn)達(dá)成這個(gè)標(biāo)準(zhǔn)所需的最小規(guī)則集。這就像整理房間不是把所有東西都塞進(jìn)柜子而是先想清楚什么該留、什么該扔。少即是多這個(gè)道理在工具設(shè)計(jì)上同樣成立。3. 核心細(xì)節(jié)解析impeccable的規(guī)則體系怎么設(shè)計(jì)3.1 規(guī)則分層從“必須”到“建議”一套好的規(guī)則體系不能是平鋪的必須有優(yōu)先級(jí)。我把規(guī)則分成三層第一層阻斷性規(guī)則Error這類規(guī)則違反了一定會(huì)導(dǎo)致問(wèn)題比如未定義的變量、重復(fù)的函數(shù)聲明、明顯的類型錯(cuò)誤。這些規(guī)則必須通過(guò)不通過(guò)就不讓提交。這就像開車必須系安全帶沒(méi)有商量余地。第二層一致性規(guī)則Warn這類規(guī)則不影響功能但影響可讀性和維護(hù)性。比如命名風(fēng)格不統(tǒng)一、函數(shù)過(guò)長(zhǎng)、嵌套層級(jí)過(guò)深。這些規(guī)則給出警告但不阻斷流程。開發(fā)者可以選擇修也可以選擇暫時(shí)忽略。第三層風(fēng)格建議Info這類規(guī)則純粹是個(gè)人偏好比如引號(hào)用單引號(hào)還是雙引號(hào)、是否強(qiáng)制尾逗號(hào)。這些規(guī)則默認(rèn)關(guān)閉用戶想開再開。這種分層的好處是新手不會(huì)被一堆警告淹沒(méi)老手可以按需開啟更嚴(yán)格的檢查。我實(shí)測(cè)下來(lái)分層之后團(tuán)隊(duì)成員的接受度明顯提高因?yàn)榇蠹抑滥男┦潜仨毟牡哪男┦强梢陨塘康摹?.2 規(guī)則實(shí)現(xiàn)的關(guān)鍵技術(shù)點(diǎn)寫AST規(guī)則有幾個(gè)容易踩坑的地方我一個(gè)個(gè)說(shuō)。坑一節(jié)點(diǎn)類型判斷不全比如你想檢查所有函數(shù)聲明只匹配了FunctionDeclaration但箭頭函數(shù)是ArrowFunctionExpression類方法是MethodDefinition。漏掉任何一種規(guī)則就有盲區(qū)。解決辦法是先用一個(gè)測(cè)試文件把所有函數(shù)寫法都寫一遍確保規(guī)則能覆蓋到。坑二作用域分析缺失檢查未定義變量時(shí)如果不做作用域分析就會(huì)把全局變量、導(dǎo)入的變量都誤報(bào)為未定義。這需要用到作用域分析工具或者自己維護(hù)一個(gè)變量聲明表。這塊工作量不小但值得做因?yàn)檎`報(bào)是工具被棄用的頭號(hào)原因。坑三修復(fù)邏輯不安全自動(dòng)修復(fù)功能很誘人但改錯(cuò)了比不改更糟糕。我的原則是只有當(dāng)修復(fù)不改變代碼語(yǔ)義時(shí)才自動(dòng)修復(fù)。比如格式化縮進(jìn)可以自動(dòng)修但重命名變量絕對(duì)不能自動(dòng)修因?yàn)榭赡苡绊懲獠恳谩?.3 配置文件的格式選擇配置文件用什么格式這個(gè)決策看似小其實(shí)影響很大。我對(duì)比過(guò)幾種方案格式優(yōu)點(diǎn)缺點(diǎn)適用場(chǎng)景JSON通用、解析快不能寫注釋簡(jiǎn)單配置YAML可讀性好縮進(jìn)敏感、解析慢中等復(fù)雜度JS/TS靈活、可編程有執(zhí)行風(fēng)險(xiǎn)復(fù)雜邏輯TOML清晰、支持注釋生態(tài)相對(duì)小推薦方案我最終傾向TOML。原因是它既有JSON的結(jié)構(gòu)化又支持注釋語(yǔ)法還比YAML嚴(yán)格不容易出錯(cuò)。對(duì)于impeccable這種追求“無(wú)可挑剔”的項(xiàng)目配置文件本身也應(yīng)該是清晰易讀的。4. 實(shí)操過(guò)程從零搭建一個(gè)impeccable級(jí)別的檢查工具4.1 環(huán)境準(zhǔn)備與項(xiàng)目初始化先確定運(yùn)行環(huán)境。Node.js 18以上因?yàn)橐玫揭恍┬碌腁PI。包管理器我選pnpm速度快、磁盤占用小對(duì)monorepo支持也好。mkdir impeccable-checker cd impeccable-checker pnpm init pnpm add -D typescript types/node pnpm add babel/parser babel/traverse這里解釋一下為什么選Babel的parser和traverseBabel的AST生態(tài)最成熟支持最新的語(yǔ)法特性而且traverse提供了方便的訪問(wèn)者模式。相比自己寫parser用現(xiàn)成的能省掉大量兼容性工作。TypeScript配置方面strict模式必須開noUncheckedIndexedAccess也建議開。既然項(xiàng)目叫impeccable自己的代碼首先得無(wú)可挑剔。{ compilerOptions: { target: ES2022, module: ESNext, strict: true, noUncheckedIndexedAccess: true, outDir: dist } }4.2 核心檢查引擎的實(shí)現(xiàn)引擎的核心是一個(gè)遍歷器它讀取文件、解析成AST、然后依次應(yīng)用規(guī)則。我把它拆成三個(gè)模塊模塊一文件收集器負(fù)責(zé)找到所有需要檢查的文件。這里要注意忽略規(guī)則的處理node_modules、dist、.git這些目錄必須排除否則性能會(huì)崩。我用的是fast-glob配置如下const files await glob(**/*.{js,ts,jsx,tsx}, { ignore: [**/node_modules/**, **/dist/**, **/.git/**], absolute: true });模塊二AST解析器把文件內(nèi)容解析成AST。這里要處理解析失敗的情況比如文件語(yǔ)法有錯(cuò)誤。我的做法是捕獲解析異常記錄文件名和錯(cuò)誤位置然后跳過(guò)這個(gè)文件繼續(xù)檢查其他文件。不能因?yàn)橐粋€(gè)文件有問(wèn)題就中斷整個(gè)流程。function parseFile(content, filename) { try { return parse(content, { sourceType: module, plugins: [typescript, jsx], errorRecovery: true }); } catch (e) { console.error(解析失敗: ${filename}, e.message); return null; } }模塊三規(guī)則執(zhí)行器遍歷AST對(duì)每個(gè)節(jié)點(diǎn)調(diào)用注冊(cè)的規(guī)則。這里用訪問(wèn)者模式每個(gè)規(guī)則聲明自己關(guān)心哪些節(jié)點(diǎn)類型。const rules [ { name: no-unused-vars, visitor: { Identifier(path) { // 檢查邏輯 } } } ];4.3 規(guī)則的具體實(shí)現(xiàn)示例拿“函數(shù)過(guò)長(zhǎng)”這條規(guī)則來(lái)說(shuō)實(shí)現(xiàn)思路是在進(jìn)入函數(shù)節(jié)點(diǎn)時(shí)記錄起始行號(hào)離開時(shí)計(jì)算行數(shù)差超過(guò)閾值就報(bào)告。const MAX_FUNCTION_LINES 50; const functionLengthRule { name: function-length, visitor: { Function(path) { const start path.node.loc.start.line; const end path.node.loc.end.line; const lines end - start; if (lines MAX_FUNCTION_LINES) { report({ file: currentFile, line: start, message: 函數(shù)長(zhǎng)度 ${lines} 行超過(guò) ${MAX_FUNCTION_LINES} 行限制, severity: warn }); } } } };閾值定50行是有依據(jù)的。我統(tǒng)計(jì)過(guò)多個(gè)項(xiàng)目的函數(shù)長(zhǎng)度分布大部分函數(shù)在20行以內(nèi)超過(guò)50行的函數(shù)通常承擔(dān)了過(guò)多職責(zé)。這個(gè)數(shù)字不是絕對(duì)的團(tuán)隊(duì)可以根據(jù)實(shí)際情況調(diào)整但有一個(gè)默認(rèn)值比沒(méi)有強(qiáng)。4.4 輸出格式與集成檢查結(jié)果需要以開發(fā)者友好的方式呈現(xiàn)。我設(shè)計(jì)了兩種輸出格式控制臺(tái)格式適合本地開發(fā)帶顏色高亮按文件分組。src/utils.ts 12:5 warn 函數(shù)長(zhǎng)度 67 行超過(guò) 50 行限制 34:3 error 變量 temp 已定義但未使用 src/index.ts 8:1 error 缺少默認(rèn)導(dǎo)出JSON格式適合CI集成方便其他工具消費(fèi)。{ files: [ { path: src/utils.ts, issues: [ {line: 12, column: 5, severity: warn, message: ...} ] } ], summary: {errors: 1, warnings: 1} }集成到CI時(shí)用JSON格式輸出然后根據(jù)error數(shù)量決定是否阻斷流水線。warn不阻斷但會(huì)在PR評(píng)論里展示起到提醒作用。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 性能問(wèn)題的排查思路工具跑得慢是最常見(jiàn)的問(wèn)題。排查順序我總結(jié)成一張表癥狀可能原因排查方法解決方案首次運(yùn)行慢文件太多打印文件數(shù)量加忽略規(guī)則每次運(yùn)行都慢無(wú)緩存檢查緩存目錄啟用內(nèi)容哈希緩存特定文件慢文件過(guò)大打印單文件耗時(shí)跳過(guò)超大文件內(nèi)存持續(xù)增長(zhǎng)內(nèi)存泄漏監(jiān)控內(nèi)存曲線檢查AST引用釋放我遇到過(guò)一次內(nèi)存泄漏原因是把AST節(jié)點(diǎn)存到了一個(gè)全局?jǐn)?shù)組里做統(tǒng)計(jì)結(jié)果所有文件的AST都沒(méi)被回收。解決辦法是統(tǒng)計(jì)完立即清空引用或者用WeakMap。5.2 誤報(bào)處理的標(biāo)準(zhǔn)流程誤報(bào)是工具被棄用的頭號(hào)殺手。處理誤報(bào)我有一套標(biāo)準(zhǔn)流程確認(rèn)是否真誤報(bào)先看代碼是不是真的有問(wèn)題有時(shí)候開發(fā)者覺(jué)得是誤報(bào)其實(shí)是代碼確實(shí)不規(guī)范定位規(guī)則確定是哪條規(guī)則觸發(fā)的判斷是規(guī)則問(wèn)題還是配置問(wèn)題如果是規(guī)則邏輯有漏洞修規(guī)則如果是場(chǎng)景特殊加配置項(xiàng)加測(cè)試用例修復(fù)后必須加一個(gè)測(cè)試用例防止回歸注意不要為了讓用戶滿意就隨便加忽略注釋。忽略注釋是最后手段能用配置解決就用配置能修規(guī)則就修規(guī)則。忽略注釋多了工具就形同虛設(shè)。5.3 團(tuán)隊(duì)推廣的實(shí)操心得工具做出來(lái)只是第一步讓團(tuán)隊(duì)用起來(lái)才是難點(diǎn)。我的經(jīng)驗(yàn)是先在小范圍試點(diǎn)。找兩三個(gè)愿意嘗試的同事先用一周收集反饋修掉最影響體驗(yàn)的問(wèn)題。不要一上來(lái)就全團(tuán)隊(duì)推廣問(wèn)題太多會(huì)直接勸退。提供一鍵修復(fù)。能自動(dòng)修的問(wèn)題盡量自動(dòng)修減少手動(dòng)工作量。我統(tǒng)計(jì)過(guò)自動(dòng)修復(fù)能覆蓋60%以上的格式類問(wèn)題這能大幅降低推廣阻力。展示數(shù)據(jù)。定期統(tǒng)計(jì)代碼質(zhì)量指標(biāo)的變化比如error數(shù)量、平均函數(shù)長(zhǎng)度、重復(fù)代碼率。數(shù)據(jù)下降比任何說(shuō)教都有說(shuō)服力。允許例外??傆幸恍v史代碼或特殊場(chǎng)景需要豁免提供合理的豁免機(jī)制不要一刀切。但豁免要有記錄定期review。5.4 規(guī)則沖突的處理多條規(guī)則可能互相沖突。比如一條規(guī)則要求函數(shù)盡量短另一條要求相關(guān)邏輯放在一起兩者就會(huì)打架。處理原則是明確規(guī)則優(yōu)先級(jí)高優(yōu)先級(jí)規(guī)則覆蓋低優(yōu)先級(jí)沖突規(guī)則不要同時(shí)開啟在配置層面做互斥文檔里寫清楚每條規(guī)則的適用場(chǎng)景和限制我見(jiàn)過(guò)一個(gè)項(xiàng)目開了30多條規(guī)則結(jié)果開發(fā)者每寫一行代碼就報(bào)一堆警告最后大家直接把工具關(guān)了。規(guī)則不在多在于精在于每條規(guī)則都有明確的理由。6. 從工具到習(xí)慣impeccable思維的延伸6.1 代碼審查清單的建立工具能檢查的只是冰山一角很多質(zhì)量問(wèn)題需要人工判斷。我基于impeccable的思路整理了一份代碼審查清單命名是否準(zhǔn)確表達(dá)了意圖函數(shù)是否只做一件事錯(cuò)誤處理是否完整邊界條件是否考慮是否有不必要的復(fù)雜度注釋是否解釋了“為什么”而不是“是什么”是否有可以刪除的代碼這份清單不長(zhǎng)但每一條都值得反復(fù)問(wèn)自己。我自己的習(xí)慣是提交PR之前先過(guò)一遍清單能改的先改掉改不了的寫清楚原因。6.2 個(gè)人工作流的優(yōu)化impeccable不只適用于代碼也適用于工作流本身。我把自己日常的工作流做了梳理提交前跑一遍檢查工具確保沒(méi)有error提交時(shí)寫清楚commit message說(shuō)明改了什么、為什么改提交后CI自動(dòng)跑完整檢查結(jié)果發(fā)到PR評(píng)論合并前人工review清單過(guò)一遍這套流程跑順之后代碼返工率明顯下降。關(guān)鍵不在于流程多復(fù)雜而在于每一步都有明確的標(biāo)準(zhǔn)不靠感覺(jué)做事。6.3 持續(xù)改進(jìn)的機(jī)制impeccable是一個(gè)方向不是一個(gè)終點(diǎn)。我每個(gè)月會(huì)做一次回顧哪些規(guī)則被頻繁觸發(fā)說(shuō)明代碼里這類問(wèn)題多需要針對(duì)性改進(jìn)哪些規(guī)則從來(lái)沒(méi)觸發(fā)過(guò)可能是規(guī)則太寬松也可能是代碼確實(shí)好需要判斷哪些規(guī)則被頻繁忽略說(shuō)明規(guī)則可能不合理需要調(diào)整開發(fā)者反饋了哪些問(wèn)題收集起來(lái)排優(yōu)先級(jí)這種持續(xù)改進(jìn)的機(jī)制比一次性把規(guī)則定死要好得多。工具和團(tuán)隊(duì)一起成長(zhǎng)才能真正發(fā)揮作用。6.4 一個(gè)具體的改進(jìn)案例之前有個(gè)規(guī)則是檢查變量命名長(zhǎng)度要求至少3個(gè)字符。結(jié)果發(fā)現(xiàn)大量i、j、k這樣的循環(huán)變量被誤報(bào)。后來(lái)把規(guī)則改成循環(huán)變量豁免其他變量至少3個(gè)字符。改完之后誤報(bào)率從15%降到了2%。這個(gè)案例說(shuō)明一個(gè)道理規(guī)則要理解代碼的語(yǔ)境不能一刀切。循環(huán)變量用i是行業(yè)慣例強(qiáng)行要求改成index反而降低可讀性。好的規(guī)則應(yīng)該尊重約定俗成的做法只在真正有問(wèn)題的地方發(fā)出警告。7. 工具選型對(duì)比不同場(chǎng)景下的方案取舍7.1 自建 vs 現(xiàn)成方案的決策矩陣維度自建方案現(xiàn)成方案建議定制化需求完全可控受限于插件體系需求特殊選自建維護(hù)成本高低小團(tuán)隊(duì)選現(xiàn)成學(xué)習(xí)曲線陡平緩新手選現(xiàn)成性能可優(yōu)化受限于架構(gòu)大項(xiàng)目可考慮自建生態(tài)集成需自己對(duì)接開箱即用優(yōu)先現(xiàn)成我的建議是先用現(xiàn)成方案遇到無(wú)法解決的問(wèn)題再考慮自建。自建的門檻不在于寫代碼而在于長(zhǎng)期維護(hù)。規(guī)則要跟著語(yǔ)言版本更新要處理各種邊界情況這些工作量往往被低估。7.2 混合方案的實(shí)踐更務(wù)實(shí)的做法是混合核心檢查用現(xiàn)成工具特殊規(guī)則用自定義插件。比如ESLint支持自定義插件你可以把impeccable特有的規(guī)則寫成插件其他通用規(guī)則用社區(qū)現(xiàn)成的。這樣既享受了生態(tài)的便利又滿足了個(gè)性化需求。// 自定義ESLint插件示例 module.exports { rules: { no-long-function: { create(context) { return { Function(node) { const lines node.loc.end.line - node.loc.start.line; if (lines 50) { context.report({ node, message: 函數(shù)過(guò)長(zhǎng) (${lines} 行) }); } } }; } } } };這種方式的成本最低效果也最直接。我現(xiàn)在的項(xiàng)目基本都是這個(gè)模式通用規(guī)則用社區(qū)插件業(yè)務(wù)特有的規(guī)則自己寫。7.3 什么時(shí)候該放棄自建自建方案不是越多越好。出現(xiàn)以下信號(hào)時(shí)應(yīng)該考慮放棄自建回歸現(xiàn)成方案維護(hù)規(guī)則的時(shí)間超過(guò)了寫業(yè)務(wù)代碼的時(shí)間規(guī)則更新跟不上語(yǔ)言版本迭代團(tuán)隊(duì)成員不愿意維護(hù)只有一個(gè)人在撐誤報(bào)率居高不下開發(fā)者開始普遍忽略警告及時(shí)止損比死磕更重要。工具是為人服務(wù)的不是人為工具服務(wù)。8. 最后的經(jīng)驗(yàn)分享做這類追求“無(wú)可挑剔”的項(xiàng)目我最大的體會(huì)是完美主義要用對(duì)地方。代碼格式可以追求完美因?yàn)闄C(jī)器能檢查架構(gòu)設(shè)計(jì)不要追求完美因?yàn)樾枨髸?huì)變。把精力花在能產(chǎn)生復(fù)利的地方比如自動(dòng)化檢查、清晰的文檔、可復(fù)用的模式這些投入會(huì)隨著時(shí)間推移不斷產(chǎn)生回報(bào)。另外一個(gè)小技巧每次想加一條新規(guī)則時(shí)先問(wèn)自己“這條規(guī)則能防止什么具體問(wèn)題”。如果答不上來(lái)說(shuō)明這條規(guī)則可能只是個(gè)人偏好不值得加。規(guī)則要有明確的收益否則就是噪音。這個(gè)項(xiàng)目后續(xù)還可以往幾個(gè)方向擴(kuò)展一是增加更多語(yǔ)言的解析支持二是做IDE插件實(shí)現(xiàn)實(shí)時(shí)檢查三是把檢查結(jié)果可視化做成趨勢(shì)圖。不過(guò)這些都是后話先把核心規(guī)則集打磨好比什么都重要。