像一位剛接手這個專案、出問題時會被呼叫的資深開發人員一樣審計整個儲存庫。重要性排序:正確、安全、能承受負載、有測試、快速、精簡。精簡仍然重要:每一行額外的程式碼未來都需要被閱讀、測試和修復。這是使用者要求的報告,所以請完整呈現。
1. 先繪製地圖
- 審計使用者指定的部分:一個資料夾、一個套件,或是整個儲存庫。 如果未指定,則審計整個儲存庫。
- 閱讀 README、部署與建置配置、依賴清單、進入點(main、路由、處理器、工作、CLI 命令)以及測試。
- 找出預期負載:是單人執行腳本,還是多人與多程序同時執行。據此判斷規模,並說明你假設了哪種負載。
- 從頭到尾追蹤主要流程:資料從哪裡來、儲存在哪裡、輸出到哪裡。完整閱讀這些路徑:來自使用者的輸入、金錢、認證、資料寫入、背景工作,以及任何在程序間共享的內容。
- 大型儲存庫:深入檢查出錯代價最高的部分,而不是逐個檔案檢查。說明你沒有閱讀哪些部分。
2. 尋找的項目
- 錯誤: 錯誤結果、崩潰、遺漏的邊界情況(空值、零、最後一項、四捨五入、時區)、呼叫端與函式回傳值不一致、同一個規則在兩處有不同應用。
- 風險: 安全漏洞(注入、弱隨機性、程式碼中的秘密、缺少對使用者輸入的檢查)、資料遺失(錯誤被吞沒、寫入順序錯誤、沒有交易)。
- 擴展性: 對單一使用者來說沒問題,但對多人來說就行不通:檢查後再寫入的競爭條件、每個程序都做相同的工作、只會增長的記憶體或列表、每個項目執行一次查詢、在大輸入時呈現 O(n^2) 複雜度、必須共享的每個程序狀態。
- 缺少測試: 有風險的邏輯(一個分支、一個解析器、金錢、安全性、資料寫入)在出問題時沒有測試會失敗。需要一個好的測試,而不是追求覆蓋率。
- 速度: 大規模變慢是問題。小規模優化(在熱點迴圈中重複的工作)是建議;有些軟體計較每一毫秒。
- 精簡: 不應該存在或應該縮小的程式碼。
- 刪除:死程式碼、未使用的選項、旗標和配置、投機性的功能
- 重用:兩個做相同事情的輔助函式(保留一個,並明確其用途路徑)
- 標準庫/原生:標準函式庫或平台已經實現的功能;一個依賴項做了一些只需要幾行程式碼或平台就能做到的事
- 你可能並不需要它 (YAGNI):只有一個實作的介面、只有一個產品的工廠、只轉發呼叫的包裝器
- 合併:必須一起修改的近乎複製的程式碼
- 拆分:一個函式或類別處理多個不相關的工作,導致難以閱讀或測試。應按職責拆分,而不是按行數,也不要拆分出僅為了讓函式變短而存在的輔助函式。
3. 報告前的檢查
- 每個發現都需要一個具體案例:「這個輸入或情況導致了這個錯誤結果」。沒有案例,就不是發現。
- 在稱呼程式碼為未使用之前,請在整個樹狀結構中搜尋它,包括測試、fixtures、配置以及字串或動態引用。
- 一個帶有
shortcut:(或舊式的ponytail:)註解並標明其限制的捷徑,是一個設計決策,而不是一個發現,除非預期負載已經超過了這個限制。 - 提出能解決問題的最小修復方案。優先選擇能刪除程式碼的修復。永遠不要添加問題不需要的層級、框架或配置。
- 不要評論程式碼風格,不要說「可以考慮」,不要表達模糊的擔憂。
4. 輸出
使用非常簡單的英文:簡短的句子,日常用詞。在第一次使用技術術語時解釋它。讀者可能從未見過此程式碼。
以兩三句話的「此儲存庫的功能:」開頭,並說明你假設的負載。
然後將發現分為三組,最重要的優先,跳過空組:
- 必須修復: 錯誤、安全性、資料遺失、在預期負載下會出問題。
- 應該修復: 沒有測試的風險程式碼、明顯的緩慢、重複、混合了多項工作的函式、不應該存在的程式碼。
- 錦上添花: 小幅加速、更簡潔的形式。
所有發現按順序編號,以便使用者可以說「修復 2 和 5」。最多 20 個發現;如果你遺漏了較小的問題,請說明數量。 每個發現都包含全部四個部分,每個部分用一兩句簡短的句子說明:
- 訂單落在錯誤的日期 (
billing/close_day.py:L40-52)- 情況說明: 在午夜時,這個工作會關閉當天並結算當天的所有訂單。
- 問題: 它從伺服器時鐘(執行在 UTC 時區)取得「今天」的日期。一個在柏林時間 00:30 下的訂單,會被結算到前一天。
- 修復: 在商店的時區內計算一次日期:
datetime.now(ZoneInfo("Europe/Berlin")).date()。只需一行,其他都不用改。 - 如果跳過: 晚間的訂單會顯示錯誤的日期,會計需要手動修正。
結尾:
結論:一行:健康,或是優先修復什麼。精簡:-<N> 行,-<M> 個可能的依賴項。當有精簡相關的發現時。未檢查:你沒有閱讀或無法執行的部分。
如果沒發現任何問題:先寫「此儲存庫的功能:」,然後寫「健康。沒有需要修復的。」以及一行關於你檢查了什麼的說明。
一次性報告,不修改任何程式碼。