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

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

ในบทนี้ผมจะพูดถึงข้ออ้างที่นักพัฒนาใช้ในการหลีกเลี่ยงการเขียนคอมเมนต์ และเหตุผลว่าทำไมคอมเมนต์ถึงมีความสำคัญจริงๆ บทที่ 13 จะอธิบายวิธีเขียนคอมเมนต์ที่ดี และสองสามบทถัดจากนั้นจะพูดถึงประเด็นที่เกี่ยวข้อง เช่น การเลือกชื่อตัวแปร และการใช้เอกสารเพื่อปรับปรุงการออกแบบระบบ ผมหวังว่าบทเหล่านี้จะทำให้คุณเชื่อในสามสิ่งนี้: คอมเมนต์ที่ดีสามารถสร้างความแตกต่างครั้งใหญ่ในคุณภาพโดยรวมของซอฟต์แวร์; การเขียนคอมเมนต์ที่ดีไม่ใช่เรื่องยาก; และ (ซึ่งอาจจะยากที่จะเชื่อ) การเขียนคอมเมนต์สามารถสนุกได้จริงๆ

เมื่อนักพัฒนาไม่เขียนคอมเมนต์ พวกเขามักจะแก้ตัวด้วยข้ออ้างหนึ่งหรือหลายข้อต่อไปนี้:

  • "Good code is self-documenting." (โค้ดที่ดีคือเอกสารในตัว)
  • "I don't have time to write comments." (ไม่มีเวลาเขียนคอมเมนต์)
  • "Comments get out of date and become misleading." (คอมเมนต์ล้าสมัยและทำให้เข้าใจผิด)
  • "The comments I have seen are all worthless; why bother?" (คอมเมนต์ที่เคยเห็นล้วนไร้ค่า จะเสียเวลาไปทำไม) ในหัวข้อถัดไปผมจะกล่าวถึงข้ออ้างแต่ละข้อตามลำดับ

12.1 Good code is self-documenting (โค้ดที่ดีคือเอกสารในตัว)

บางคนเชื่อว่าหากโค้ดถูกเขียนมาอย่างดีแล้ว มันจะชัดเจนในตัวเองจนไม่จำเป็นต้องมีคอมเมนต์ นี่เป็นตำนานที่ฟังดูน่าอภิรมย์ เหมือนข่าวลือที่ว่าไอศกรีมดีต่อสุขภาพ — เราอยากจะเชื่อมันจริงๆ! แต่โชคไม่ดีที่มันไม่เป็นความจริง แน่นอนว่ามีสิ่งที่คุณทำได้ในการเขียนโค้ดเพื่อลดความจำเป็นในการใช้คอมเมนต์ เช่น การเลือกชื่อตัวแปรที่ดี (ดู บทที่ 14 ) อย่างไรก็ตาม ยังคงมีข้อมูลสำคัญเกี่ยวกับการออกแบบอีกมากที่ไม่สามารถแสดงออกผ่านโค้ดได้ ตัวอย่างเช่น มีเพียงส่วนเล็กๆ ของ interface ของคลาสเท่านั้น เช่น signature ของ method ที่สามารถระบุอย่างเป็นทางการในโค้ดได้ ส่วนที่ไม่เป็นทางการของ interface เช่น คำอธิบายระดับสูงว่าแต่ละ method ทำอะไร หรือความหมายของผลลัพธ์ สามารถอธิบายได้ในคอมเมนต์เท่านั้น ยังมีตัวอย่างอื่นๆ อีกมากมายของสิ่งที่อธิบายในโค้ดไม่ได้ เช่น เหตุผลเบื้องหลังการตัดสินใจออกแบบบางอย่าง หรือเงื่อนไขที่เหมาะสมในการเรียก method ใด method หนึ่ง

นักพัฒนาบางคนเถียงว่าถ้าคนอื่นอยากรู้ว่า method ทำอะไร ก็ควรอ่านโค้ดของ method นั้นเลย ซึ่งจะแม่นยำกว่าคอมเมนต์ใดๆ มันเป็นไปได้ที่ผู้อ่านจะสามารถอนุมาน abstract interface ของ method ได้จากการอ่านโค้ด แต่มันจะใช้เวลานานและเจ็บปวด นอกจากนี้ ถ้าคุณเขียนโค้ดโดยคาดหวังให้ผู้ใช้อ่าน implementation ของ method คุณจะพยายามทำให้แต่ละ method สั้นที่สุดเท่าที่จะทำได้ เพื่อให้อ่านง่าย ถ้า method ทำอะไรที่ไม่ใช่เรื่องเล็กน้อย คุณจะแยกมันออกเป็น method ย่อยๆ หลายอัน ซึ่งจะส่งผลให้มี method ผิวๆ จำนวนมาก ยิ่งไปกว่านั้น มันไม่ได้ทำให้โค้ดอ่านง่ายขึ้นจริงๆ เพราะการจะเข้าใจพฤติกรรมของ method ระดับบน ผู้อ่านอาจต้องเข้าใจพฤติกรรมของ method ที่ซ้อนอยู่ข้างในด้วย สำหรับระบบขนาดใหญ่ การให้ผู้ใช้อ่านโค้ดเพื่อทำความเข้าใจพฤติกรรมนั้นไม่ใช่แนวทางปฏิบัติที่เหมาะสม

