3 คะแนน โดย GN⁺ 2024-09-12 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • คอมเมนต์สามารถใช้ ภาษามนุษย์ ที่สื่อความหมายได้มากกว่าตัวระบุ จึงเหมาะกับการบันทึกทางเลือกที่ไม่มีในโค้ดและทางเลือกที่ตัดสินใจไม่ใช้
  • วลี “comment the why, not the what” เป็นแนวทางที่พยายามใส่ข้อมูลลงในตัวระบุให้ได้มากที่สุด แต่ช่วงหลังมีแนวโน้มจะย้ายแม้กระทั่งเหตุผลไปไว้ในชื่อฟังก์ชันยาว ๆ หรือชื่อเทสต์
  • ในการ build epub ของ Logic for Programmers มีการใช้ implementation ที่ช้า โดยแทนที่สัญลักษณ์คณิตศาสตร์ 16 ตัวแบบไล่ทีละสตริง แต่ตอนนี้มีสตริงคณิตศาสตร์อยู่แค่ 25 รายการ จึง เร็วพอ
  • คอมเมนต์แบบนี้จะช่วยบอกตำแหน่งที่ควรแก้ในอนาคต หากภายหลังจำนวนสตริงคณิตศาสตร์เพิ่มเป็นหลายร้อยรายการจนกลายเป็นคอขวดของการ build และยังบันทึกไว้ว่าโค้ดที่ช้านั้นเป็น trade-off ที่ตั้งใจเลือก
  • ชื่อฟังก์ชันหรือเทสต์เป็นสิ่งที่ผูกกับสิ่งที่โค้ดทำจริง จึงยากจะทำให้เป็น self-documenting สำหรับ ข้อมูลเชิงปฏิเสธ ที่ว่ามีทางเลือกใดที่ไม่ได้เลือก หรือมีอะไรที่โค้ดไม่ได้ทำ

ข้อมูลที่คอมเมนต์เก็บได้ง่ายกว่าโค้ด

  • โค้ดเป็นภาษาสำหรับเครื่องที่มีโครงสร้าง ส่วนคอมเมนต์เขียนด้วย ภาษามนุษย์ ที่แสดงความหมายได้หลากหลายกว่า
  • “comment the why, not the what” อาจถูกตีความได้ว่าให้ใส่ข้อมูลลงในตัวระบุให้มากที่สุดเท่าที่ทำได้
  • ตัวระบุคล้ายภาษามนุษย์แบบจำกัดที่ฝังอยู่ในโค้ด แม้จะใส่ “what” ได้ไม่ครบทุกอย่าง แต่ในหลายกรณีก็ทำได้มากพอ
  • ช่วงหลังมีคนเห็นมากขึ้นว่าแม้แต่ “why” ก็ไม่จำเป็นต้องอยู่ในคอมเมนต์ แต่อาจใส่ไว้ในชื่อฟังก์ชันยาว ๆ หรือชื่อ test case ได้
  • codebase ที่เป็น self-documenting มักเพิ่มการอธิบายด้วยการ เพิ่มตัวระบุ
    • มีข้อยกเว้นอยู่บ้าง เช่น กรณีที่เปลี่ยนคอมเมนต์ให้เป็น logging เพื่อให้โค้ด self-documenting มากขึ้น

คอมเมนต์แบบ “Why Not” จัดการกับอะไร

  • ข้อมูลที่แสดงผ่านโค้ดได้ยากคือ ข้อมูลเชิงปฏิเสธ
  • ข้อมูลเชิงปฏิเสธช่วยดึงความสนใจไปที่สิ่งที่ไม่มีอยู่ในระบบ หรือเหตุผลว่าทำไมจึงไม่ได้เลือกทางเลือกบางอย่าง
  • “why not” ไม่ได้อธิบายว่าโค้ดทำอะไร แต่เก็บบันทึกว่ามีทางเลือกใดที่โค้ดไม่ได้ทำ และเพราะอะไร

