ตั้งชื่อตัวแปรให้คนอ่านไม่ต้องเดา

เวลาเราพูดกันว่า "โค้ดอ่านง่าย" คนส่วนใหญ่นึกถึงการจัดย่อหน้า การแตกฟังก์ชัน หรือการเขียนคอมเมนต์ แต่จากประสบการณ์การนั่งอ่านโค้ดที่คนอื่นเขียนไว้ สิ่งที่ทำให้เหนื่อยที่สุดไม่ใช่โครงสร้าง แต่คือการต้องหยุดคิดทุกสองบรรทัดว่า ตัวแปรตัวนี้มันคืออะไรกันแน่

ชื่อที่ดีมีข้อได้เปรียบอย่างหนึ่งเหนือคอมเมนต์ คือมันโกหกไม่ได้นาน คอมเมนต์ที่เขียนไว้เมื่อปีที่แล้วอาจจะผิดไปแล้วโดยไม่มีใครรู้ แต่ชื่อตัวแปรถูกบังคับให้เปลี่ยนตามโค้ดเสมอ เพราะมันเป็นส่วนหนึ่งของโค้ดจริง ๆ

ชื่อที่ดีตอบคำถามว่า "ทำไม" ไม่ใช่ "อะไร"

ตัวแปรชื่อ list บอกเราว่ามันเป็นลิสต์ ซึ่งเป็นข้อมูลที่ตัวภาษาบอกเราอยู่แล้ว ชื่อนี้จึงไม่ได้เพิ่มอะไรเลย ส่วน activeSubscribers บอกทั้งว่ามันเป็นของหลายชิ้น เป็นคนประเภทไหน และผ่านเงื่อนไขอะไรมาแล้ว

ชื่อที่ทำให้ต้องเดาชื่อที่อธิบายตัวเองที่มันดีกว่าเพราะ
datapendingInvoicesบอกว่าเป็นข้อมูลเรื่องอะไร และอยู่ในสถานะไหน
temporiginalPriceBeforeDiscountบอกว่าเก็บไว้ทำไม ไม่ใช่แค่ว่าเก็บชั่วคราว
flaghasVerifiedEmailอ่านแล้วรู้ทันทีว่า true แปลว่าอะไร
resultmatchedUserIdsบอกทั้งชนิดและความหมายของสิ่งที่ได้มา
ddaysUntilExpiryหน่วยเวลาชัด ไม่ต้องไล่หาว่านับเป็นวันหรือวินาที

boolean ควรอ่านเหมือนประโยคคำถามที่ตอบว่าใช่หรือไม่

ตัวแปรชนิด boolean เป็นจุดที่พลาดกันบ่อยที่สุด เพราะชื่อกลาง ๆ อย่าง status หรือ check ทำให้คนอ่านต้องไล่ย้อนไปดูว่า true หมายถึงสำเร็จหรือหมายถึงมีปัญหา ให้ขึ้นต้นด้วย is, has, can, should หรือ needs แล้วปัญหานี้จะหายไปเอง

typescript
// ต้องเดา: true แปลว่าปิดอยู่ หรือปิดไม่ได้?
if (order.close) { ... }

// อ่านเป็นประโยคได้เลย
if (order.isClosed) { ... }
if (order.canBeCancelled) { ... }
if (user.needsPasswordReset) { ... }
ความต่างระหว่างชื่อที่ต้องเดากับชื่อที่อ่านเป็นประโยค

ความยาวของชื่อควรแปรผันตามระยะที่มันมีชีวิตอยู่

