Rename Field
จุดประสงค์
หัวข้อที่มีชื่อว่า “จุดประสงค์”ชื่อ field คือคำศัพท์ของโครงสร้างข้อมูล พอ field ชื่อ name แต่จริง ๆ เก็บคำนำหน้าของลูกค้า หรือ date แต่จริง ๆ เก็บ timestamp ตอนสร้าง ชื่อนั้นก็โกหก และผู้อ่านทุกคนต้องเสียภาษีเล็ก ๆ ไปกับการถอดรหัส Rename Field เปลี่ยนชื่อ field ให้ตรงกับสิ่งที่เก็บอยู่จริง แล้วอัปเดตทุกจุดที่อ่านหรือเขียน โครงสร้างข้อมูลก็กลับมาบอกความจริงอีกครั้ง
อาการของปัญหา
หัวข้อที่มีชื่อว่า “อาการของปัญหา”smell คือ ชื่อ field ที่ไม่ตรงกับข้อมูลอีกต่อไป ชื่อจะเลื่อนไหลไปเรื่อยเมื่อ software โต บาง field เพิ่มมาเพื่องานหนึ่งแล้วโดนเอาไปใช้กับอีกงาน คำในโดเมนเปลี่ยน หรือชื่อเดิมก็คลุมเครือมาตั้งแต่แรก คุณจะสังเกตได้เมื่อชื่อต้องมี comment มาช่วยอธิบาย เมื่อคนใหม่อ่านผิดซ้ำ ๆ หรือเมื่อชื่อ field กับการใช้งานจริงค่อย ๆ แยกออกจากกันเงียบ ๆ ชื่อที่ล้าสมัยคือแหล่งความเข้าใจผิดที่เล็กแต่อยู่ยาว
ก่อน → หลัง
หัวข้อที่มีชื่อว่า “ก่อน → หลัง”record ลูกค้ามี field ชื่อ name แต่เก็บคำนำหน้าทางการ หลัง refactor เปลี่ยนชื่อเป็น title พร้อมอัปเดต accessor ให้ caller อ่านชื่อที่ชัดกว่าเดิม
// Beforeinterface Customer { name: string; // actually a formal title like "Dr." email: string;}function greet(c: Customer): string { return `Welcome, ${c.name}`;}
// Afterinterface Customer { title: string; email: string;}function greet(c: Customer): string { return `Welcome, ${c.title}`;}# Beforefrom dataclasses import dataclass
@dataclassclass Customer: name: str # actually a formal title like "Dr." email: str
def greet(c): return f"Welcome, {c.name}"
# After@dataclassclass Customer: title: str email: str
def greet(c): return f"Welcome, {c.title}"// Beforetype Customer struct { Name string // actually a formal title like "Dr." Email string}func Greet(c Customer) string { return "Welcome, " + c.Name}
// Aftertype Customer struct { Title string Email string}func Greet(c Customer) string { return "Welcome, " + c.Title}// Beforestruct Customer { name: String, // actually a formal title like "Dr." email: String,}fn greet(c: &Customer) -> String { format!("Welcome, {}", c.name)}
// Afterstruct Customer { title: String, email: String,}fn greet(c: &Customer) -> String { format!("Welcome, {}", c.title)}กลไกการทำงาน
หัวข้อที่มีชื่อว่า “กลไกการทำงาน”- ถ้า record เล็กและอยู่ในมือคุณทั้งหมด rename-symbol ของ editor จบงานได้ในขั้นเดียวอย่างปลอดภัย ใช้เลยแล้วรัน test
- ถ้า record ใช้กันกว้างหรือแชร์ออกไปข้างนอก ให้ค่อย ๆ ทำ เริ่มจากถ้า field ยังไม่ encapsulate ก็ซ่อนไว้หลัง getter และ setter ก่อน caller จะได้พึ่ง accessor แทน field ดิบ
- เปลี่ยนชื่อ field ที่อยู่ข้างใต้ โดยคง accessor ไว้ชื่อเดิมชั่วคราว รัน test
- เปลี่ยนชื่อ getter และ setter ให้เป็นชื่อใหม่ อัปเดต call site เป็นชุดเล็ก ๆ รัน test หลังแต่ละชุด
- ถ้า field ถูก serialize ออกไป (JSON, column ใน database, wire format) ให้ตรึง key เดิมไว้ด้วย mapping ที่ชัดเจน หรือไม่ก็ migrate อย่างตั้งใจ ข้อมูลที่เก็บไว้และ service อื่นจะได้ไม่พัง
- เมื่อทุกผู้อ่านและผู้เขียนใช้ชื่อใหม่ และ mapping ของ serialisation ลงตัวแล้ว การเปลี่ยนชื่อก็เสร็จสมบูรณ์
ใช้เมื่อไหร่ / ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ใช้เมื่อไหร่ / ข้อแลกเปลี่ยน”Rename Field คุ้มที่จะทำทันทีที่ชื่อเริ่มทำให้อ่านผิด เพราะต้นทุนของชื่อสับสนทบขึ้นกับทุกคนที่อ่าน และถูกที่สุดเมื่อทำกับโครงสร้างภายในเล็ก ๆ ที่ rename ด้วยเครื่องมือได้ทันทีและปลอดภัย
อันตรายคือการเอื้อมไปไกล field หนึ่งอาจถูกอ้างถึงไกลเกินไฟล์ของคุณ: ใน payload ที่ serialize, schema ฐานข้อมูล, สัญญา API หรือ repository อื่นที่คุณไม่ได้ควบคุม สำหรับพวกนั้น การ rename แบบมืดบอดจะทำให้ผู้บริโภคพัง ให้แยกชื่อ ภายใน ออกจากชื่อ ภายนอก — map ชื่อ field ใหม่ไปยัง key ที่ serialize ของเดิม — หรือทำ migration ที่วางแผนไว้ เมื่อไม่แน่ใจว่าใครอ่านข้อมูลนี้ ให้ encapsulate ก่อนแล้วค่อยเปลี่ยนชื่อหลังขอบเขตนั้น
เนื้อหาที่เกี่ยวข้อง
หัวข้อที่มีชื่อว่า “เนื้อหาที่เกี่ยวข้อง”| ใช้ Rename Field เมื่อ | หลีกเลี่ยงเมื่อ |
|---|---|
| ชื่อเดิมทำให้เข้าใจผิดหรือไม่สื่อ intent | field นั้นเป็น public API ที่ external system ใช้งาน |
| domain ของ code เปลี่ยนและชื่อเดิมไม่ตรงแล้ว | ต้องแก้ไขหลายไฟล์มากและไม่มี automated refactoring |
| code review เริ่มถามว่า field นี้หมายถึงอะไร | การ rename จะ break serialization format (JSON, DB) |
⚠️ ไม่ควร Rename Field เมื่อ:
- field เป็น public API ที่ external client serialize/deserialize
- database column ที่ต้องการ migration — ต้องทำพร้อมกับ DB schema
- ชื่อใหม่ไม่ได้ชัดเจนกว่าชื่อเดิมมากพอที่จะคุ้มกับความเสี่ยง