36 คะแนน โดย GN⁺ 2025-02-17 | 6 ความคิดเห็น | แชร์ทาง WhatsApp
  • บทวิเคราะห์เชิงวิพากษ์เกี่ยวกับกฎ 10 ข้อของ NASA สำหรับการพัฒนาซอฟต์แวร์
    • กฎเหล่านี้มีไว้สำหรับระบบฝังตัวที่มีความสำคัญอย่างยิ่งยวด (เช่น ซอฟต์แวร์ยานอวกาศ)
    • แต่ก็จำเป็นต้องถกเถียงกันว่ากฎเหล่านี้เหมาะสมกับสภาพแวดล้อมการพัฒนาแบบอื่นด้วยหรือไม่ หรือสามารถนำไปใช้กับภาษาอื่นที่ไม่ใช่ C ได้หรือไม่

1. รักษาโฟลว์การควบคุมให้เรียบง่าย (ห้ามใช้ goto, setjmp/longjmp, การเรียกซ้ำ)

  • กฎข้อนี้ห้ามใช้การจัดการข้อยกเว้น (setjmp()/longjmp()) และการเรียกซ้ำ
  • การเรียกซ้ำไม่ได้ไม่มีประสิทธิภาพเสมอไป หากใช้วิธีที่เหมาะสมก็สามารถรับประกันการสิ้นสุดได้
  • การบังคับแปลงการเรียกซ้ำให้เป็นลูปอาจเสี่ยงทำให้โค้ดดูแลรักษายากขึ้น

คำวิจารณ์:

  • การรับประกันการสิ้นสุดนั้นสำคัญ แต่การจำกัดอย่างสุดโต่งอาจทำลายความอ่านง่ายและการบำรุงรักษา
  • การห้ามการเรียกซ้ำแบบเหมารวมมีแนวโน้มจะก่อให้เกิดความซับซ้อนที่ไม่จำเป็น

2. ทุกลูปต้องมีขอบเขตบนที่ชัดเจน

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

คำวิจารณ์:

  • การกำหนดขอบเขตบนเพียงอย่างเดียวไม่อาจรับประกันเวลาทำงานที่เป็นจริงได้
  • แม้จะตั้งขอบเขตบนไว้ หากค่านั้นใหญ่เกินไป ในทางปฏิบัติก็แทบไม่ต่างจากลูปไม่สิ้นสุด

3. ห้ามจัดสรรหน่วยความจำแบบไดนามิกหลังการเริ่มต้นระบบ

  • ในระบบฝังตัว หน่วยความจำมีจำกัด จึงมีเป้าหมายเพื่อป้องกันการล่มจากหน่วยความจำไม่พอ
  • แต่บางครั้งการจัดสรรแบบไดนามิกที่คาดการณ์ได้อาจปลอดภัยกว่าการจัดการหน่วยความจำด้วยมือ
  • ตัวอย่างเช่น หากใช้ real-time garbage collector (RTGC) ก็อาจทำให้การจัดสรรแบบไดนามิกคาดการณ์ได้เช่นกัน

คำวิจารณ์:

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

4. จำกัดขนาดฟังก์ชันให้อยู่ภายในกระดาษ A4 หนึ่งหน้า (ประมาณ 60 บรรทัด)

  • ตรรกะคือหากฟังก์ชันยาวเกินไป ความอ่านง่ายจะลดลง
  • แต่ในสภาพแวดล้อมการพัฒนาสมัยใหม่มีฟังก์ชัน code folding ดังนั้นขนาดของหน่วยตรรกะจึงสำคัญกว่าความยาวของฟังก์ชัน

คำวิจารณ์:

  • ควรใช้ความซับซ้อนเชิงตรรกะเป็นเกณฑ์ มากกว่าขนาดทางกายภาพ (จำนวนบรรทัด)
  • การแยกฟังก์ชันให้เล็กลงไม่ควรกลายเป็นเป้าหมายในตัวเอง → เพราะอาจทำให้ดูแลรักษายากขึ้นเสียอีก

5. แต่ละฟังก์ชันต้องมี assert อย่างน้อยสองจุด

  • assert มีประโยชน์มากต่อการดีบักและการทำเอกสาร
  • แต่การบังคับกำหนดจำนวนตายตัวอาจไม่มีประสิทธิภาพ

คำวิจารณ์:

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

6. ลดขอบเขตการมองเห็นของออบเจ็กต์ข้อมูลให้เล็กที่สุด

  • เป็นหลักการที่ดีซึ่งสนับสนุนการใช้ตัวแปรภายใน
  • แต่ไม่ใช่แค่ฟังก์ชันเท่านั้น ขอบเขตของชนิดข้อมูลและฟังก์ชันก็ควรถูกลดให้เล็กที่สุดด้วย