นี่เป็นหลักที่ช่วยได้มากเวลาลังเลว่าจะตั้งชื่อยาวแค่ไหน ตัวแปรที่เกิดและตายภายในสองบรรทัด เช่นตัวนับใน loop ใช้ i ก็เพียงพอและอ่านง่ายกว่าด้วยซ้ำ แต่ตัวแปรที่ประกาศไว้ระดับโมดูลและถูกเรียกใช้จากอีกสิบไฟล์ ควรมีชื่อที่ยาวพอให้คนที่เปิดมาเจอครั้งแรกเข้าใจได้โดยไม่ต้องไล่หาที่มา

  • ตัวแปรใน loop หรือ callback สั้น ๆ — สั้นได้เต็มที่
  • ตัวแปรในฟังก์ชันยาวสามสิบบรรทัด — ควรบอกความหมายให้ครบ
  • ค่าคงที่ที่ export ออกไปทั้งโปรเจกต์ — ยาวหน่อยไม่เป็นไร คนอ่านมากกว่าคนเขียน

ใส่หน่วยไว้ในชื่อ แล้วบั๊กจะหายไปหนึ่งประเภท

บั๊กที่เจ็บปวดและหายากหลายตัวเกิดจากการเข้าใจหน่วยผิด มีคนคิดว่า timeout เป็นวินาที อีกคนส่งมาเป็นมิลลิวินาที ระบบก็รอนานกว่าที่ตั้งใจไปพันเท่าโดยไม่มี error ใด ๆ ขึ้นให้เห็น

typescript
// คลุมเครือ
const timeout = 30;
const size = 5;
const price = 1200;

// ชัดเจน
const timeoutSeconds = 30;
const maxUploadSizeMb = 5;
const priceInSatang = 120000;
ใส่หน่วยลงไปในชื่อโดยตรง

ใช้คำเดียวกันตลอดทั้งโปรเจกต์

ถ้าในโค้ดฐานเดียวกันมีทั้ง fetchUser, getUser, loadUser และ retrieveUser คนอ่านจะเสียเวลาหาคำตอบว่าสี่คำนี้ต่างกันตรงไหน ทั้งที่ความจริงมันทำงานเหมือนกันหมด แค่คนเขียนคนละคนคนละเดือน

วิธีแก้ที่ได้ผลคือทำข้อตกลงสั้น ๆ ไว้ในไฟล์เดียวของทีม เช่น get ใช้กับการอ่านจากหน่วยความจำ fetch ใช้กับการเรียกข้าม network และ build ใช้กับการประกอบ object ขึ้นมาใหม่ ไม่ต้องยาว แค่มีให้อ้างอิงตอน review ก็พอ

มีอยู่สองเรื่องยากในวิทยาการคอมพิวเตอร์ คือการทำ cache ให้ถูกต้อง กับการตั้งชื่อสิ่งต่าง ๆ
สำนวนที่เล่าต่อกันมาในวงการ มักอ้างถึง Phil Karlton

สรุปสั้น ๆ ไว้ใช้ตอน review

  1. อ่านชื่อแล้วรู้ไหมว่ามันเก็บอะไร ถ้าไม่รู้ ให้เพิ่มคำที่บอกความหมายเข้าไป
  2. ถ้าเป็น boolean อ่านออกมาแล้วเป็นประโยคที่ตอบใช่หรือไม่ได้หรือเปล่า
  3. มีหน่วยหรือสกุลเงินเข้ามาเกี่ยวไหม ถ้ามีให้ใส่ไว้ในชื่อเลย
  4. คำที่ใช้ตรงกับคำที่โปรเจกต์ใช้อยู่แล้วหรือเปล่า
  5. ชื่อยาวสมดุลกับขอบเขตที่มันอยู่ไหม ตัวแปรอายุสองบรรทัดไม่ต้องยาวสามคำ

การตั้งชื่อเป็นงานที่ไม่มีใครชม เพราะเวลาทำได้ดีจะไม่มีใครสังเกตเห็น คนอ่านแค่รู้สึกว่าโค้ดนี้เข้าใจง่ายผิดปกติ แล้วก็ทำงานต่อไปได้เลย ซึ่งนั่นแหละคือผลลัพธ์ที่เราต้องการ

ความคิดเห็น

กำลังโหลดความคิดเห็น