簡化程式碼以提升可讀性。適用於重構程式碼以提升清晰度但不改變行為時。適用於程式碼可運作但難以閱讀、維護或擴展時。適用於審查累積了不必要複雜性的程式碼時。
程式碼簡化
靈感來自 Claude Code Simplifier 外掛。在此改編為模型無關、流程驅動的技能,適用於任何 AI 程式碼代理。
概述
透過降低複雜度同時保留精確行為來簡化程式碼。目標不是減少行數,而是讓程式碼更容易閱讀、理解、修改和除錯。每個簡化都必須通過一個簡單的測試:「新團隊成員理解這個版本的速度是否比原始版本快?」
使用時機
- 功能已可運作且測試通過,但實作方式比需要的更繁重時
- 程式碼審查中發現可讀性或複雜度問題時
- 遇到深度巢狀邏輯、過長函式或不明確的名稱時
- 重構在時間壓力下撰寫的程式碼時
- 合併分散在多個檔案中的相關邏輯時
- 合併引入了重複或不一致性的變更後
何時不該使用:
- 程式碼已經乾淨且可讀——不要為了簡化而簡化
- 你還不了解程式碼的作用——先理解再簡化
- 程式碼對效能至關重要,且「更簡單」的版本會明顯變慢
- 你即將完全重寫該模組——簡化即將廢棄的程式碼是浪費力氣
五大原則
1. 精確保留行為
不要改變程式碼的功能——只改變表達方式。所有輸入、輸出、副作用、錯誤行為和邊界情況必須保持完全相同。如果不確定某個簡化是否保留了行為,就不要做。
每次變更前先問:
→ 對每個輸入是否產生相同輸出?
→ 是否維持相同的錯誤行為?
→ 是否保留相同的副作用和順序?
→ 所有現有測試是否仍能通過且無需修改?
2. 遵循專案慣例
簡化意味著讓程式碼與程式碼庫更一致,而不是強加外部偏好。在簡化之前:
1. 閱讀 CLAUDE.md / 專案慣例
2. 研究相鄰程式碼如何處理類似模式
3. 比對專案的風格:
- 匯入順序和模組系統
- 函式宣告風格
- 命名慣例
- 錯誤處理模式
- 型別註解深度
破壞專案一致性的簡化不是簡化——而是無謂的改動。
3. 清晰勝於巧妙
當簡潔版本需要停頓思考才能解析時,明確的程式碼優於緊湊的程式碼。
// 不明確:密集的三元鏈
const label = isNew ? 'New' : isUpdated ? 'Updated' : isArchived ? 'Archived' : 'Active';
// 清晰:可讀的對應
function getStatusLabel(item: Item): string {
if (item.isNew) return 'New';
if (item.isUpdated) return 'Updated';
if (item.isArchived) return 'Archived';
return 'Active';
}
// 不明確:鏈式 reduce 搭配內聯邏輯
const result = items.reduce((acc, item) => ({
...acc,
[item.id]: { ...acc[item.id], count: (acc[item.id]?.count ?? 0) + 1 }
}), {});
// 清晰:命名中間步驟
const countById = new Map<string, number>();
for (const item of items) {
countById.set(item.id, (countById.get(item.id) ?? 0) + 1);
}
4. 維持平衡
簡化有一個失敗模式:過度簡化。注意這些陷阱:
- 過度內聯——移除為概念命名的輔助函式,反而讓呼叫點更難讀
- 合併不相關的邏輯——兩個簡單函式合併成一個複雜函式,並非更簡單
- 移除「不必要的」抽象——某些抽象是為了可擴展性或可測試性,而非複雜度
- 追求行數最少——行數少不是目標,容易理解才是
5. 範圍限於變更部分
預設只簡化最近修改的程式碼。除非明確要求擴大範圍,否則避免順便重構不相關的程式碼。無範圍限制的簡化會造成 diff 雜訊,並增加非預期回歸的風險。
簡化流程
步驟 1:先理解再動手(Chesterton's Fence)
在變更或移除任何東西之前,先了解它為何存在。這就是 Chesterton's Fence:如果你在路上看到一道柵欄卻不知道為什麼在那裡,不要拆掉它。先了解原因,再決定原因是否仍然適用。
在簡化之前,先回答:
- 這段程式碼的責任是什麼?
- 誰呼叫它?它呼叫誰?
- 邊界情況和錯誤路徑是什麼?
- 是否有測試定義了預期行為?
- 為什麼當初會這樣寫?(效能?平台限制?歷史原因?)
- 檢查 git blame:這段程式碼的原始背景是什麼?
如果你無法回答這些問題,你還沒準備好簡化。先閱讀更多背景資訊。
步驟 2:識別簡化機會
掃描這些模式——每個都是具體的信號,而非模糊的氣味:
結構複雜度:
| 模式 | 信號 | 簡化方式 |
|---|---|---|
| 深度巢狀(3 層以上) | 難以追蹤控制流程 | 將條件提取為守衛子句或輔助函式 |
| 長函式(50 行以上) | 多重責任 | 拆分為專注的函式並使用描述性名稱 |
| 巢狀三元運算子 | 需要心智堆疊來解析 | 替換為 if/else 鏈、switch 或查詢物件 |
| 布林參數旗標 | doThing(true, false, true) |
替換為選項物件或獨立函式 |
| 重複條件 | 多處出現相同的 if 檢查 |
提取為命名良好的謂詞函式 |
命名與可讀性:
| 模式 | 信號 | 簡化方式 |
|---|---|---|
| 通用名稱 | data、result、temp、val、item |
重新命名以描述內容:userProfile、validationErrors |
| 縮寫名稱 | usr、cfg、btn、evt |
使用完整單詞,除非縮寫是通用的(id、url、api) |
| 誤導名稱 | 名為 get 的函式卻會修改狀態 |
重新命名以反映實際行為 |
| 解釋「做什麼」的註解 | // increment counter 在 count++ 上方 |
刪除註解——程式碼已經夠清楚 |
| 解釋「為什麼」的註解 | // Retry because the API is flaky under load |
保留這些——它們承載了程式碼無法表達的意圖 |
冗餘:
| 模式 | 信號 | 簡化方式 |
|---|---|---|
| 重複邏輯 | 多處出現相同的 5 行以上程式碼 | 提取為共用函式 |
| 死程式碼 | 無法到達的分支、未使用的變數、註解掉的區塊 | 移除(在確認確實是死程式碼後) |
| 不必要的抽象 | 沒有增加價值的包裝 | 內聯包裝,直接呼叫底層函式 |
| 過度設計的模式 | 工廠的工廠、只有一個實作的策略模式 | 替換為簡單直接的方式 |
| 冗餘型別斷言 | 轉換為已推斷的型別 | 移除斷言 |
步驟 3:逐步套用變更
一次只做一個簡化。每次變更後執行測試。將重構變更與功能或錯誤修復變更分開提交。 一個同時重構和新增功能的 PR 其實是兩個 PR——請分開。
每個簡化:
1. 進行變更
2. 執行測試套件
3. 如果測試通過 → 提交(或繼續下一個簡化)
4. 如果測試失敗 → 還原並重新考慮
避免將多個簡化批次處理成一個未經測試的變更。如果出錯,你需要知道是哪個簡化造成的。
500 行規則: 如果重構會觸及超過 500 行,請投資自動化(codemod、sed 腳本、AST 轉換),而不是手動變更。這種規模的手動編輯容易出錯且審查起來很累人。
步驟 4:驗證結果
完成所有簡化後,退一步評估整體:
比較前後差異:
- 簡化後的版本是否真的更容易理解?
- 你是否引入了任何與程式碼庫不一致的新模式?
- diff 是否乾淨且易於審查?
- 團隊成員會批准這個變更嗎?
如果「簡化」後的版本更難理解或審查,請還原。並非每次簡化嘗試都會成功。
語言特定指引
TypeScript / JavaScript
// 簡化:不必要的 async 包裝
// 之前
async function getUser(id: string): Promise<User> {
return await userService.findById(id);
}
// 之後
function getUser(id: string): Promise<User> {
return userService.findById(id);
}
// 簡化:冗長的條件賦值
// 之前
let displayName: string;
if (user.nickname) {
displayName = user.nickname;
} else {
displayName = user.fullName;
}
// 之後
const displayName = user.nickname || user.fullName;
// 簡化:手動建立陣列
// 之前
const activeUsers: User[] = [];
for (const user of users) {
if (user.isActive) {
activeUsers.push(user);
}
}
// 之後
const activeUsers = users.filter((user) => user.isActive);
// 簡化:冗餘的布林回傳
// 之前
function isValid(input: string): boolean {
if (input.length > 0 && input.length < 100) {
return true;
}
return false;
}
// 之後
function isValid(input: string): boolean {
return input.length > 0 && input.length < 100;
}
Python
# 簡化:冗長的字典建立
# 之前
result = {}
for item in items:
result[item.id] = item.name
# 之後
result = {item.id: item.name for item in items}
# 簡化:巢狀條件搭配早期回傳
# 之前
def process(data):
if data is not None:
if data.is_valid():
if data.has_permission():
return do_work(data)
else:
raise PermissionError("No permission")
else:
raise ValueError("Invalid data")
else:
raise TypeError("Data is None")
# 之後
def process(data):
if data is None:
raise TypeError("Data is None")
if not data.is_valid():
raise ValueError("Invalid data")
if not data.has_permission():
raise PermissionError("No permission")
return do_work(data)
React / JSX
// 簡化:冗長的條件渲染
// 之前
function UserBadge({ user }: Props) {
if (user.isAdmin) {
return <Badge variant="admin">Admin</Badge>;
} else {
return <Badge variant="default">User</Badge>;
}
}
// 之後
function UserBadge({ user }: Props) {
const variant = user.isAdmin ? 'admin' : 'default';
const label = user.isAdmin ? 'Admin' : 'User';
return <Badge variant={variant}>{label}</Badge>;
}
// 簡化:透過中間元件傳遞 props
// 之前——考慮 context 或 composition 是否更適合。
// 這是需要判斷的——標記它,不要自動重構。
常見合理化藉口
| 合理化藉口 | 現實 |
|---|---|
| 「程式碼能運作,不需要動它」 | 難以閱讀的運作中程式碼,在出錯時會難以修復。現在簡化可以為未來每次變更節省時間。 |
| 「行數越少總是越簡單」 | 一行的巢狀三元運算子並不比五行的 if/else 簡單。簡單在於理解速度,而非行數。 |
| 「我順便快速簡化一下這個不相關的程式碼」 | 無範圍限制的簡化會造成 diff 雜訊,並增加非預期程式碼的回歸風險。保持專注。 |
| 「型別本身就是文件」 | 型別記錄結構,而非意圖。命名良好的函式解釋「為什麼」比型別簽名解釋「做什麼」更好。 |
| 「這個抽象以後可能有用」 | 不要保留推測性的抽象。如果現在沒用到,就是沒有價值的複雜度。移除它,需要時再重新加入。 |
| 「原始作者一定有他的理由」 | 也許。檢查 git blame——應用 Chesterton's Fence。但累積的複雜度通常沒有理由;只是壓力下迭代的殘留物。 |
| 「我可以在新增功能的同時重構」 | 將重構與功能開發分開。混合的變更更難審查、還原和從歷史中理解。 |
紅旗
- 需要修改測試才能通過的簡化(你可能改變了行為)
- 「簡化」後的程式碼比原始版本更長且更難理解
- 根據個人偏好而非專案慣例重新命名
- 因為「讓程式碼更乾淨」而移除錯誤處理
- 簡化你尚未完全理解的程式碼
- 將多個簡化批次處理成一個大型、難以審查的提交
- 在未經要求的情況下重構當前任務範圍之外的程式碼
驗證
完成簡化流程後:
- [ ] 所有現有測試無需修改即可通過
- [ ] 建置成功,無新警告
- [ ] Linter/formatter 通過(無風格回歸)
- [ ] 每個簡化都是可審查的增量變更
- [ ] diff 乾淨——沒有混入不相關的變更
- [ ] 簡化後的程式碼遵循專案慣例(對照 CLAUDE.md 或同等文件檢查)
- [ ] 沒有移除或削弱錯誤處理
- [ ] 沒有留下死程式碼(未使用的匯入、無法到達的分支)
- [ ] 團隊成員或審查代理會認為該變更是淨改善