คำวิจารณ์:

  • ใน Ada, Pascal, JavaScript และภาษาฟังก์ชัน ชนิดข้อมูลและฟังก์ชันก็สามารถประกาศแบบเฉพาะที่ได้ → เป็นแนวทางที่ดีกว่ากฎของ NASA

7. ต้องตรวจสอบค่าที่คืนจากฟังก์ชันและความถูกต้องของพารามิเตอร์

  • ต้องตรวจสอบค่าที่คืนกลับเสมอ
  • แต่การตรวจสอบทุกกรณีทั้งหมดนั้นทำได้ยากในทางปฏิบัติ

คำวิจารณ์:

  • เพื่อป้องกันข้อผิดพลาดขณะรัน จำเป็นต้องมีการตรวจสอบให้มากที่สุดเท่าที่ทำได้ แต่ก็ต้องคำนึงถึงข้อจำกัดด้านการใช้งานจริง
  • โดยเฉพาะใน C การตรวจสอบค่าที่คืนกลับมีความสำคัญ แต่ในภาษาสมัยใหม่ (เช่น Java, Rust) สามารถใช้ระบบชนิดข้อมูลเพื่อจัดการให้ปลอดภัยยิ่งขึ้นได้

8. จำกัดการใช้ preprocessor (อนุญาตเฉพาะการ include header และแมโครง่าย ๆ)

  • ห้ามใช้แมโครซับซ้อน, token pasting, variadic macro(...)
  • อย่างไรก็ตาม variadic macro อาจมีประโยชน์เป็นเครื่องมือดีบัก

คำวิจารณ์:

  • แทนที่จะจำกัดการใช้ preprocessor การส่งเสริมสไตล์การเขียนแมโครที่อ่านง่ายอาจเหมาะสมกว่า
  • หากห้าม conditional compilation อย่าง #ifdef ก็อาจทำให้เขียนโค้ดข้ามแพลตฟอร์มได้ยากขึ้น

9. จำกัดการใช้พอยน์เตอร์ (ห้ามใช้พอยน์เตอร์สองชั้น, ห้ามใช้ function pointer)

  • ห้ามใช้ function pointer → มีเป้าหมายเพื่อความเสถียรสูง
  • แต่ function pointer เป็นสิ่งจำเป็นสำหรับ callback, strategy pattern, device driver เป็นต้น

คำวิจารณ์:

  • หากบังคับให้เลือกฟังก์ชันผ่าน switch-case แทน function pointer จะทำให้อ่านโค้ดยากขึ้นและบำรุงรักษายากขึ้น
  • ในการพัฒนาระบบปฏิบัติการ, network stack และไดรเวอร์นั้น function pointer เป็นสิ่งจำเป็น
  • แทนที่จะจำกัดพอยน์เตอร์ วิธีที่ดีกว่าคือทำให้มั่นใจว่ามีการใช้พอยน์เตอร์อย่างปลอดภัย (เช่น smart pointer ของ C++, Rust เป็นต้น)

10. ตั้งค่าคำเตือนของคอมไพเลอร์ให้สูงสุดสำหรับโค้ดทั้งหมด และใช้เครื่องมือวิเคราะห์แบบสถิต

  • กฎข้อนี้เป็นคำแนะนำที่ดีมาก
  • การกำจัดคำเตือนจากคอมไพเลอร์ + การใช้เครื่องมือวิเคราะห์แบบสถิต = เพิ่มความเสถียร

คำวิจารณ์:

  • กฎอื่น ๆ ของ NASA (เช่น ห้ามใช้พอยน์เตอร์, จำกัดขนาดฟังก์ชัน) มีจุดประสงค์เพียงเพื่อชดเชยข้อจำกัดของเครื่องมือวิเคราะห์แบบสถิต
  • แต่เนื่องจากเครื่องมือวิเคราะห์แบบสถิตสมัยใหม่พัฒนาไปมากแล้ว การใช้เทคนิคการวิเคราะห์ที่ละเอียดขึ้นย่อมมีประโยชน์มากกว่าการจำกัดที่เข้มงวดเกินไป

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

 
regentag 2025-02-18

ถ้ามองทั้งหมดจากมุมมองของระบบเรียลไทม์และระบบฝังตัว ก็เป็นกฎที่เข้าใจได้และจำเป็นทั้งนั้นครับ เครื่องมือวิเคราะห์แบบสแตติกจะช่วยทำหน้าที่แทนกฎเหล่านี้ได้ไหม?

