Rename Function/Variable
จุดประสงค์
หัวข้อที่มีชื่อว่า “จุดประสงค์”เปลี่ยนชื่อ function หรือตัวแปรให้บอกตรง ๆ ว่าคืออะไรหรือทำอะไร ชื่อใหม่ต้องทำให้ผู้อ่านเข้าใจจุดที่เรียกได้โดยไม่ต้องเปิดดูข้างใน จากนั้นอัปเดต caller ทุกจุดให้ใช้ชื่อใหม่ behavior ไม่เปลี่ยนสักนิด เปลี่ยนแค่ถ้อยคำ
Code Smell
หัวข้อที่มีชื่อว่า “Code Smell”ชื่อที่ชวนเข้าใจผิดแย่กว่าไม่มีชื่อเสียอีก ลองนึกถึง function ชื่อ getCustomer ที่แอบสร้างลูกค้าใหม่ให้ถ้าหาไม่เจอ ตัวแปรชื่อ data ที่เก็บราคาแค่ค่าเดียว หรือ flag ชื่อ check ที่จริง ๆ แล้ว ลบข้อมูลทิ้ง ทุกอันบังคับให้ผู้อ่านต้องเมินชื่อแล้วไปไล่อ่านข้างในแทน ที่เป็นความล้มเหลวที่ชื่อดี ๆ มีไว้ป้องกันพอดี เมื่อไรที่คุณจับได้ว่าตัวเองเปิด implementation อ่านเพียงเพื่อจะรู้ว่าคืออะไร นั่นแหละถึงเวลาเปลี่ยนชื่อ
ก่อน → หลัง
หัวข้อที่มีชื่อว่า “ก่อน → หลัง”function ที่ชื่อบอกน้อยกว่างานที่ทำจริงมาก บวกกับตัวแปร local ที่คลุมเครืออีกตัว หลัง refactor ชื่อทั้งสองบอกความหมายออกมาตรง ๆ
// Beforefunction calc(d: number): number { const x = d * 0.0085; return d + x;}
const result = calc(1200);
// Afterfunction applyDailyInterest(balance: number): number { const interest = balance * 0.0085; return balance + interest;}
const balanceWithInterest = applyDailyInterest(1200);# Beforedef calc(d): x = d * 0.0085 return d + x
result = calc(1200)
# Afterdef apply_daily_interest(balance): interest = balance * 0.0085 return balance + interest
balance_with_interest = apply_daily_interest(1200)// Beforefunc Calc(d float64) float64 { x := d * 0.0085 return d + x}
result := Calc(1200)
// Afterfunc ApplyDailyInterest(balance float64) float64 { interest := balance * 0.0085 return balance + interest}
balanceWithInterest := ApplyDailyInterest(1200)// Beforefn calc(d: f64) -> f64 { let x = d * 0.0085; d + x}
let result = calc(1200.0);
// Afterfn apply_daily_interest(balance: f64) -> f64 { let interest = balance * 0.0085; balance + interest}
let balance_with_interest = apply_daily_interest(1200.0);กลไกการทำงาน
หัวข้อที่มีชื่อว่า “กลไกการทำงาน”- เลือกชื่อใหม่ แล้วลองอ่านออกเสียงดู ชื่อนั้นบอกผลลัพธ์ ไม่ใช่บอกกลไก ใช่ไหม คนที่เพิ่งเข้ามาอ่านจะเข้าใจจุดที่เรียกโดยไม่ต้องเปิดข้างในหรือเปล่า
- ถ้า function เป็นส่วนหนึ่งของ API ที่เผยแพร่แล้ว ให้พิจารณาคงชื่อเดิมไว้เป็น wrapper บาง ๆ ที่ส่งต่อไปยังชื่อใหม่ เพื่อไม่ให้ caller เดิมพัง
- เปลี่ยนชื่อที่ declaration จะพึ่ง rename-symbol ของ editor ก็ได้ถ้าเชื่อมือได้ แต่ต้องไล่ดูขอบเขตที่ถูกแก้ทุกครั้ง
- อัปเดต caller ทุกจุด — และ comment หรือ string ทุกตัวที่อ้างถึงชื่อเดิม
- รัน test การเปลี่ยนชื่อไม่ควรเปลี่ยน behavior ดังนั้นชุด test ที่ผ่านยืนยันว่าคุณแค่ขยับถ้อยคำเท่านั้น
- เมื่อ caller ทั้งหมดใช้ชื่อใหม่แล้ว ให้ลบ wrapper ชั่วคราวจากขั้นที่ 2 ทิ้ง
ใช้เมื่อไหร่ / ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ใช้เมื่อไหร่ / ข้อแลกเปลี่ยน”เปลี่ยนชื่อทุกครั้งที่ชื่อทำให้คุณต้องหยุดคิด ทุกครั้งที่ต้องเปิดข้างในอ่านเพื่อจะรู้ว่าคืออะไร หรือทุกครั้งที่ behavior ของ code เคลื่อนห่างจากสิ่งที่ชื่อเคยสัญญาไว้ นี่คือ refactoring ที่เสี่ยงต่ำที่สุดเท่าที่มี และให้ผลตอบแทนสูงที่สุดท่าหนึ่ง เพราะชื่อชัด ๆ ถูกอ่านบ่อยกว่าถูกเขียนหลายเท่า
ต้นทุนคือความวุ่นวายเชิงกลไกที่กระจายไปทั่ว caller และสำหรับ API สาธารณะ อาจเป็นการเปลี่ยนแปลงที่ทำให้ของเดิมพัง บรรเทาได้ด้วยเครื่องมือ rename อัตโนมัติ และในจุดที่สัญญามีความเสี่ยง ให้มีช่วงเวลา deprecation ความเสี่ยงแทบจะคุ้มเสมอ: ผู้อ่านทุกคนที่คุณช่วยให้รอดพ้นจากชื่อที่ชวนสับสน ตอบแทนคุณกลับมา
เนื้อหาที่เกี่ยวข้อง
หัวข้อที่มีชื่อว่า “เนื้อหาที่เกี่ยวข้อง”| ใช้ Rename Function/Variable เมื่อ | หลีกเลี่ยงเมื่อ |
|---|---|
| ต้องเปิด implementation อ่านถึงจะรู้ว่าทำอะไร | ชื่อเก่าเป็นส่วนหนึ่งของ public API ที่ publish ไปแล้ว |
| ชื่อหลอกให้เข้าใจผิดหรือ outdated | มี caller จำนวนมากที่ยังไม่พร้อม migrate |
| behavior เคลื่อนออกห่างจากสิ่งที่ชื่อสัญญาไว้ | rename ที่ทำโดยไม่มี test จะไม่รู้ว่าพังหรือเปล่า |
⚠️ ไม่ควร Rename Function/Variable เมื่อ:
- ชื่อเป็นส่วนของ stable public API — ต้องมี deprecation period และ wrapper ชั่วคราว
- ทำ rename พร้อมกับ behavior change — ให้แยก commit เพื่อให้ git blame ชัดเจน
- ยังไม่เข้าใจ function นั้นดีพอ — rename ที่ผิดแย่กว่าชื่อเดิม