“มีคนบอกฉันว่า สิ่งที่แย่ที่สุดในการเป็นโปรแกรมเมอร์ที่อาจเกิดขึ้นได้ นั่นคือการที่มีโค้ดของคุณอยู่บนสภาพแวดล้อมในการทำงานจริง (Production) แล้วมันล้มเหลว (Crash)” ประโยคขึ้นต้นจากวีดีโอ “how NASA writes space-proof code” ที่แม้ว่ามันมีโอกาสที่จะเกิดขึ้น แต่ก็คงไม่มีโปรแกรมเมอร์คนใดอยากให้เกิดเลวร้ายแบบนี้แน่นอน
การพัฒนาซอฟต์แวร์ในปัจจุบันจึงมีแนวทางเพื่อแก้ไขบั๊กก่อนขึ้น Production หลายแบบ เช่น การสร้าง Unit Test หรือ Automated Testing ผ่าน CI/CD เป็นต้น แต่ถ้า Production นั้นคือบน “อวกาศ (Space)” แล้วยังพบว่าโค้ดที่เขียนนั้นมีบั๊ก “Null Pointer” หรือ “Dereference” ในขณะที่ดาวเทียมที่กำลังถูกชนและอาจจะพังทลายหายไป จนทำให้ไม่สามารถจะควบคุมดาวเทียมอะไรได้เลย อะไรคือแนวทางที่ควรทำ เพื่อหลีกเลี่ยงเหตุการณ์ดังกล่าวให้เกิดขึ้น
เหตุนี้เอง NASA องค์การบริหารการบินและอวกาศแห่งชาติสหรัฐอเมริกา จึงได้มีแนวทางให้กับนักพัฒนาระบบด้วยการกำหนด10 กฎเหล็กให้พึงปฏิบัติ หรือ “The Power of 10 Rules”โดย Gerard J. Holzman หัวหน้า Jet Propulsion Laboratory (JPL) ห้องปฏิบัติการเพื่อซอฟต์แวร์ที่น่าเชื่อ (Laboratory for Reliable Software) แห่ง NASA ซึ่งถ้าหากงานใดที่ต้องการความปลอดภัยระดับสูงสุดพร้อมกับทำให้โค้ดที่เขียนสามารถเข้าใจและวิเคราะห์ต่อยอดได้ง่าย ต้องดำเนินการตาม 10 กฎ ดังต่อไปนี้
#01 Simple Control Flow
สิ่งแรกที่ต้องทำ คือการเขียนโฟลวของโค้ดให้ “เรียบง่าย (Simple)” โดยไม่เลือกใช้ goto setjmp longjmp หรือแม้กระทั่ง Recursion ที่เป็นการเขียนโค้ดเพื่อวนลูปไปที่ฟังก์ชันตัวเอง เพราะการเขียนโค้ดในลักษณะต่าง ๆ เหล่านี้ จะทำให้ติดตามโฟลว การทำงานได้ยากมาก และมีโอกาสที่จะทำให้เกิดเหตุการณ์ที่ไม่สามารถควบคุมได้จนระบบ Crash และหยุดการทำงานได้ในที่สุด โดยเฉพาะอย่างยิ่งในระบบจำพวก Embedded System ต่าง ๆ
#02 Limit All Loops
ทุกลูปที่เขียนต้องมีการกำหนดขอบบนสุด (Upper bound) ของลูปเสมอ แม้ว่าจะมีโค้ดเงื่อนไขที่ดักไว้แล้วว่าจะให้หยุดเมื่อใดอยู่แล้ว แต่เพื่อป้องกันไม่ให้โค้ดรันไปอย่างไม่มีที่สิ้นสุดไม่ว่าจะด้วยเหตุใดก็ตาม NASA จึงกำหนดให้ใส่อีกตัวแปรในเงื่อนไข เพื่อกำหนดขอบบนสุดว่าลูปนี้จะสิ้นสุดในรอบที่เท่าไหร่ไว้ด้วยเสมอ (ตามภาพคือการใส่ MAX_ITER เพิ่มเติมเข้าไปในเงื่อนไขของ while ด้วย)
#03 Don’t Use the Heap
หนึ่งกฎที่น่าสนใจของ NASA คือการไม่ให้ใช้ Heap หรือว่า Memory Allocation ใด ๆ ทั้งสิ้นเลย เพื่อป้องกันไม่ให้โค้ดมีปัญหาในส่วนที่เกี่ยวข้องกับเรื่องหน่วยความจำ (Memory) ที่มีอยูามากมาย เช่น Memory Leaks, Heap Overflows หรือช่องโหว่ use-after-free เป็นต้น
ที่สำคัญ โค้ดในส่วนที่อาจจะทำให้เกิดบั๊กเกี่ยวกับ Memory นั้นจะไม่สามารถพบเจอได้ในเครื่องมือวิเคราะห์โค้ด (Static Code Analyzer) ใด ๆ อีกด้วย ดังนั้น เพื่อให้การใช้งาน Memory เป็นสิ่งที่กำหนดได้ (Deterministic) จึงต้องหลีกเลี่ยงไม่ให้ใช้ Heap หรือคำสั่งจำพวก malloc หรือ free ทั้งหมด
#04 Limit Function Size
ในหนึ่งฟังก์ชันให้ “ดำเนินการ (Action) ใด ๆ อย่างเดียวเท่านั้น” แม้ว่า Action ดังกล่าวจะมีขั้นตอนในการดำเนินการหลายอย่าง แต่ขอให้มั่นใจว่าฟังก์ชันนั้นสามารถอ่านเข้าใจได้ง่ายอย่างรวดเร็ว โดย NASA แนะนำว่าในฟังก์ชันหนึ่ง ๆ นั้นไม่ควรยาวเกินกว่า 60 บรรทัด หรือว่าไม่ควรจะเกิน 1 หน้ากระดาษ A4 ทั่ว ๆ ไป เพื่อให้มั่นใจว่าพอส่งมอบโค้ดต่อให้คนอื่นตรวจแล้วจะสามารถอ่านเข้าใจว่าฟังก์ชันนั้นทำงานอะไรได้ทันที รวมทั้งยังกระชับเพียงพอที่จะสามารถทดสอบใน Unit Test ได้อย่างครอบคลุม
#05 Two Runtime Assertions per Function
ข้อนี้ไม่ได้มีนำเสนอในวีดีโอ แต่เป็นอีกข้อที่ค่อนข้างสำคัญ โดย NASA แนะนำให้ใช้ 2 Runtime Assertions ต่อฟังก์ชันเป็นขั้นต่ำ เพื่อตรวจสอบว่าข้อสันนิษฐานที่เขียนโปรแกรมขึ้นมานั้นถูกต้องหรือไม่อย่างสม่ำเสมอ ทั้ง ณ เวลาที่กำลังเขียนโค้ดและตอนรันใช้งาน
#06 Practice Data Hiding
NASA สนับสนุนแนวคิดการซ่อนข้อมูล (Data Hiding) หรือการประกาศตัวแปร (Variable) ที่ใช้งานให้มีสโคป (Scope) ต่ำที่สุดเท่าที่จะเป็นไปได้ ซึ่งนอกจากจะสามารถจำกัดการเข้าถึงตัวแปรดังกล่าวอยู่ในเฉพาะสโคปที่กำหนดไว้เท่านั้นแล้ว ยังสามารถลดจำนวนการไปไล่ในบริเวณที่ไม่เกี่ยวข้องในตอนที่ต้อง Debug โค้ดว่าทำไมค่าตัวแปรดังกล่าวถึงไม่ถูกต้องได้ด้วย
#07 Check Return Values
ปัญหาทั่วไปที่มักจะพบในบรรดา Junior Developer คือการลืมตรวจสอบค่า (Value) ที่ตอบกลับ (Return) ออกไปในทุก ๆ ฟังก์ชันที่ไม่ได้ตอบกลับเป็นค่าเปล่า (Void) โดย NASA แนะว่าต้องตรวจสอบ Non-Void Function ทุกครั้งว่าฟังก์ชันนั้น ๆ สามารถส่งค่ากลับมาได้อย่างถูกต้องหรือไม่ ซึ่งวิธีการสำหรับคนตรวจสอบโค้ดที่ทำได้ง่าย ๆ เลย คือการเข้าไปดูในหน้า Manual ที่สร้างขึ้นมาสักเล็กน้อยว่าฟังก์ชันดังกล่าวนั้นทำงานอย่างไรและจะ Return อะไรออกมา ซึ่งถ้าหากพบว่าฟังก์ชันที่ควรจะต้อง Return อะไรออกไปกลับไม่ได้มีการ Return ค่าจริง NASA แนะว่าให้แปลงค่า (Cast) ให้เป็น Void ไว้เพื่อระบุว่าเป็นฟังก์ชันที่ไร้ประโยชน์ ซึ่งถ้าหากนักพัฒนาลืมทำสิ่งดังกล่าว นั่นแปลว่าฟังก์ชันนั้นควรจะต้องนำออกจาก Code Review
#08 Limit the Preprocessor
NASA จำกัดการใช้งาน C Preprocessor ให้เฉพาะเท่าที่จำเป็นเท่านั้น เพราะ NASA มองว่า C Preprocessor คือ “ส่วนของโค้ดที่ทำให้งง ที่สามารถทำลายความชัดเจนของโค้ด และสร้างความสับสนให้กับ Static Code Analyzer” โดยเฉพาะอย่างยิ่งในการทำ “Conditional Compilation” หรือการมีธง (Flag) เงื่อนไขในการเปลี่ยนโค้ดไปตอนเวลาที่แปลงโค้ด (Compile Time) ซึ่งถ้าหากมี 10 Flag ที่เปลี่ยนแปลงได้ นั่นแปลว่าจะต้องมีการ Compile และตรวจสอบถึง 210 (2 ยกกำลัง 10) ครั้งเพื่อให้ทดสอบได้อย่างครอบคลุม ซึ่งเป็นสิ่งที่ยากในการตรวจสอบและขยาย (Scale) ต่อไปได้
#09 Restrict Pointers Use
อีกกฎที่น่าสนใจของทาง NASA คือ “การจำกัดการใช้ Pointer” โดย NASA มองว่า Pointer นั้นต้องไม่สามารถที่จะถูก Dereference ได้มากกว่า 1 ระดับ ณ เวลาใดเวลาหนึ่ง เพราะแม้ Pointer จะทรงพลัง แต่ก็มีการใช้งานอย่างผิด ๆ ได้ง่ายมาก ซึ่งการกำหนดให้สามารถ Dereference ได้แค่ระดับเดียวนั้นจะทำให้เกิดโครงสร้าง (Structure) ที่สามารถติดตาม Pointer ได้อย่างเหมาะสม และที่สำคัญ NASA ยังกำหนดไว้ว่า “ไม่ให้ใช้ Function Pointer เด็ดขาด” อีกด้วย เพราะสิ่งนี้จะทำให้โฟลวของโค้ดมีความสับสนและทำให้ยากมาก ๆ ในการวิเคราะห์และทดสอบได้อย่างครบถ้วน
#10 Be Pedantic
ตอน Compile โค้ดนั้น ให้ Compile โดยเปิด Warning ทั้งหมด เพื่อให้เห็นทุก Warning ที่เกิดขึ้นซึ่งต้องมองว่าทุก Warning ที่มีนั้นคือ Error ที่เกิดขึ้นในโค้ด และจะต้องถูกปรับแก้ไขในทุก Warning ที่มีก่อนที่จะปล่อยโค้ดดังกล่าวไปใช้งานจริงเสมอ ซึ่งสิ่งนี้เพื่อให้นักพัฒนาระบบมั่นใจว่าโค้ดนั้นได้ผ่านการตรวจสอบโดย Static Code Analyzer หลาย ๆ ตัวและผ่าน Unit Test แล้วก่อนนำโปรแกรมหรือซอฟต์แวร์ไปใช้งานจริง