ยกตัวอย่างเช่น ถ้าอนุญาตให้มีการจัดสรรหน่วยความจำแบบไดนามิก จะสามารถรับประกันได้หรือไม่ว่าในการใช้งานทุกสถานการณ์ การจัดสรรหน่วยความจำจะสำเร็จเสมอ?

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

 
smboy86 2025-02-18

ดูเหมือนว่าผมจะสะดุดตากับข้อโต้แย้งมากกว่า
คงเป็นกฎที่ไม่ค่อยเข้ากับผมเท่าไหร่นะครับ 55

 
rtyu1120 2025-02-17

ดูเหมือนว่าไม่ใช่แค่ NASA เท่านั้น แต่ในอุตสาหกรรมอย่างการบิน/ยานยนต์ที่เกี่ยวข้องกับชีวิตโดยตรง ก็มักจะใช้กฎการเขียนโค้ดที่คล้ายกันอยู่บ่อย ๆ เหมือนกันนะ 555

 
ssssut 2025-02-17

https://github.com/kubernetes/kubernetes/…
ในซอร์สโค้ดของ Kubernetes ทำให้นึกถึงบล็อกโค้ดแบบ 'space shuttle style' ที่ว่ากันว่าเขียนตามแนวทางการเขียนซอร์สโค้ดของแอปพลิเคชัน NASA Space Shuttle ขึ้นมาครับ
เธรด HN ที่เกี่ยวข้อง: https://news.ycombinator.com/item?id=18772873

 
GN⁺ 2025-02-17
ความคิดเห็นจาก Hacker News
  • ถ้าอ่านต้นฉบับ จะเห็นว่ามีการอธิบายจุดประสงค์ของแต่ละข้อไว้
  • ต้นฉบับมุ่งไปที่ภาษา C เป็นหลัก และพยายามปรับให้เหมาะกับการตรวจสอบความน่าเชื่อถือของแอปพลิเคชันสำคัญที่เขียนด้วย C ได้อย่างเข้มงวดยิ่งขึ้น
  • ผู้เขียนต้นฉบับเข้าใจอย่างชัดเจนว่าตัวเองกำลังทำอะไรอยู่ และอธิบายหลายวิธีในการตรวจสอบโค้ด C
  • เหตุผลในต้นฉบับเข้าใจได้อย่างสมบูรณ์ทั้งหมด
    • อาจเป็นเพราะฉันเรียนรู้ C บนระบบขนาดเล็ก
    • ฉันเรียนรู้ C สำหรับฮาร์ดแวร์ของอุปกรณ์การแพทย์แบบฝังในร่างกาย และในห้องแล็บก็ปฏิบัติตามแนวทางคล้ายกัน
  • ย่อหน้าสุดท้ายนั้นยอดเยี่ยม
    • ตอนแรกกฎอาจรู้สึกเข้มงวด แต่ต้องคำนึงถึงกรณีที่ความถูกต้องของโค้ดอาจเป็นเรื่องความเป็นความตาย
    • เหมือนเข็มขัดนิรภัยในรถยนต์ ตอนแรกอาจไม่สบาย แต่เมื่อเวลาผ่านไปก็จะใช้ได้อย่างเป็นธรรมชาติ
  • คำวิจารณ์ของฉันต่อกฎเหล่านี้คงจะแตกต่างจาก OP มาก
    • ตั้งแต่แรกก็ยากที่จะจริงจังกับบทความที่ปกป้อง setjmp/longjmp
    • ใครก็ตามที่เคยลองใช้แพตเทิร์นนี้จะเห็นชัดว่ามันมีปัญหา
    • บทความอ้างว่า setjmp/longjmp คือการจัดการข้อยกเว้น
    • และอ้างว่าการจัดการข้อยกเว้นเป็นสิ่งที่ดี
    • สมมติฐานข้อที่สองมีปัญหาร้ายแรง
  • หมายถึงให้กำหนดจำนวนรอบการวนซ้ำสูงสุดสำหรับลูป
    • 10^90 เป็นตัวเลขที่ไร้สาระและไม่เกี่ยวข้อง
    • หลังจากจุดนี้ฉันก็ไม่ได้อ่านต่อ
  • ถ้าจะวิจารณ์กฎ ก็ควรโฟกัสที่ประเด็นต่อไปนี้
    • ความยาวของเนื้อหาฟังก์ชันไม่ได้มีความสัมพันธ์กับความเรียบง่ายในการทำความเข้าใจ และอาจตรงกันข้ามกับที่กฎชี้นำเสียด้วยซ้ำ
    • การมี assertions 2 จุดนั้นเป็นตัวเลขที่ตั้งขึ้นมาเองล้วนๆ และควร assert ทุกสิ่งที่สามารถ assert ได้
    • คนที่ใช้ Ada, Pascal (Delphi), JavaScript หรือภาษาสายฟังก์ชัน ควรประกาศ type และฟังก์ชันให้มีขอบเขตเฉพาะที่มากที่สุดเท่าที่เป็นไปได้
  • แนวทางส่วนตัวของฉันใน JavaScript คือจะไม่กำหนดฟังก์ชันแบบซ้อนกัน เว้นแต่ตั้งใจจะ capture ค่าอย่างชัดเจน
    • อาจเป็นเพราะ mental model แบบเก่า
    • ในการทำ performance profiling มันเคยแสดงให้เห็นว่ามีการนิยามฟังก์ชันใหม่ทุกครั้งที่ถูกเรียก
    • ฉันไม่คิดว่า JavaScript interpreter สมัยใหม่จะทำงานแบบนั้น
    • น่าจะมีการปรับแต่งเชิงลึกเกิดขึ้นหลังการมาของ arrow function
    • นิสัยเก่าเลิกยาก
    • ฟังก์ชันที่มีชื่อและไม่ capture local variable จะคงไว้ที่ขอบเขตไฟล์/โมดูล
  • บันทึกอื่นๆ อีกมากก็น่าสนใจและลงรายละเอียดมาก
    • เป็นแนวแบบ "ความถูกต้องที่ถูกต้องตามเทคนิคคือความถูกต้องที่ดีที่สุด" ซึ่งวิศวกรรุ่นเก่าชอบ
    • ฉันคิดว่าโทนโดยรวมของความรอบคอบที่กฎของ NASA พยายามสื่อออกมานั้นดีมาก และเห็นด้วยกับส่วนใหญ่
  • ตามบริบท สิ่งเหล่านี้เป็น "แนวปฏิบัติที่เสนอ" มากกว่าจะเป็น "กฎ"
    • "กฎ" อย่างเป็นทางการจะอยู่ในเอกสารชื่อประมาณ "NPR"
    • นักพัฒนาไม่ได้มีหน้าที่ต้องปฏิบัติตามหรือเพิกเฉยต่อ "กฎ" เหล่านี้
  • GCC สามารถให้ข้อมูลการใช้สแตกและความสัมพันธ์ระหว่างผู้เรียก-ผู้ถูกเรียกได้หลังคอมไพล์
    • setjmp() และ longjmp() เป็นวิธีที่แย่ในการจัดการข้อยกเว้น
    • โค้ดทำความสะอาดจะไม่ถูกรัน
    • ถ้าทำตามเจตนารมณ์ของกฎ ก็ไม่ควรมีทรัพยากรที่ต้องทำความสะอาด
  • ปัญหาหลักจะแสดงออกต่างกันไปในแต่ละแอปพลิเคชัน
    • จะทำอย่างไรเมื่อเกินขีดจำกัดการวนซ้ำ หรือเมื่อทรัพยากรคงที่ที่จัดสรรไว้ตอนเริ่มต้นไม่เพียงพอ
  • ทุกวันนี้โปรแกรมเมอร์อ่านโค้ดบนหน้าจอ จึงไม่ชัดเจนว่าทำไมขนาดกระดาษถึงยังเกี่ยวข้อง
    • มีการพูดซ้ำเรื่องหน้ามาตรฐานและขนาดตัวอักษร
    • ไม่ใช่แค่ข้อจำกัดของกระดาษ แต่รวมถึงข้อจำกัดของมนุษย์ด้วย
  • กฎเรื่อง recursion มีไว้เพื่อรับประกันขอบเขตที่ทราบได้แบบสถิตของพื้นที่สแตกที่ต้องใช้
    • คำวิจารณ์ว่าเรื่องนี้ต้องพึ่งคอมไพเลอร์ก็ถูกต้อง แต่เป็นเงื่อนไขเบื้องต้นในการหาขอบบนของรันไทม์
    • ระบบที่ความปลอดภัยมีความสำคัญจำเป็นต้องมีเวลาตอบสนองที่รับประกันได้
  • ชื่อเรื่องควรระบุว่านี่เป็น คำวิจารณ์ ของกฎ
  • แนะนำให้ใช้ type แบบเข้มงวด
    • ใช้ type แบบเข้มงวดกับ scalar type ทุกชนิด
    • อย่าปะปนหน่วยอิมพีเรียลกับหน่วยเมตริก
 
roxie 2025-02-21

> ชื่อเรื่องต้องสื่อว่าเป็น คำวิจารณ์ ต่อกฎดังกล่าว

222