🎯 什麼情境該想到我
當你「看到一個函式、變數或欄位的名字,卻得跳進去看實作才知道它到底代表什麼」的時候——名字沒有誠實說出它的用途與意圖。
⚙️ 怎麼用(步驟 / 公式)
這味道是什麼徵兆:命名是程式最基本也最重要的溝通工具。名字取不好,代表你其實還沒真正想清楚這東西的職責;含糊、縮寫、與內容不符的名字會讓後來的人(包含未來的你)不斷誤解與踩雷。
好名字的價值在於「不用看實作就懂」。當你想不到好名字時,往往是背後的設計還沒理清——改名的過程常會逼出更深的問題。
通常用哪些重構手法對治:
- 改變函式宣告(Change Function Declaration):替函式改一個能表達意圖的名字。
- 變數改名(Rename Variable):讓變數名說出它承載的意義。
- 欄位改名(Rename Field):讓資料結構的欄位名反映真實內容。
★ 怎麼具體認出來:五個可操作的訊號
| 訊號 | 例子 | 為什麼是問題 |
|---|---|---|
| 通用名詞 | data、info、manager、handler、util、helper、process() | 這些字對讀者的資訊量是零 |
| ★ 名字與內容不符 | getUser() 裡面寫了一筆 DB、發了一個事件 | 最危險的一種——讀者會信任名字 |
| 型別已經說了的話又說一次 | userList []User、nameString string、isFlag bool | 噪音 |
| ★ 縮寫只有作者懂 | cfgMgr、procRes、tmpVal2 | 半年後作者也不懂 |
| 同一概念多個名字 | 同一份資料在三個函式裡叫 user / u / acct | 讀者以為是三個東西 |
可以直接跑的檢查(Go):
# ★ 找出通用名詞當變數名的地方
grep -rnE '\b(data|info|val|tmp|res|obj|item)[0-9]? *:?=' --include='*.go' .
# golangci-lint(2026-09-24 查證的 linter 清單)
# revive —— golint 的替代品,含 var-naming 規則
# varnamelen —— 變數名長度要配合它的作用域大小
# predeclared —— 抓到遮蔽 len / new / cap / error 這些預宣告識別字
# errname —— sentinel error 要 Err 前綴、error 型別要 Error 後綴★★ Go 的命名慣例和 Java/C# 相反的四點
// ① getter 不加 Get
func (u User) Name() string // ✅
func (u User) GetName() string // ★ 非 Go 風格(setter 仍叫 SetName)
// ② 作用域越小,名字越短 —— 這和「名字要有描述性」不衝突
for i, v := range items { … } // ✅ 三行內的迴圈,i/v 最清楚
func processIncomingPayment(paymentRecord Payment) // ★ 短作用域裡這樣反而囉唆
// ③ ★ 套件名已經是前綴,不要重複
http.HTTPServer // ★ 結巴
http.Server // ✅
// 呼叫端看到的是 http.Server,套件名本身就是語境
// ④ 介面名用 -er
type Reader interface{ Read(p []byte) (int, error) }★ 第②點是 Go 最反直覺的一條:Go 官方風格指南明確說「變數名的長度應該與其作用域大小成正比」。
for i := range 的 i 不是壞名字;i 活過五十行才是。→ 見 Google Go 風格指南、工具-Go命名慣例。
★★ 什麼時候這其實不是味道
| 情況 | 說明 |
|---|---|
| 數學/演算法的慣例短名 | i、j、n、k、dp[i][j]、lo/hi。★ 硬展開成 currentIndex 反而更難對照論文與教科書 |
| ★ 領域內公認的縮寫 | URL、HTTP、ID、UTC、ctx、db、tx、w/r(http.HandlerFunc 的參數) |
| 語言/框架的固定慣例 | Go 的 err、ok、t *testing.T;React 的 props |
| ★ 名字含糊但概念本身就含糊 | Context、Options、Config——它們就是「一包東西」,這是誠實 |
★ 判準不是「名字夠長嗎」,是「讀者需要跳去看實作嗎」。
ctx、err、i從來不需要——它們是共享詞彙。procData每次都需要。
⚠️ 改名的三個 Go 專屬風險
- ★ 匯出名稱改名是破壞性變更。要走遷移式:留舊名當 alias/轉發,掛
// Deprecated:(見 改變函式宣告)。// Deprecated: 改用 Client。 type Cli = Client // ★ 型別別名可以無痛過渡 - ★ 反射/
jsontag/DB 欄位/RPC 註冊表靠字串,Rename不會動它們。Name string `json:"name"` // ★ 改 Go 欄位名不影響 JSON;改 tag 才影響。反之亦然 —— 兩者要分開想 - 改名要一次改完。一半新一半舊是比原本更糟的狀態——讀者會以為那是兩個不同的東西。
工具支援
gopls 的 Rename(VS Code F2)被官方列為最嚴謹的操作,會做型別安全檢查(2026-09-25 查證)。
改名是最便宜、最安全、報酬率最高的重構——沒有理由不做。
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
-
別為了「更潮」而改名,改名要讓意圖更清楚,而非只是換個花樣。
-
廣為人知的慣例名(如迴圈的
i、數學公式裡的短名)在小範圍內反而清楚,不必硬展開。 -
改名要連同所有引用一起改,靠工具或編譯器確保沒漏,別留下半新半舊的名字。
-
★ 慣例短名不是壞名字:
i、err、ok、ctx、tx、w/r。Go 官方的規則是「名字長度與作用域大小成正比」。 -
★ 匯出名稱改名是破壞性變更。用型別別名或轉發做過渡,掛
// Deprecated:。 -
★ 反射/JSON tag/DB 欄位對映不受 Rename 保護。改前 grep 那個字串。
-
改名要一次改到底。留下半新半舊的狀態比不改更糟。
-
不要為了「更精準」把名字加到 40 個字元。
calculateTotalPriceIncludingTaxAndShipping沒人讀——那是在用名字寫文件,該拆函式了。
🔗 相關工具
- 過長函式 Long Function(拆小以後,每一段更需要一個誠實的名字)
- 註釋 Comments(常是壞名字的補丁;先改名讓碼自解釋,再看還需不需要註釋)
- 重構
- 工具-有意義的命名 —— 命名的通用原則
- 工具-Go命名慣例 —— Go 的具體規則(getter 不加 Get、套件名不重複)
- Google Go 風格指南 —— 「名字長度與作用域成正比」的官方說法
- 註釋 —— 用註解補救壞名字,不如直接改名
- 冗員元素 —— 名字想不出來,有時是因為那個東西不該存在