ยิ่งไปกว่านั้น คอมเมนต์เป็นพื้นฐานของ abstractions จำได้ไหมว่าใน บทที่ 4 เราได้กล่าวว่าเป้าหมายของ abstractions คือการซ่อนความซับซ้อน: abstraction คือมุมมองที่เรียบง่ายของสิ่งหนึ่ง ซึ่งเก็บรักษาข้อมูลสำคัญไว้ แต่ละทิ้งรายละเอียดที่สามารถ มองข้ามได้อย่างปลอดภัย ถ้าผู้ใช้ต้องอ่านโค้ดของ method เพื่อที่จะใช้งาน method นั้น แสดงว่าไม่มีการสร้าง abstraction ขึ้นเลย: ความซับซ้อนทั้งหมดของ method ถูกเปิดเผยออกมา หากไม่มีคอมเมนต์ abstraction เพียงอย่างเดียวของ method คือ declaration ซึ่งระบุชื่อ method และชื่อกับประเภทของ argument และผลลัพธ์ declaration ขาดข้อมูลสำคัญมากเกินไปที่จะเป็น abstraction ที่มีประโยชน์ได้ด้วยตัวเอง ตัวอย่างเช่น method สำหรับตัด substring อาจมี argument สองตัว start และ end เพื่อระบุช่วงของอักขระที่จะตัด จาก declaration เพียงอย่างเดียว เราไม่สามารถบอกได้ว่า substring ที่ได้จะรวมอักขระที่ end ชี้อยู่หรือไม่ หรือจะเกิดอะไรขึ้นถ้า start > end คอมเมนต์ช่วยให้เราจับข้อมูลเพิ่มเติมที่ผู้เรียกใช้ต้องการได้ ทำให้เราสร้างมุมมองที่เรียบง่ายขึ้นพร้อมกับซ่อนรายละเอียด implementation ไปพร้อมกัน นอกจากนี้ การที่คอมเมนต์เขียนด้วยภาษามนุษย์อย่างภาษาอังกฤษก็มีความสำคัญเช่นกัน มันทำให้คอมเมนต์มีความแม่นยำน้อยกว่าโค้ด แต่มันให้พลังในการแสดงออกมากกว่า ทำให้เราสามารถสร้างคำอธิบายที่เรียบง่ายและ intuitive ได้ ถ้าคุณต้องการใช้ abstractions เพื่อซ่อนความซับซ้อน คอมเมนต์เป็นสิ่งจำเป็น

12.2 I don't have time to write comments (ไม่มีเวลาเขียนคอมเมนต์)

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

ข้อโต้แย้งต่อข้ออ้างนี้คือแนวคิดเรื่อง investment mindset ที่กล่าวถึงใน หน้า 15 ถ้าคุณต้องการโครงสร้างซอฟต์แวร์ที่สะอาด ซึ่งจะช่วยให้คุณทำงานได้อย่างมีประสิทธิภาพในระยะยาว คุณต้องใช้เวลาเพิ่มขึ้นเล็กน้อยในตอนเริ่มต้นเพื่อสร้างโครงสร้างนั้น คอมเมนต์ที่ดีสร้างความแตกต่างอย่างมหาศาลในการบำรุงรักษาซอฟต์แวร์ ดังนั้นความพยายามที่ใช้ไปกับมันจะคืนทุนอย่างรวดเร็ว ยิ่งไปกว่านั้น การเขียนคอมเมนต์ไม่จำเป็นต้องใช้เวลามาก ลองถามตัวเองดูว่าคุณใช้เวลาส่วนไหนของวันในการพัฒนาที่เป็นการพิมพ์โค้ด (เทียบกับการออกแบบ คอมไพล์ ทดสอบ ฯลฯ) โดยสมมติว่าคุณไม่ได้รวมคอมเมนต์เข้าไป ผมว่าไม่น่าจะเกิน 10% ทีนี้สมมติว่าคุณใช้เวลาในการพิมพ์คอมเมนต์เท่ากับที่ใช้พิมพ์โค้ด ซึ่งน่าจะเป็นค่าสูงสุดที่ปลอดภัย จากสมมติฐานเหล่านี้ การเขียนคอมเมนต์ที่ดีจะเพิ่มเวลาพัฒนาไม่เกินประมาณ 10% ซึ่งประโยชน์ของการมีเอกสารที่ดีจะชดเชยต้นทุนนี้ได้อย่างรวดเร็ว

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

12.3 Comments get out of date and become misleading (คอมเมนต์ล้าสมัยและทำให้เข้าใจผิด)

