Naming Things
ตั้งชื่อให้สื่อเจตนา — งานที่ยากที่สุดอย่างหนึ่งในการเขียนโปรแกรม
คืออะไร
หัวข้อที่มีชื่อว่า “คืออะไร”Naming Things คือวินัยในการเลือก ชื่อ ให้กับองค์ประกอบทุกระดับใน source code — ตัวแปร parameter method class interface namespace ไปจนถึงชื่อ file และ project — ให้สื่อความหมายและเจตนาได้ชัดเจนที่สุดเท่าที่จะทำได้ มันไม่ใช่แค่เรื่องสไตล์หรือรสนิยมส่วนตัว แต่เป็นเครื่องมือสื่อสารหลักระหว่างนักพัฒนาด้วยกัน เพราะ code ถูก “อ่าน” มากกว่า “เขียน” หลายเท่าตัวตลอดอายุของระบบ
Phil Karlton เคยกล่าวประโยคที่ Martin Fowler นำมาเผยแพร่ต่อจนโด่งดังไว้ว่า
“ในวิทยาการคอมพิวเตอร์มีเรื่องยากอยู่แค่สองเรื่อง: การ invalidate cache และการตั้งชื่อสิ่งต่าง ๆ”
ความยากของการตั้งชื่อไม่ได้อยู่ที่ไวยากรณ์ แต่อยู่ที่การตั้งชื่อที่ดีบังคับให้ผู้เขียนต้อง เข้าใจ domain และเจตนาของ code อย่างถ่องแท้เสียก่อน หากยังไม่เข้าใจพอ ชื่อที่ได้จะกำกวมหรือทำให้เข้าใจผิดเสมอ ด้วยเหตุนี้ชื่อที่หาไม่เจอ หรือชื่อที่ต้องคิดนานผิดปกติ จึงมักเป็นสัญญาณว่าการออกแบบยังไม่นิ่งหรือ concept นั้นยังไม่ถูก factor ออกมาอย่างเหมาะสม
หลักการข้อแรกและสำคัญที่สุดของการตั้งชื่อคือ ความสอดคล้องภายในทีมหรือ project ไม่ว่าความชอบส่วนตัวของแต่ละคนจะเป็นอย่างไร code ที่ทุกคนใช้ร่วมกันต้องใช้ชื่อและรูปแบบที่สอดคล้องกัน ไม่มีหลักการอื่นใดด้านล่างนี้สำคัญเหนือกว่าข้อนี้
ทำอย่างไร
หัวข้อที่มีชื่อว่า “ทำอย่างไร”1. สื่อเจตนา ไม่ใช่แค่ชนิดข้อมูลหรือกลไก — ชื่อควรตอบคำถามว่า “ทำไมสิ่งนี้จึงมีอยู่” และ “มันทำอะไร” มากกว่าจะบอกแค่ว่ามันเก็บข้อมูลชนิดใด หากต้องมีคอมเมนต์มาช่วยอธิบายความหมายของชื่อ นั่นคือสัญญาณว่าชื่อนั้น สั้นหรือคลุมเครือเกินไป
2. ใส่หน่วยและบริบทไว้ในชื่อ เมื่อมีความเสี่ยงต่อความกำกวม เช่น หน่วยเวลา สกุลเงิน หรือระบบพิกัด ให้ชื่อสื่อสิ่งนั้นออกมาตรง ๆ แทนที่จะพึ่งคอมเมนต์
3. ใช้ภาษาของ Ubiquitous Language ชื่อ class method และตัวแปรใน code ควรตรงกับคำศัพท์ที่ domain expert ใช้จริง ไม่ใช่ศัพท์เทคนิคที่นักพัฒนาคิดขึ้นเอง — ดู Ubiquitous Language ในบริบทของ Domain-Driven Design
4. เลือกระดับ abstraction ให้เหมาะกับ scope ตัวแปรที่มี scope แคบ (เช่น ตัวนับใน loop สั้น ๆ) อาจใช้ชื่อสั้นได้ แต่ตัวแปร method หรือ class ที่มี scope กว้าง ควรใช้ชื่อที่ยาวและอธิบายตัวเองมากขึ้น
5. ตั้งชื่อ method ตามสิ่งที่มันทำจริง รวมถึง side effect ถ้า method ชื่อ GetUser แต่แอบบันทึก log หรือแก้ state ภายใน นั่นคือชื่อที่โกหกผู้เรียกใช้
6. หลีกเลี่ยง anti-pattern ที่พบบ่อย
- ใช้ชื่อสุ่มไม่มีความหมาย (เช่น
foo,Fred) แทนตัวแปรจริง - ใช้ชื่อตัวอักษรเดียว โดยเฉพาะ
lเพราะสับสนกับเลข1ได้ง่าย (ยกเว้นตัวนับ loop สั้น ๆ ตามธรรมเนียม) - ทำให้ชื่อที่คล้ายกันต่างกันด้วยการสะกดผิดหรือสะกดสำรอง (
color/colour) - ตั้งชื่อ class กระเป๋ารวมที่ทำสารพัดอย่าง เช่น
Util,Utils,Manager,Helper - ตั้งชื่อ boolean ในเชิงปฏิเสธ เช่น
notReady,isInvalid— ทำให้เงื่อนไขif (!notReady)อ่านยาก - ต่อท้ายชื่อที่ซ้ำกันด้วยตัวเลข (
item1,item2,item3) แทนที่จะแยก concept ให้ชัด - ปนคำตรงข้ามไม่สม่ำเสมอ (ใช้
begin/endที่หนึ่ง แล้วใช้start/finishอีกที่)
7. Pair กับเพื่อนร่วมทีมเวลาตั้งชื่อยาก ชื่อที่สองคนช่วยกันคิดจนเข้าใจตรงกัน มีแนวโน้มสูงที่คนอื่นในทีมจะเข้าใจตามได้ง่ายเช่นกัน
8. ถ้าชื่อยาวเกินไปจนอึดอัด ให้มองเป็น code smell — method หรือ class ที่ต้องใช้ชื่อยาวมากเพื่ออธิบายให้ครบ มักกำลังทำหน้าที่มากเกินไป ควรแตกออกเป็นส่วนที่มี cohesion สูงขึ้นตาม Single Responsibility
แผนผังด้านล่างสรุปกระบวนการตัดสินใจคร่าว ๆ เวลาต้องตั้งชื่ออะไรสักอย่าง
flowchart TD
Start[เริ่มตั้งชื่อ] --> Intent{อธิบายเจตนาได้ชัดหรือไม่}
Intent -- ไม่ชัด --> Understand[ทำความเข้าใจ domain เพิ่ม]
Understand --> Intent
Intent -- ชัด --> UL{มีคำใน Ubiquitous Language หรือไม่}
UL -- มี --> UseUL[ใช้คำนั้นตรงๆ]
UL -- ไม่มี --> Draft[ร่างชื่อผู้สมัคร]
Draft --> Pair[Pair กับเพื่อนร่วมทีม]
UseUL --> Pair
Pair --> Check{ต้องมีคอมเมนต์อธิบายเพิ่มไหม}
Check -- ต้องมี --> Draft
Check -- ไม่ต้อง --> Done[ใช้ชื่อนี้]
ตัวอย่าง
หัวข้อที่มีชื่อว่า “ตัวอย่าง”ตัวอย่างคลาสสิกจาก Tim Ottinger ในหนังสือ Clean Code แสดงให้เห็นชัดว่าชื่อสั้นเกินไปบังคับให้ต้องพึ่งคอมเมนต์:
ชื่อที่แย่ — ต้องพึ่งคอมเมนต์บอกความหมาย
int d; // elapsed time in daysชื่อที่ดี — ชื่อบอกความหมายและหน่วยในตัวเอง
int elapsedTimeInDays;int daysSinceCreation;ลองนึกถึง method Thread.Sleep ของ .NET ที่รับค่า int — ไม่มีความกำกวมเรื่องหน่วยเลย เพราะ parameter ถูกตั้งชื่อว่า millisecondsTimeout แทนที่จะเป็นแค่ timeout เฉย ๆ
ตัวอย่างการตั้งชื่อในบริบท domain model ที่สื่อ Ubiquitous Language แทนศัพท์เทคนิคทั่วไป:
// ไม่ดี: ใช้ศัพท์ generic ที่ไม่สื่อ domainpublic class OrderManager{ public void Process(Order o, bool flag) { if (!flag) { // ... } }}
// ดีขึ้น: ชื่อสื่อเจตนาและใช้ภาษาที่ domain expert เข้าใจpublic class OrderFulfillmentService{ public void ShipOrder(Order order, bool isExpeditedShipping) { if (!isExpeditedShipping) { ScheduleStandardDelivery(order); } else { ScheduleExpeditedDelivery(order); } }}Microsoft Learn ยังกำหนดข้อตกลงการตั้งชื่อสำหรับ .NET/C# ไว้ชัดเจน เช่น ใช้ PascalCase กับ type, method และ public member ทั้งหมด ใช้ camelCase กับ local variable และ parameter ขึ้นต้น interface ด้วย I และ หลีกเลี่ยงคำย่อ เว้นแต่เป็นคำย่อที่รู้จักกันทั่วไป ตัวอย่างการตั้งชื่อ field ตามธรรมเนียมของทีม .NET Runtime:
public class OrderProcessor : IOrderProcessor{ private readonly IOrderRepository _orderRepository; // private field: _camelCase private static int s_processedCount; // private static field: s_prefix
public OrderProcessor(IOrderRepository orderRepository) // parameter: camelCase { _orderRepository = orderRepository; }
public void ProcessOrder(Order order) // public method: PascalCase { // ... }}ประโยชน์และข้อควรระวัง
หัวข้อที่มีชื่อว่า “ประโยชน์และข้อควรระวัง”ประโยชน์
- ลดภาระทางปัญญา (cognitive load) ของผู้อ่าน code เพราะไม่ต้องเดาความหมายหรือไล่ตามคอมเมนต์
- ทำให้ code self-documenting ลดการพึ่งพาคอมเมนต์ที่มักจะ “ล้าสมัย” ไม่ตรงกับ code จริงเมื่อเวลาผ่านไป
- เร่ง onboarding สมาชิกใหม่ในทีม เพราะชื่อที่สื่อความหมายทำหน้าที่เป็นเอกสารในตัวเอง
- ช่วยให้การค้นหา (searchability) และ refactor ทำได้แม่นยำขึ้น เพราะชื่อไม่กำกวมและไม่ซ้ำโดยไม่ตั้งใจ
- เผยให้เห็น design smell ได้เร็ว — ถ้าตั้งชื่อยากผิดปกติ มักแปลว่าความรับผิดชอบของ element นั้นยังไม่ชัดเจน
ข้อควรระวัง
- การตั้งชื่อใหม่ทั้งหมดทันทีอาจกลายเป็น bikeshedding เสียเวลาไปกับการถกเถียงเรื่องผิว ๆ แทนที่จะโฟกัสที่ตรรกะ ควรทำผ่าน Code Review หรือ pairing แบบพอเหมาะ
- การเปลี่ยนชื่อ public API ที่มีผู้ใช้ภายนอกพึ่งพาอยู่ (breaking change) ต้องพิจารณาผลกระทบและ versioning ก่อนทำ
- ความสอดคล้อง (consistency) สำคัญกว่าความสมบูรณ์แบบของชื่อเดี่ยว ๆ — การมีสองรูปแบบปนกันใน codebase เดียวสร้างความสับสนมากกว่าชื่อที่ไม่สมบูรณ์แบบแต่สอดคล้องกัน
- ชื่อที่ดีไม่ทดแทนการออกแบบที่ดี — ถ้าโครงสร้าง code สับสนอยู่แล้ว การตั้งชื่อใหม่เพียงอย่างเดียวแก้ปัญหาที่ต้นเหตุไม่ได้ ต้อง refactor โครงสร้างควบคู่ไปด้วย
ที่เกี่ยวข้อง
หัวข้อที่มีชื่อว่า “ที่เกี่ยวข้อง”- Ubiquitous Language
- Separation of Concerns
- Single Responsibility
- Simple Design
- Poor Names (code smell)
- Refactoring
แหล่งอ้างอิง
หัวข้อที่มีชื่อว่า “แหล่งอ้างอิง”- ที่มา · deviq.com/practices/naming-things
- Two Hard Things — Martin Fowler
- Identifier names - rules and conventions - C# | Microsoft Learn
- General Naming Conventions - Framework Design Guidelines | Microsoft Learn
- Clean Code — Robert C. Martin
- Code Complete — Steve McConnell