🎯 什麼情境該想到我

當你「函式的名字沒把意圖說清楚,或參數清單不理想」的時候——修改函式的名字或參數,讓這個介面更容易被理解與使用。改名是這其中最常用、也最有價值的動作。

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

意圖:改動函式的名字或參數,讓函式介面更好、更清楚地表達用途。

動作要點(小步安全進行):

  • 簡單做法:改動不大、呼叫點不多時,直接改宣告、更新所有呼叫點、測試即可。
  • 遷移式做法(migration,適合難一次改完或對外公開的 API):
    1. 若要調整本體,先把本體整理到能直接搬移的狀態。
    2. 用提煉函式 Extract Function把函式本體抽成一個具備「新宣告」的新函式。
    3. 讓舊函式改為呼叫新函式(形成過渡期,兩者並存)。
    4. 逐一把呼叫端改成呼叫新函式,每步測試。
    5. 全部遷移完後,用行內函式 Inline Function除掉舊函式,或直接刪除。
  • 命名技巧:先想「假如已經寫好、理想的名字會是什麼」,再回頭改實作。

★ 兩種節奏:大步 vs 遷移式

大步(simple)★ 遷移式(migration)
做法直接改宣告,讓編譯器帶你去每個呼叫點新增新簽章 → 舊的改成轉發 → 逐步遷移 → 刪掉舊的
適用同一個 module 內、呼叫點你全都改得到★ 匯出的 API、別的 repo 在用、呼叫點上百處
Go 的優勢編譯器是最好的 checklist——改完 go build ./... 列出所有呼叫點

遷移式在 Go 的標準寫法:

// Deprecated: 改用 NewClientWithConfig。此函式會在 v2 移除。
func NewClient(addr string) *Client {          // ★ 舊簽章保留,只轉發
    return NewClientWithConfig(Config{Addr: addr})
}
 
func NewClientWithConfig(cfg Config) *Client { /* 真正的實作 */ }

★ // Deprecated: 開頭是 godoc 認得的慣例(pkg.go.dev 會把它標成刪除線,
staticcheck 的 SA1019 會對呼叫端報警)。格式要精確:Deprecated: 要在一個註解區塊的開頭自成一段。

★ Go 的關鍵差異:「加一個參數」通常不該真的加參數

// ❌ 每次需求變動就加一個參數 —— 呼叫端全部要改,而且 f(a, true, false, nil) 沒人看得懂
func Serve(addr string, tls bool, timeout time.Duration, logger *slog.Logger) error
 
// ✅ 選項用 Functional Options(Go 生態的主流)
func Serve(addr string, opts ...Option) error
Serve(":8080", WithTLS(cert), WithTimeout(5*time.Second))
 
// ✅ 或 params struct(欄位多、且大多會用到時)
func Serve(cfg ServeConfig) error

判準見 工具-Go函式參數太多怎麼辦。兩者都讓「加參數」不再是破壞性變更。

唯一的例外——這個參數要加在最前面:

func Fetch(ctx context.Context, url string) ([]byte, error)   // ★ ctx 永遠是第一個,名字永遠叫 ctx

補 context.Context 是實務上最常見的「改變函式宣告」,而它沒有 options 可以繞。

工具支援(2026-09-25 查證)

  • 改名:gopls 的 Rename(VS Code F2)被官方列為最嚴謹的操作之一,會檢查型別安全。
  • ★ 改簽章:在 func 這個 token 上執行 Rename,gopls 會讓你直接編輯整個簽章並更新所有呼叫點。
  • refactor.rewrite.removeUnusedParam、moveParamLeft / moveParamRight —— 刪除未使用參數、調整參數順序。
  • 改套件名:在 import 路徑的套件名上 Rename,會連檔案一起搬;跨子套件要開 renameMovesSubpackages。

🧪 我實際套用的紀錄

  • (待填)

⚠️ 注意 / 什麼時候不適用

  • 對外公開、無法一次掌握所有呼叫端的 API,用遷移式做法逐步過渡,別硬改。

  • 別為了改名而破壞既有語意;名字要反映真正的意圖。

  • ★ 改名和改參數不要一起做。分兩次 commit:先改名(純機械、工具可保證),再動參數(要思考)。一起做,review 的人分不出哪個是哪個。

  • ★ 匯出 API 用大步改 = 破壞下游。Go module 的相容性承諾是「同一 major 版本內不破壞」——要破壞就得升 /v2 路徑。用遷移式。

  • 參數改順序最危險:Copy(dst, src) 改成 Copy(src, dst),型別相同 → 編譯完全通過,語義整個反了。這種改動要嘛改名(強迫呼叫端面對),要嘛不要做。

  • 實作介面的方法不能單獨改宣告——會同時破壞介面與所有其他實作。先改介面,再讓編譯器列出所有實作。

  • 反射/encoding/json tag/RPC 註冊表不受編譯器保護。靠字串找函式或欄位的地方,改名後是執行期才爆。改前先 grep -rn 那個字串。

🔗 相關工具