กรณีการแทนที่สัญลักษณ์คณิตศาสตร์ใน epub

  • ในการ build epub ของ Logic for Programmers ด้วยเหตุผลทางเทคนิค สัญลักษณ์คณิตศาสตร์อย่าง \forall จึงไม่ถูกแปลงเป็นสัญลักษณ์อย่าง
  • เพื่อแก้ปัญหานี้ จึงเขียนสคริปต์ที่แทนที่ token ภายในสตริงคณิตศาสตร์ด้วยสัญลักษณ์ยูนิโค้ดที่ตรงกันโดยตรง
  • implementation ที่ง่ายที่สุดคือเรียก string = string.replace(old, new) สำหรับ สัญลักษณ์คณิตศาสตร์ 16 ตัว ที่ต้องใช้แต่ละตัว
  • วิธีนี้ไม่มีประสิทธิภาพเพราะต้องวนผ่านแต่ละสตริง 16 รอบ แต่ถ้าจะทำให้ครบทั้ง 16 การแทนที่ในรอบเดียว โค้ดก็จะซับซ้อนขึ้น
  • ใจความของคอมเมนต์ที่ทิ้งไว้มีดังนี้
    • แต่ละสตริงถูกวนผ่าน 16 รอบ
    • ตอนนี้ในหนังสือมีสตริงคณิตศาสตร์แค่ 25 รายการ และส่วนใหญ่ยาวไม่ถึง 5 ตัวอักษร
    • ดังนั้นจึงยังเร็วพออยู่ดี
  • คอมเมนต์นี้ไม่ได้อธิบายแค่ว่า “ทำไมจึงใช้โค้ดที่ช้า” แต่ยังอธิบายด้วยว่า “ทำไมจึงไม่ใช้โค้ดที่เร็วกว่า”

ป้ายบอกทางสำหรับผู้อ่านในอนาคต

  • ต่อให้โค้ดที่ช้ายังไม่เป็นปัญหาในตอนนี้ วันหนึ่งมันก็อาจกลายเป็นปัญหาได้
  • หาก Logic for Programmers เวอร์ชันในอนาคตมีสตริงคณิตศาสตร์เพิ่มเป็นหลายร้อยรายการ ขั้นตอน build นี้อาจกลายเป็นคอขวดของทั้งกระบวนการ build
  • ถ้าทิ้งป้ายบอกทางไว้ตั้งแต่ตอนนี้ ภายหลังก็จะรู้ได้ทันทีว่าควรไปแก้ตรงไหน
  • แม้โค้ดจะยังทำงานได้โดยไม่มีปัญหาต่อไป คอมเมนต์ก็ยังเก็บรักษาความจริงที่ว่า ผู้เขียนรู้ว่ามี trade-off นี้อยู่
  • เมื่อกลับมาเปิด epub_math_fixer.py อีกครั้งในอีก 2 ปีข้างหน้า ก็ไม่ต้องเสียเวลาสืบใหม่ว่าโค้ดที่ช้านั้นเกิดจากความไม่ชำนาญ เวลาไม่พอ หรือความผิดพลาดกันแน่
  • คอมเมนต์เชิงปฏิเสธทิ้งร่องรอยไว้ว่าผู้เขียนรู้ถึง implementation ที่ช้า พิจารณาทางเลือกอื่นแล้ว และตัดสินใจว่าจะไม่ optimize มัน

ทำไมจึงแทนด้วยชื่อฟังก์ชันและเทสต์ได้ยาก

  • ชื่อฟังก์ชันอย่าง RunFewerTimesSlowerAndSimplerAlgorithmAfterConsideringTradeOffs ยาวเกินไป และก็ยังอธิบาย trade-off ที่แท้จริงได้ไม่เพียงพอ
  • ถ้าภายหลังมีการ optimize โค้ด ชื่อนี้อาจต้องถูกแก้ในหลายจุด
  • ปัญหาที่ใหญ่กว่าคือชื่อแบบนี้ไม่ได้บอกเลยว่าฟังก์ชันนั้นทำ อะไรจริง ๆ จึงกลับทำให้ความเป็น self-documenting อ่อนลง
  • ตัวระบุอย่างชื่อฟังก์ชันและชื่อตัวแปรรองรับข้อมูลได้เพียงหนึ่งวลีเท่านั้น
  • จึงยากที่จะใส่ทั้ง “สิ่งที่ฟังก์ชันทำ” และ “trade-off ที่ฟังก์ชันยอมรับ” ไว้ในตัวระบุเดียวกันพร้อมกัน

ข้อมูลเชิงปฏิเสธที่เก็บไว้ด้วยเทสต์ก็ทำได้ยาก

  • เราอาจสร้างเทสต์ที่ grep หา math block ในหนังสือแล้วให้ล้มเหลวถ้าจำนวนเกิน 80 ได้
  • แต่เทสต์แบบนั้นไม่ได้ทดสอบ EpubMathFixer โดยตรง
  • ภายในฟังก์ชันเองก็ไม่มีจุดใดที่เทสต์ดังกล่าวจะไป hook ได้
  • การทำให้โค้ด self-documenting คือการให้คำอธิบายแนบไปกับโค้ดที่เขียนอยู่ เพื่ออธิบายสิ่งที่โค้ดนั้นทำ
  • แต่ข้อมูลเชิงปฏิเสธพูดถึงสิ่งที่โค้ดไม่ได้ทำ จึงไม่เข้ากับแนวทาง self-documenting โดยพื้นฐาน