คอมเมนต์บางครั้งก็ล้าสมัย แต่นี่ไม่จำเป็นต้องเป็นปัญหาใหญ่ในทางปฏิบัติ การทำให้เอกสารเป็นปัจจุบันไม่ต้องใช้ความพยายามมากมาย การเปลี่ยนแปลงเอกสารครั้งใหญ่จำเป็นก็ต่อเมื่อมีการเปลี่ยนแปลงโค้ดครั้งใหญ่ ซึ่งการเปลี่ยนแปลงโค้ดจะใช้เวลามากกว่าการเปลี่ยนแปลงเอกสาร บทที่ 16 อธิบายวิธีจัดระเบียบเอกสารเพื่อให้ง่ายที่สุดที่จะอัปเดตหลังจากการปรับเปลี่ยนโค้ด (แนวคิดหลักคือหลีกเลี่ยงเอกสารที่ซ้ำซ้อนและเก็บเอกสารไว้ใกล้กับโค้ดที่เกี่ยวข้อง) การตรวจสอบโค้ด (code review) เป็นกลไกที่ดีเยี่ยมในการตรวจจับและแก้ไขคอมเมนต์ที่ล้าสมัย

12.4 All the comments I have seen are worthless (คอมเมนต์ที่เคยเห็นล้วนไร้ค่า)

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

12.5 Benefits of well-written comments (ประโยชน์ของคอมเมนต์ที่เขียนอย่างดี)

ตอนนี้ที่ผมได้พูดถึง (และหวังว่าจะได้หักล้าง) ข้อโต้แย้งต่างๆ ต่อการเขียนคอมเมนต์แล้ว มาดูประโยชน์ที่คุณจะได้รับจากคอมเมนต์ที่ดีกัน แนวคิดหลักเบื้องหลังคอมเมนต์คือการเก็บข้อมูลที่อยู่ในความคิดของนักออกแบบแต่ไม่สามารถแสดงออกผ่านโค้ดได้ ข้อมูลนี้มีตั้งแต่งายละเอียดระดับต่ำ เช่น ความผิดปกติของฮาร์ดแวร์ที่เป็นเหตุผลเบื้องหลังโค้ดที่ซับซ้อนเป็นพิเศษ ไปจนถึงแนวคิดระดับสูง เช่น เหตุผลเบื้องหลังการมีคลาสหนึ่งๆ เมื่อนักพัฒนาคนอื่นเข้ามาแก้ไขในภายหลัง คอมเมนต์จะช่วยให้พวกเขาทำงานได้เร็วและแม่นยำยิ่งขึ้น หากไม่มีเอกสาร นักพัฒนาในอนาคตจะต้องเดาหรืออนุมานความรู้เดิมของนักพัฒนาขึ้นมาใหม่ ซึ่งจะใช้เวลาเพิ่มเติม และยังมีความเสี่ยงที่จะเกิดบั๊กหากนักพัฒนาคนใหม่เข้าใจเจตนาของนักออกแบบเดิมผิด คอมเมนต์ยังมีประโยชน์แม้กระทั่งในกรณีที่นักออกแบบเดิมเป็นคนแก้ไขโค้ดเอง เพราะหากผ่านไปเกินสองสามสัปดาห์ตั้งแต่คุณทำงานกับโค้ดชิ้นนั้นครั้งล่าสุด คุณจะลืมรายละเอียดมากมายของการออกแบบดั้งเดิมไปแล้ว

บทที่ 2 ได้อธิบายถึงสามวิธีที่ความซับซ้อนปรากฏในระบบซอฟต์แวร์:

Change amplification: การเปลี่ยนแปลงที่ดูเหมือนง่ายต้องแก้ไขโค้ดในหลายๆ จุด

Cognitive load: ในการที่จะเปลี่ยนแปลงโค้ด นักพัฒนาต้องสะสมข้อมูลจำนวนมาก

Unknown unknowns: ไม่ชัดเจนว่าโค้ดส่วนไหนที่ต้องแก้ไข หรือต้องพิจารณาข้อมูลใดบ้างเพื่อทำการเปลี่ยนแปลงเหล่านั้น

เอกสารที่ดีช่วยแก้ปัญหาในสองข้อหลังได้ เอกสารช่วยลด cognitive load โดยการให้ข้อมูลที่นักพัฒนาต้องการเพื่อทำการเปลี่ยนแปลง และทำให้ง่ายต่อการละเว้นข้อมูลที่ไม่เกี่ยวข้อง หากไม่มีเอกสารที่เพียงพอ นักพัฒนาอาจต้องอ่านโค้ดจำนวนมากเพื่อสร้างความเข้าใจในสิ่งที่นักออกแบบคิดขึ้นมาใหม่ เอกสารยังช่วยลด unknown unknowns ได้ด้วยการทำให้โครงสร้างของระบบชัดเจนขึ้น เพื่อให้เห็นว่าข้อมูลและโค้ดใดที่เกี่ยวข้องกับการเปลี่ยนแปลงใดๆ

บทที่ 2 ชี้ให้เห็นว่าสาเหตุหลักของความซับซ้อนคือ dependencies และ obscurity เอกสารที่ดีสามารถทำให้ dependencies ชัดเจนขึ้น และเติมเต็มช่องว่างเพื่อขจัดความคลุมเครือ

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