🎯 什麼情境該想到我

當你「看到一個函式、變數或欄位的名字,卻得跳進去看實作才知道它到底代表什麼」的時候——名字沒有誠實說出它的用途與意圖。

⚙️ 怎麼用(步驟 / 公式)

這味道是什麼徵兆:命名是程式最基本也最重要的溝通工具。名字取不好,代表你其實還沒真正想清楚這東西的職責;含糊、縮寫、與內容不符的名字會讓後來的人(包含未來的你)不斷誤解與踩雷。

好名字的價值在於「不用看實作就懂」。當你想不到好名字時,往往是背後的設計還沒理清——改名的過程常會逼出更深的問題。

通常用哪些重構手法對治:

  • 改變函式宣告(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 專屬風險

  1. ★ 匯出名稱改名是破壞性變更。要走遷移式:留舊名當 alias/轉發,掛 // Deprecated:(見 改變函式宣告)。
    // Deprecated: 改用 Client。
    type Cli = Client        // ★ 型別別名可以無痛過渡
  2. ★ 反射/json tag/DB 欄位/RPC 註冊表靠字串,Rename 不會動它們。
    Name string `json:"name"`   // ★ 改 Go 欄位名不影響 JSON;改 tag 才影響。反之亦然 —— 兩者要分開想
  3. 改名要一次改完。一半新一半舊是比原本更糟的狀態——讀者會以為那是兩個不同的東西。

工具支援

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 沒人讀——那是在用名字寫文件,該拆函式了。

🔗 相關工具