คำถามที่กว้างกว่า

  • คอมเมนต์แบบ “why not” อาจมองได้ว่าเป็นตัวอย่างหนึ่งของข้อมูลสวนทางข้อเท็จจริง (counterfactual)
  • จึงยังมีคำถามต่อว่าองค์ประกอบเชิงนามธรรมของการสื่อสารระหว่างมนุษย์ โดยทั่วไปแล้วจะทำให้เป็น self-documenting ได้หรือไม่
  • ข้อมูลอย่างอุปมา ความไม่แน่นอน หรือข้ออ้างเชิงจริยธรรม ก็ยังคงเป็นคำถามอยู่ว่าจะทำให้เป็น self-documenting ได้หรือไม่

1 ความคิดเห็น

 
GN⁺ 2024-09-12
ความคิดเห็นจาก Hacker News
  • จำมุกที่น่าจะเคยเห็นบน Twitter เมื่อหลายปีก่อนได้ว่า: “วิศวกรระดับจูเนียร์เขียนคอมเมนต์อธิบายว่าโค้ด ทำอะไร, วิศวกรระดับกลางเขียนคอมเมนต์อธิบายว่าโค้ด ทำไมถึงทำแบบนั้น, และวิศวกรระดับซีเนียร์เขียนคอมเมนต์อธิบายว่าทำไมโค้ดถึงไม่ได้ถูกเขียนเป็นอีกแบบหนึ่ง”

    • จริงมาก เคยมีครั้งที่ต้องใส่คอมเมนต์ยาวกว่าโค้ดตัวมันเอง 5 เท่า เพื่อกันไม่ให้คนที่ไม่รู้ข้อควรระวังของโค้ดหรือโดเมน หรือช่วงพลวัตขนาดใหญ่ของระบบ มาทำรีแฟกเตอร์ด้วยความหวังดีแล้วพัง
      ในทางกลับกัน ถ้าตั้งชื่อฟังก์ชันและตัวแปรให้ชัดจนเข้าใจได้เอง ก็เคยเขียนฟังก์ชันค่อนข้างใหญ่โดยไม่ต้องมีคอมเมนต์เลยเหมือนกัน
    • ทำทั้งสามแบบเลย คอมเมนต์สรุป มีประโยชน์มากและมีงานวิจัยเชิงประจักษ์ยืนยันแล้ว แต่หลายคนจมอยู่กับแนวคิดแบบ Clean Code ลึกเกินกว่าจะกู้ได้แล้ว
    • โปรแกรมเมอร์จูเนียร์มักจะไม่เขียนเอกสารอะไรเลย หรือไม่ก็เขียนเอกสารทุกอย่าง พอมีประสบการณ์ก็จะรู้ว่าควรบันทึกเฉพาะสิ่งที่แปลกหรือผิดคาด และพอเก่งขึ้นอีก สิ่งแปลก ๆ ก็ยิ่งน้อยลง คอมเมนต์ก็เลยยิ่งน้อยลง
      เพราะงั้นฝั่งที่มีคอมเมนต์น้อยกว่าก็ดูเหมือนชนะ แต่เวลาเห็นโค้ดเบสที่แทบไม่มีคอมเมนต์ ก็ต้องลงไปดูเองถึงจะแยกได้ว่าเป็นผลงานชั้นครูที่ขัดเกลามาดี หรือเป็นโค้ดเปราะบางที่มือใหม่เขียน
    • ยังมีคอมเมนต์ที่อธิบายว่าทำไมเราต้องคงพฤติกรรมที่ดู โง่ชัด ๆ นี้เอาไว้ เพราะมีอย่างอื่นที่พึ่งพา พฤติกรรมโง่ ๆ นั้นอยู่
    • ถ้าเป็นวิศวกรระดับ C ก็คงทิ้งคอมเมนต์ประมาณว่า “เสียเวลาไป X ชั่วโมงกับการรีแฟกเตอร์โค้ดนี้ ถ้าคุณตัดสินใจเดินรอยตามรุ่นพี่ ก็ช่วยเพิ่มตัวนับนี้ด้วย”
      มันอาจดูเป็นการแปะเทปแก้ปัญหาไปวัน ๆ แต่บางครั้งนั่นก็เป็นสิ่งที่ดีที่สุดเท่าที่จะทำได้จริง ๆ
  • ถ้าคิดว่าอีก 1 ปีข้างหน้ากลับมาดูแล้วจะเป็นประโยชน์กับตัวเอง ผมจะเขียนคอมเมนต์ไว้หมด โดยมากจะเป็นเรื่อง ทำไม และ ทำไมถึงทำไม่ได้ และถ้าโค้ดซับซ้อนก็จะเขียน “อะไร” แบบสั้น ๆ เพื่อช่วยมองเห็นลำดับการทำงาน
    สิ่งที่ไม่มีประโยชน์คือคอมเมนต์ที่เขียนเพราะถูกบังคับ API สาธารณะควรมีเอกสารครบถ้วนอยู่แล้ว แต่บางองค์กรบังคับให้ใส่คอมเมนต์กับทุกฟังก์ชันแม้แต่ private function และจบลงด้วยการแค่พูดซ้ำชื่อฟังก์ชันเพราะจุดประสงค์มันชัดเจนเกินไป นี่ไม่ใช่แค่เสียเวลา แต่ยังทำให้คนชินชากับคอมเมนต์จนเรียนรู้นิสัยที่จะมองข้ามมัน
    ผมยังไม่ชอบคอมเมนต์ไร้สาระที่เครื่องมือเติมให้ด้วย แบบที่ใส่ //for หรือ //try ให้ทุกลูปนี่แย่เป็นพิเศษ

    • น่าแปลกที่ ชุดสี syntax highlighting หลายแบบทำให้คอมเมนต์ดูจางและคอนทราสต์ต่ำ คงเพราะคอมเมนต์บังคับหรือคอมเมนต์ที่สร้างอัตโนมัติมักมีข้อมูลน้อย
      ทางที่ดีควรลบคอมเมนต์บังคับและคอมเมนต์ที่สร้างอัตโนมัติออก แล้วในธีมมืดก็ทำคอมเมนต์ให้เป็นสีนีออนสว่าง ๆ เพื่อให้เด่น ถ้ามีคอมเมนต์อยู่ นั่นควรหมายความว่ามันสำคัญ
    • เมื่อก่อนผมอยู่ฝั่ง “คอมเมนต์ทุกอันคือ code smell” และตอนนี้ก็ยังคิดแบบนั้นอยู่พอสมควร แต่พอได้ทำงานกับ โค้ดเบสขนาดใหญ่มาก ที่มีคนหลายร้อยคนพัฒนาและดูแลอย่างต่อเนื่อง มุมมองก็อ่อนลงนิดหน่อย
      ในสภาพแวดล้อมธุรกิจที่เดดไลน์บีบคั้นและต้องดูแลระบบเก่าขนาดใหญ่ บางทีก็ต้องทำเรื่องประหลาด ๆ และตอนนั้นคุณต้องอธิบายว่าทำไม
      เหตุผลใหญ่ที่คนต่อต้านคอมเมนต์คือคอมเมนต์ก็กลายเป็นส่วนหนึ่งของโค้ดและต้องบำรุงรักษาเหมือนกัน แต่คนส่วนใหญ่อ่านคอมเมนต์ตอนที่ติดปัญหาเท่านั้น และตัวแก้ไขโค้ดก็มักทำให้คอมเมนต์เป็นสีเทาจาง ๆ จนมองไม่เห็นในเชิงจิตวิทยา เลยทำให้คอมเมนต์เก่าง่าย
      เลยสงสัยว่าบริบทที่เขียนไว้ให้ “ตัวเราในอนาคต” มีประโยชน์ในโค้ดเบสร่วมที่นักพัฒนาหลายคนแตะต้องหรือไม่ หรือมันเป็นแนวทางที่เวิร์กก็ต่อเมื่อมีคนแตะโค้ดไม่กี่คน
    • เห็นด้วยเต็มที่ ถ้ามีคอมเมนต์มากเกินไป จะยิ่งดูยากว่าข้างในคลาสหรือฟังก์ชันมีอะไรอยู่ เดิมทีคลาสหรือฟังก์ชันที่ควรเห็นจบในหน้าจอเดียว ถ้าต้องล้นจอเพราะคอมเมนต์ ก็เกิด ต้นทุนด้านความอ่านง่าย
    • เมื่อวานในโปรเจกต์ส่วนตัวผมเห็นบรรทัดที่มีคอมเมนต์ว่า “นี่มีประโยชน์จริงเหรอ?” และมันก็ดูเหมือนจะลบออกได้ง่าย ผมพยายามใช้คลาสใหม่ที่ดูสะอาดกว่า แต่สุดท้ายกลับต้องใช้วิธีเก่าแบบเฉพาะนั้นจริง ๆ
      สุดท้ายเลยเติม “=> yes!” ต่อท้ายคอมเมนต์เดิม และรู้สึกขอบคุณตัวเองในอดีตที่บันทึกความสงสัยนั้นไว้ ตอนทำงาน โดยเฉพาะเวลาซ่อมบั๊ก ผมมักทิ้งคอมเมนต์สั้น ๆ หนึ่งหรือสองบรรทัดพร้อม หมายเลขตั๋วงาน ไว้บนการเปลี่ยนแปลงที่ดูไม่ชัดเจน
  • รูปแบบคอมเมนต์ที่ผมชอบที่สุดเท่าที่เคยเขียนไว้ในโค้ด คือเทมเพลตนี้: “DEAR MAINTAINER: โค้ดนี้เป็นแบบนี้เพราะ [เหตุผล] ถ้าคุณพยายามจะ ‘แก้’ มันแล้วพบว่านั่นเป็นความผิดพลาดอันเลวร้าย กรุณาเพิ่มตัวนับ total_hours_wasted_here = n เพื่อเตือนคนถัดไปด้วย”
    ผมไม่ใช่คนคิดต้นฉบับ แต่ก็เคยหยิบไปใช้อย่างซาบซึ้งอยู่ครั้งสองครั้ง และพอเห็นคอมมิตบรรทัดเดียวที่แค่เพิ่มตัวนับก็ตลกดี

    • ถ้ามีสิ่งนี้เมื่อหลายปีก่อนก็คงดี ผมเคยทำโค้ดสร้าง SQL ที่ต้องใช้ recursion ค่อนข้างอิสระเพื่อให้ตรงกับข้อกำหนด และแม้จะมี mutual recursion เยอะจนโค้ดดูรก แต่ก็เป็นความชั่วร้ายที่จำเป็น
      ต่อมาวิศวกรที่ซีเนียร์กว่ารับช่วงโค้ดเบสไปแล้ว “แก้” ทุกอย่างให้เป็นแบบวนลูป พร้อมจะส่งอีเมลมาสั่งสอนว่าทำไม recursion ถึงไม่ดี แต่โค้ดของเขากลับไม่รองรับข้อกำหนดจริงทั้งหมด และสุดท้ายก็ต้องสร้างสิ่งที่ผมเคยทำด้วย recursion ขึ้นมาใหม่แทบทั้งหมด
      หลังจากนั้นเขาก็ขอโทษสำหรับบางคำพูด แต่ถ้ามีคอมเมนต์แบบนี้แปะไว้ข้างบนตั้งแต่แรก งานทั้งหมดนั้นอาจหลีกเลี่ยงได้
  • เห็นด้วยว่าชื่อเรื่องกำกวม และนั่นแหละที่ทำให้ผมกดเข้ามาอ่าน โดยส่วนตัวผมยังชอบแนวทางที่ใช้คอมเมนต์น้อยโดยรวม แต่ คอมเมนต์เชิงอธิบาย ที่บทความพูดถึงมีคุณค่าชัดเจน เป็นเครื่องเตือนใจที่ดีให้บอกว่าทำไมถึงทำแบบนั้น และทำไมไม่ใช้วิธีอื่น
    โดยเฉพาะกับโค้ดของตัวเองที่ต้องดูแลต่ออีก 5 ปี 10 ปี หรือ 15 ปี ไม่นานมานี้ผมรีวิวโค้ดใหม่ของเพื่อนร่วมงานแล้วคิดว่า “ทำไมทำแบบนี้นะ?” แต่พอดูขึ้นไป 10 บรรทัด ก็เจอเหตุผลที่ผมเองเขียนไว้เมื่อ 8 ปีก่อน เพื่อนคนนั้นแค่ทำตามกฎสำคัญของงานบำรุงรักษา นั่นคือทำให้มัน ดูเหมือนโค้ดเดิมที่มีอยู่

    • เวลาบำรุงรักษาโค้ดเบสเก่า การ ทำให้มันดูเหมือนโค้ดเดิมที่มีอยู่ เป็นเรื่องที่ถูกประเมินค่าต่ำเกินไปมาก เพื่อสุขภาพจิตของคนที่จะมาทีหลัง ได้โปรดทำให้มันดูเหมือนโค้ดเดิมเถอะ
  • นี่เป็นเพียงกรณีพิเศษของหลักการที่กว้างกว่านั้น: ให้ใส่คอมเมนต์กับสิ่งที่ ชวนให้ประหลาดใจ เวลาอ่านโค้ด
    ถ้าตอนเขียนโค้ดคุณถามตัวเองในใจตลอดว่า “เดี๋ยวกลับมาอ่านทีหลังจะเข้าใจโค้ดนี้ไหม?” แล้วตอบแบบสัญชาตญาณทุกครั้งว่า “เข้าใจ” คนแบบนั้นมักมั่นใจเกินไปและผิดบ่อย หากคำตอบคือ “ไม่แน่ใจ” คำถามถัดไปตามธรรมชาติก็คือ “ทำไม?” และคำตอบนั้นเองคือสิ่งที่ควรเขียนไว้ในคอมเมนต์
    บางครั้งคำตอบคือ “เพราะคนอ่านอาจสงสัยว่าทำไมไม่เขียนอีกแบบ” ซึ่งเป็นกรณีพิเศษที่บทความนี้พูดถึง แต่บางครั้งคำตอบคือ “เพราะยังไม่ชัดว่ามันทำงานอย่างไรหรือทำไมถึงถูกต้อง” และในกรณีนั้นก็ต้องใช้คอมเมนต์อีกประเภทหนึ่ง

    • ถ้าคุณลองเขียนโค้ดแบบหนึ่งก่อนแล้วมันใช้ไม่ได้ จนต้องเปลี่ยนไปใช้แนวทางที่สอง นั่นเป็นสัญญาณแรงมากว่าตรงนั้นควรมีคอมเมนต์ หากคุณรู้สึกประหลาดใจระหว่างเขียน ก็มีโอกาสสูงว่าอีก 1 ปีให้หลังเมื่อกลับมาอ่านและลืมความประหลาดใจนั้นไปแล้ว คุณจะประหลาดใจอีกครั้ง
    • อีกหลักการที่คล้ายกันคือใช้คำถามว่า “ถ้าสิ่งนี้ไม่ทำงานตามคาด ฉันควรไปดูตรงไหน?” เป็นเกณฑ์ ถ้าคำตอบไม่มีอยู่ในเอกสาร หรืออยู่ห่างเกิน 10 บรรทัดตามเส้นทาง wiki → package/module → file → class → function/method ก็ให้ใส่ คอมเมนต์แบบอินไลน์ หรืออัปเดตเอกสาร
      เรื่องแบบนี้มักเกิดตอนตัดแต่งสตริง หรือไล่สำรวจโครงสร้างข้อมูลแปลก ๆ ในขั้นกลาง
  • แค่ตัวระบุก็พาไปได้ไกลมาก แต่ไปไม่สุด ส่วนตัวผมชอบแนวทางที่บังคับให้เมธอด ตัวแปร ฟิลด์ และพารามิเตอร์สาธารณะมี documentation comment อย่าง jsdoc/xmldoc
    การตั้งชื่อเมธอดให้ดีก็สำคัญ แต่การลองเขียนสั้น ๆ ว่ามันทำอะไรจะช่วยให้ชัดขึ้นกว่าเดิม และโดยเฉพาะช่วยเปิดเผยข้อบกพร่องที่เห็นได้ชัด หลายครั้งพอเริ่มเขียนประโยคแรกก็จะนึกชื่อที่ดีกว่าออกมาได้ และถ้าคำอธิบายเริ่มมีคำว่า “และ” ก็เป็นสัญญาณว่าเมธอดนั้นทำหลายอย่างเกินไปและควรถูกแยกให้เป็นตรรกะมากขึ้น
    คนมักคิดว่า property ชัดเจนเกินกว่าจะต้องมีเอกสาร แต่ /** The API key */ string ApiKey; แบบนี้ขาดข้อมูลไปมากเกินไป เราไม่รู้ว่าคีย์นี้มาจากไหน ใช้ภายในอย่างเดียวหรือมีการรับส่งกับระบบภายนอก เป็นค่าบังคับหรือไม่ รับค่า null หรือค่าว่างได้ไหม มีความยาวสูงสุดหรือไม่ ถ้าค่าไม่ถูกต้องจะเกิดอะไรขึ้น หรือมีโค้ดหรือเอกสารให้ไปอ่านต่อไหม
    สำหรับคนที่เขียนเดิม ข้อมูลนี้ใช้เวลาเขียนแค่ 1–2 นาที แต่สำหรับคนมาใหม่ อาจต้องใช้เวลา หลายชั่วโมง กว่าจะหาคำตอบเจอเมื่อต้องแก้ไข นำไปใช้ หรือถูกดึงตัวมาซ่อมบั๊กในอีกหลายปีต่อมา

  • ถ้าพอเดาได้ว่าใน code review จะมีรีวิวเวอร์ที่ชอบซักละเอียดเกินไปท้วงอะไรบ้าง ผมก็มักเขียนคอมเมนต์ทำนองว่า “ไม่ได้ทำ X เพราะ Y” จุดประสงค์คือเพื่อลดการถกเถียงไปกลับที่น่ารำคาญ

    • จากประสบการณ์ ปริมาณการคุยไปกลับก็ยังเท่าเดิม แต่ผมได้พิมพ์ว่า “ตามที่เขียนไว้ในคอมเมนต์” อยู่สองสามครั้ง
    • เรื่องแบบนี้ผมมักใส่เพิ่มใน PR ล่วงหน้า แต่ก็ยังไม่แน่ใจว่ามันมีคุณค่ามากพอจะเก็บไว้ในโค้ดถาวรไหม
  • คอมเมนต์ประเภท “แม้จะวนผ่านแต่ละสตริง 16 รอบ แต่ในหนังสือมีสตริงสูตรอยู่แค่ 25 รายการจนถึงตอนนี้ และส่วนใหญ่ยาวไม่ถึง 5 ตัวอักษร จึงเร็วพอ” ก็ยังมีรูปแบบแปรผันอีกแบบ
    นั่นคือการใส่ debug log ที่จะทำงานเมื่ออินพุตโตเกินข้อจำกัดที่ตั้งใจไว้ตอนออกแบบเดิมมาก มันสื่อสารข้อความแทบจะเดียวกันกับนักพัฒนาในอนาคต แต่ตรวจพบได้เร็วกว่า จึงช่วยลดเวลาวินิจฉัยและดีบักได้มากกว่า

    • หลายกรณีก็เป็นไอเดียที่ดี ตอนนี้อาจมีลูปที่ทำงานถูกต้องแต่ช้ามากอยู่ ซึ่งสามารถตั้งตัวจับเวลาไว้แล้วทำแบบ “ถ้าใช้เวลาเกิน X วินาที ให้เขียน warning log” ได้
      ถ้ามีระบบ logging และ observability ในอุดมคติ ก็คงวัดเวลาทุกองค์ประกอบของแอปและทิ้งข้อมูลดีบักไว้บ่อย ๆ แต่จะมีสักกี่คนที่ใช้ระบบสมบูรณ์แบบแบบนั้นจริง ๆ ความพยายามที่สำคัญกว่าคือการใส่ log แบบเจาะจงในจุดที่ประสิทธิภาพอาจแย่ลงภายหลัง หรือในส่วนที่ยังไม่มีเวลาปรับแต่งให้ดี
      พอย้อนกลับไปมองก็เป็นไอเดียที่ obvious แต่ที่ผ่านมาแนวทางของผมคือทิ้งคอมเมนต์ไว้ในจุดที่ควรกลับมาดูทีหลัง แล้วหวังว่าถ้าเรื่องแย่ลงจะยังจำคอมเมนต์นั้นได้
    • เครื่องมือที่หวังดีอย่าง bazel มักมองเอาต์พุตที่ไม่ใช่ข้อผิดพลาดว่าเป็น noise ที่ควรถูกซ่อน มันมักส่งข้อความพวกนี้ลงถังขยะที่ใกล้ที่สุด ทำให้ในบางพื้นที่ log กลายเป็นวิธีสื่อสารข้อจำกัดที่ไม่น่าเชื่อถืออย่างมาก
  • ไม่ว่าใครจะว่าอย่างไร ก็ใส่คอมเมนต์และ doc comment ไว้ทั่วโค้ดค่อนข้างเยอะ แต่จะเริ่มแบบย้อนทาง คือเขียนคอมเมนต์สรุปรายการขั้นตอนของแอปพลิเคชันคร่าว ๆ ก่อน แล้วระหว่างพัฒนาก็ค่อยแตกขั้นตอนใหญ่ให้เป็นขั้นตอนย่อย ลบคอมเมนต์เดิมบ้าง เก็บไว้บ้าง และแยกคอมเมนต์ให้ละเอียดขึ้นเรื่อย ๆ จนเกือบกลายเป็นอัลกอริทึมที่เสร็จสมบูรณ์
    ปกติเขียนโค้ดจากนอกเข้าใน จึงเขียนโค้ดไปพร้อมกับการแตกคอมเมนต์ด้วย บางครั้งก็โค้ดรวดเดียวก่อน แล้วค่อยมานั่งใส่คอมเมนต์ทีหลังในระดับที่คนส่วนใหญ่อาจขี้เกียจทำ ใส่คำอธิบายให้ทุกฟังก์ชันและตัวแปร และแม้แต่ฟังก์ชัน deg_to_rad ก็ยังใส่ """Converts degrees to radians.""" เพราะพื้นที่เก็บข้อมูลมันถูก
    รู้ว่าคนส่วนใหญ่ไม่ชอบ แต่ก็ไม่เป็นไร ถ้าไม่ชอบดูก็ลบออกด้วยสคริปต์หรือเอาออกตอน code review ได้ ถึงอย่างนั้นการอ่าน โค้ดเก่าของตัวเอง ก็ยังเพลินกว่าการอ่านโค้ดของคนอื่นที่ไม่มีคอมเมนต์มาก ใน Python โค้ด boilerplate เรียบ ๆ อย่าง Flask API มักจะ self-documenting อยู่แล้ว แต่บางทีโค้ด boilerplate แบบนั้นกลับเปลี่ยนบ่อยจนมีคอมเมนต์สำคัญกำกับไว้ ส่วนในอุตสาหกรรม งานฝั่งอัลกอริทึมมักถูกเขียนใหม่ทั้งก้อนบ่อยกว่า
    ต่อไปก็ยังจะชอบคอมเมนต์และ doc comment อยู่ดี

    • ผมก็ทำคล้าย ๆ กัน แต่ส่วนใหญ่จะทำเฉพาะกับ องค์ประกอบระดับบนสุด เพราะตรงนั้นคอมเมนต์มีประโยชน์ที่สุด
      ผมชอบสร้างภาพรวมทั้งระบบไว้ในหัว และรู้สึกว่าในช่วงแรกการลองเขียนตัวเลือกด้านการออกแบบหลายแบบจริง ๆ มันช้าเกินไป เพราะงั้นพอตัดสินใจเรื่องการออกแบบได้แล้ว ก็ต้องบันทึกไว้แน่นอน เพราะคนอื่นยังเข้าถึงสิ่งที่อยู่ในหัวผมไม่ได้
      ส่วนรายละเอียด ผมคาดว่าคนอื่นจะเห็นชัดขึ้นเองเมื่อทำความเข้าใจภาพรวมเสร็จแล้ว แต่ถ้าด้วยเหตุผลบางอย่างการทำความเข้าใจนั้นไม่เกิดขึ้น มันก็อาจยิ่งอ่านยากขึ้นได้ จึงยังขัดเกลาเพิ่มเพื่อรักษาความอ่านง่ายพื้นฐานไว้อย่างน้อยที่สุด
    • คอมเมนต์ยอดเยี่ยมมากถ้ามีการดูแลรักษาอย่างดี แต่ถ้าไม่ใช่งานแบบโอเพนซอร์สที่คนอ่านมีน้อยกว่าคนที่ต้องช่วยกันดูแลคอมเมนต์มาก ๆ สุดท้ายทุกคนก็มักลืมหรือขี้เกียจจนไม่อัปเดตมัน
      หลายครั้งการอัปเดตคอมเมนต์ก็เป็นงานพอ ๆ กับการแก้โค้ด ดังนั้นในโลกความจริง คอมเมนต์จึงมักเป็นสิ่งที่ รอวันกลายเป็นคำโกหก อยู่เสมอ สุดท้ายคอมเมนต์กับโค้ดก็ไม่ตรงกัน และนั่นอาจแย่กว่าโค้ดที่ไม่มีคอมเมนต์เสียอีก
      สู้ใช้ automated test ที่แสดงเจตนาไปเลยจะดีกว่า โดยทั่วไป test โกหกไม่ได้ และถ้ามันโกหกก็คงไม่ถูก merge อยู่แล้ว ถ้ามีชุด test ที่ดีพอแสดงให้เห็นว่าโค้ดตั้งใจให้ถูกใช้อย่างไร แบบนั้นทั้งอธิบายได้ดีและแทบรับประกันได้ว่าเป็นความจริง ซึ่งผมอยากเห็นมากกว่า
    • เห็นด้วยจนถึงตรงที่บอกว่า “ถ้าไม่ชอบก็ลบออกด้วยสคริปต์ในเวอร์ชันของตัวเอง หรือเอาออกใน code review ก็ได้” แต่หลังจากนั้น ในฐานะเพื่อนร่วมทีม นี่ดูเป็น สัญญาณอันตรายครั้งใหญ่
    • ผมก็คล้ายกัน ประมาณครึ่งหนึ่งเริ่มจากเขียนคอมเมนต์อธิบายก่อนว่าจะทำอะไร แล้วค่อยเติมสิ่งที่ไม่เป็นไปตามคาด และสิ่งที่ต้องทำเพื่อให้มันใช้งานได้จริง
      พอต้องกลับมาดูอีกทีหลัง 5 สัปดาห์ มันช่วยได้มาก
    • ผมไม่ได้คิดว่าทุกกรณีจำเป็นต้องมี doc comment และก็ไม่ได้คิดว่าต้องบันทึกทุกพารามิเตอร์กับค่าที่คืนกลับของทุกฟังก์ชัน แต่สำหรับ API ที่ซับซ้อน มันมีประโยชน์ชัดเจน
  • ผมยึดแนวคิดที่ว่า “คอมเมนต์คือคำขอโทษที่ส่งถึงตัวเองในอนาคต”
    ถ้าโค้ดมันดูแปลก ช้า หรือเป็นส่วนที่พอจะอธิบายให้ใครฟังแล้วต้องพูดว่า “มันออกจะสะเปะสะปะหน่อยนะ” ผมก็มักจะทิ้งคอมเมนต์ไว้ โดยเฉพาะถ้าเคยลองแก้มันมาก่อน ก็จะบันทึกกรณีที่มันไม่ทำงานหรือสิ่งที่แก้ไปแล้วไว้ด้วย
    ถ้าใช้เกณฑ์แบบนี้ คอมเมนต์ที่ไม่จำเป็นจะค่อย ๆ หายไปเอง และโดยมากจะเหลือแค่ตอนที่ควรบันทึก เหตุผลว่าทำไม จริง ๆ ลองใช้กับ codebase ของตัวเองสักประมาณหนึ่งเดือนแล้วจะเข้าใจความรู้สึกนี้