代碼審查 Skill
⛔ 原則:不是「勾清單」,是「逐行質疑」。最嚴苛標準,分數可為負。
使用時機
- •任務標記完成前
- •PR 合併前
- •用戶要求 review 時
評分標準(可為負分,嚴苛版)
預設扣分制,沒證據證明沒問題就扣分。
基礎分:100 分,逐項扣分,無下限
| 類別 | 扣分項目 | 扣分 |
|---|---|---|
| 基礎檢查失敗 | ||
| typecheck 失敗(每個錯誤) | -20 | |
| lint 錯誤(每個錯誤) | -10 | |
| lint 警告(每個警告) | -5 | |
| 測試失敗(每個失敗) | -30 | |
使用 any(每處) | -15 | |
使用 @ts-ignore(每處) | -20 | |
使用 as unknown as(每處) | -15 | |
| 致命缺陷 | ||
| 安全漏洞(SQL注入、XSS) | -50 | |
| 硬編碼密鑰/Token | -50 | |
| 資料遺失風險(事務不完整) | -40 | |
| 競態條件 | -40 | |
| 函數名稱與實作不符 | -30 | |
| 嚴重缺陷 | ||
| 未處理 Supabase/API error | -20 | |
| 邊界值未處理(null/undefined) | -15 | |
| 空陣列未處理 | -15 | |
| 隱式假設未驗證 | -15 | |
| 中等缺陷 | ||
| 錯誤訊息顯示技術細節給用戶 | -10 | |
| 缺少 console.error 日誌 | -10 | |
| 魔術數字未抽成常數 | -5 | |
| 函數超過 50 行 | -5 | |
| 複雜邏輯無註解 | -5 | |
| 輕微缺陷 | ||
| 命名不清楚 | -3 | |
| 可抽成 helper 但沒抽 | -3 | |
| 未使用的 import/變數 | -2 |
分數等級
| 分數 | 等級 | 結論 |
|---|---|---|
| 90-100 | 🟢 優秀 | 可合併 |
| 80-89 | 🟡 良好 | 修正後可合併 |
| 60-79 | 🟠 需改進 | 必須修正 |
| 0-59 | 🔴 不合格 | 重寫 |
| <0 | ⛔ 災難 | 拒絕,需嚴肅檢討 |
Step 0:基礎檢查(必須先做)
在任何深度審查之前,先執行:
bash
npm run typecheck npm run lint npm run test npm run gate
檢查項目
| 檢查 | 指令 | 標準 | 失敗扣分 |
|---|---|---|---|
| TypeScript | npm run typecheck | 零錯誤 | -20/錯誤 |
| ESLint | npm run lint | 零錯誤零警告 | -10/錯誤, -5/警告 |
| 測試 | npm run test | 100% 通過 | -30/失敗 |
| Gate | npm run gate | 通過 | -30 |
掃描禁用項
bash
# 搜尋 any
grep -rn ": any" --include="*.ts" --include="*.tsx" src/
# 搜尋 @ts-ignore / @ts-expect-error
grep -rn "@ts-ignore\|@ts-expect-error" --include="*.ts" --include="*.tsx" src/
# 搜尋強制轉型
grep -rn "as unknown as" --include="*.ts" --include="*.tsx" src/
# 搜尋 non-null assertion (!)
grep -rn "\!\." --include="*.ts" --include="*.tsx" src/
# 搜尋 console.log(生產代碼不應有)
grep -rn "console.log" --include="*.ts" --include="*.tsx" src/
# 搜尋 .only(會跳過其他測試)
grep -rn "\.only(" --include="*.test.ts" --include="*.spec.ts" src/
# 搜尋 TODO/FIXME(未完成的工作)
grep -rn "TODO\|FIXME" --include="*.ts" --include="*.tsx" src/
禁用項扣分標準
| 項目 | 扣分 |
|---|---|
any | -15/處 |
@ts-ignore / @ts-expect-error | -20/處 |
as unknown as | -15/處 |
!. non-null assertion(無註解說明) | -5/處 |
console.log(非 error/warn) | -5/處 |
.only 在測試中 | -20/處 |
TODO / FIXME 未處理 | -3/處 |
每發現一處,立即扣分。
Step 1:理解意圖
對每個函數/API,回答:
| 問題 | 你的答案 |
|---|---|
| 這段代碼要解決什麼問題? | |
| 對應工單的哪個需求? | |
| 它在系統中扮演什麼角色? |
如果答不出來,先讀需求文件。
Step 2:追蹤資料流
code
輸入來源 → [這段代碼] → 輸出去向
↓ ↓ ↓
可能的值? 如何轉換? 誰使用?
檢查:
- •輸入可能是 null/undefined 嗎?
- •輸入可能是空陣列嗎?
- •輸入可能超出預期範圍嗎?
Step 3:質疑假設
列出所有隱式假設,逐一驗證:
| 假設 | 成立嗎? | 不成立會怎樣? |
|---|---|---|
| 資料庫一定成功回應 | ||
| 陣列一定有元素 | ||
| 用戶一定已登入 | ||
| ID 一定存在 |
Step 4:模擬極端
4.1 邊界值
| 輸入 | 預期 | 實際 | 有處理? |
|---|---|---|---|
| null | ✅/❌ | ||
| undefined | ✅/❌ | ||
| "" | ✅/❌ | ||
| [] | ✅/❌ | ||
| 負數 | ✅/❌ |
4.2 併發
code
兩個請求同時呼叫,會發生什麼?
4.3 中途失敗
code
執行到第 N 行失敗,系統狀態會壞掉嗎?
Step 5:名稱 vs 實作對照
拆解函數名稱,逐一驗證:
| 名稱片段 | 承諾 | 實作有做到? |
|---|---|---|
| ✅/❌ | ||
| ✅/❌ | ||
| ✅/❌ |
任何 ❌ 直接 -30 分。
Step 6:可維護性
| 問題 | 答案 |
|---|---|
| 六個月後看得懂嗎? | |
| 魔術數字要抽常數嗎? | |
| 複雜邏輯要加註解嗎? |
審查報告格式(必須輸出)
markdown
# 代碼審查報告 ## 評分:XX/100 🟢/🟡/🟠/🔴/⛔ ## 審查檔案 - `path/to/file.ts` ## 基礎檢查 - typecheck: ✅/❌ (X 錯誤) - lint: ✅/❌ (X 錯誤, X 警告) - test: ✅/❌ (X 失敗) - any 使用: X 處 - @ts-ignore: X 處 ## 扣分明細 | 扣分 | 原因 | 位置 | |------|------|------| | -XX | [原因] | L123 | | -XX | [原因] | L456 | | **總計** | **-XXX** | | ## 問題清單 ### ⛔ 致命 1. ... ### 🔴 嚴重 1. ... ### 🟡 建議 1. ... ## 結論 [能不能合併,為什麼]
執行原則
- •✅ 必須跑完全部流程,即使基礎檢查失敗
- •✅ 必須列出所有問題,不可遺漏
- •✅ 必須給出總分,讓用戶知道這次實作幾分
- •✅ 問題列在對話框,不要亂改或新增檔案
禁止事項
- •❌ 沒跑基礎檢查就開始審查
- •❌ 發現問題但略過不報告
- •❌ 跳過任何 Step
- •❌ 不給分數
- •❌ 分數虛高(沒證據就給高分)
- •❌ 用「應該沒問題」當結論
- •❌ 只報告部分問題