ผมเพิ่งได้กลับมาอ่านหนังสือ "A Philosophy of Software Design" อ่านไปได้เกือบครึ่งเล่มแล้ว โดยหนังสือพูดถึงนิยามของความซับซ้อน (Complexity) ใน Software Design ได้อย่างน่าสนใจทีเดียวครับ
Software Complexity คือ อะไรก็ตามที่เกี่ยวข้องกับโครงสร้างของซอฟต์แวร์ซึ่งทำให้ยากในการทำความเข้าใจและแก้ไขระบบนั้นๆ
ความซับซ้อนของระบบโดยรวม = ผลรวมของ (ความซับซ้อนใน Module ย่อย × เวลาที่ต้องเสียไปในการทำความเข้าใจและแก้ไข Module นั้น)
หมายความว่า การมี Module ที่ซับซ้อนมากๆ ไม่ได้ทำให้ระบบโดยรวมนั้นซับซ้อนขึ้นเสมอไป ถ้าหากเราไม่ต้องเสียเวลาไปแก้ไขส่วนย่อยนั้นบ่อยๆ
ความซับซ้อนจะชัดเจนต่อคนอ่านโค้ดมากกว่าคนที่เขียนโค้ดนั้นเองเสมอ
ถ้าคุณคิดว่าโค้ดคุณไม่ซับซ้อน แต่คนอ่านบอกว่ามันซับซ้อน งั้นมันก็ซับซ้อนแหละ
3 อาการของ "ความซับซ้อน" ใน Software
ต่อไปเป็น 3 อาการที่พบได้บ่อยๆ ในซอร์ฟแวร์ที่ซับซ้อน
1. การแก้โค้ดง่ายๆ 1 จุด ต้องโดดไปแก้โค้ดหลายๆ ที่ (Change amplification)
อันนี้ไม่ได้เกิดขึ้นแค่จากการละเมิดกฎ DRY (Don't Repeat Yourself) เท่านั้น
แต่ยังเกิดจากการแบ่ง Layer ของ Abstraction ที่ไม่ถูกต้อง หรือ การออกแบบ Class ได้ไม่ดีอีกด้วย
2. การที่ Dev ต้องรู้ข้อมูลหลายอย่างในหัวก่อน จึงจะแก้โค้ดได้ (Cognitive load)
ผู้เขียนที่เป็นอาจารย์ ยกตัวอย่าง การเขียนไลบรารีสำหรับจัดการ HTTP Requests ที่นักเรียนของเขาแยก Class สำหรับรับ Request และ Parse Request ออกเป็นคนละ Class กัน ทำให้ข้อมูลเกี่ยวกับการจัดการ HTTP Request นั้น "รั่วไหล" ไปยัง 2 Class คนที่เอาไลบรารีนี้ไปใช้ต้องเรียกใช้คลาสทั้งคู่ ทั้งที่จริงๆ แล้วคลาสทั้งสองยุบรวมเป็นคลาสเดียวกันจะง่ายกว่าในการทำความเข้าใจและใช้งานบางครั้ง Class หรือฟังก์ชันที่มีจำนวนบรรทัดยาวกว่า ก็ทำให้ Cognitive load นั้นน้อยลงกว่าการเป็น Class / ฟังก์ชันสั้นๆ (หนังสือนำเสนอแนวคิดที่กล่าวว่า "Modules should be deep" ด้วย ซึ่งจะมาเล่าให้ฟังในครั้งหน้านะครับ)
3. การ "ไม่รู้ว่าไม่รู้" (Unknown unknowns)
การที่ Developer อ่านโค้ดแล้ว แต่ไม่รู้ว่าต้องแก้ตรงไหนบ้าง เพื่อให้ได้ผลลัพธ์ที่ต้องการ หรือ ไม่รู้ว่าการแก้โค้ดในครั้งนั้นๆ นำมาซึ่งปัญหาอะไร
ตัวอย่าง ที่ผู้เขียนยกมา คือ การที่เราประกาศ Global variable "Color" สำหรับปรับสีในหน้าต่างๆ ของเว็บไซต์ 1 เว็บ แต่ดันมีตัวแปรชื่อ "DarkerColor" เป็น Local variable แทรกอยู่ตามหน้าต่างๆ ซึ่งค่าเริ่มต้นเป็นการ hardcode ค่าสีใกล้เคียงกับตัวแปร Color แต่หม่นกว่านิดหน่อย พอ Dev คนอื่นต้องการจะเปลี่ยนสีธีมของเว็บ แล้วเห็นแค่ Global variable Color ก็อาจจะเข้าใจว่าแก้แค่ตัวแปรนี้ก็พอแล้ว
Complexity is Incremental
ความซับซ้อนไม่ได้เกิดขึ้นจากแค่การก้าวพลาดก้าวเดียว แต่เกิดจากความผิดพลาดในการออกแบบเล็กๆ ที่สะสมไปเรื่อยๆ จนเป็นปัญหาที่ใหญ่ขึ้นในระยะยาว
ซึ่งจะเกิดขึ้นเมื่อเราเขียนโปรแกรมด้วย Mindset แบบ Tactical programming หรือ เน้นให้ฟีเจอร์นั้นเสร็จเป็นหลัก
โดยไม่คำนึงถึงโครงสร้างในระยะยาวของซอฟต์แวร์ ซึ่งตรงนี้ผู้เขียนได้แนะนำให้ใช้เวลา 10-20% ในการเขียนโค้ดของแต่ละ Module เพื่อพิจารณาแนวทางการเขียนโค้ดหลายๆ แบบว่าแนวทางไหนดีที่สุดในระยะยาว หรือ ที่เรียกว่า Strategic Programming นั่นเอง
แต่ถ้าบริษัท หรือ สภาพแวดล้อมที่เราทำงานอยู่ไม่โอเคกับการใช้เวลาเพิ่ม 10-20% ตรงนี้?
ผู้เขียนได้เน้นย้ำเรื่องนี้ไว้ว่า การทิ้งเวลา 10-20% ตรงนี้ จะทำให้เมื่อโค้ดของคุณกลายเป็นสปาเก็ตตี้ไปแล้ว คุณก็จะไม่มีเวลามาแก้มันทีหลังอยู่ดี ต้องแก้ด้วยแนวคิดแบบ Tactical Programming ต่อไปเรื่อยๆ และ พบเจอกับปัญหาเดิมๆ อยู่ดี
เหมือนกับในกรณีของ Facebook ที่ช่วงแรกใช้แนวคิดในการพัฒนาซอฟต์แวร์ว่า "Move fast and break things" ซึ่งทำให้ความเร็วในการพัฒนาเพิ่มขึ้นก็จริง แต่ Codebase ก็พังมากๆ ในระยะยาวจนส่งผลกระทบมาถึงทุกวันนี้