36 ポイント 投稿者 GN⁺ 2025-02-17 | 6件のコメント | WhatsAppで共有
  • NASAのソフトウェア開発10ルールに対する批判的分析
    • これらのルールは、極めて重要な組み込みシステム(例: 宇宙船ソフトウェア)のためのもの
    • しかし、こうしたルールが他の開発環境でも適切なのか、あるいは他の言語(C以外の言語)にも適用可能なのかについては議論が必要

1. 単純な制御フローを維持する(goto、setjmp/longjmp、再帰を禁止)

  • このルールは、例外処理(setjmp()/longjmp())と再帰を禁止する。
  • 再帰が必ずしも非効率とは限らない。適切な方法を使えば、再帰でも終了を保証できる。
  • 強制的に再帰をループへ変換すると、保守しにくいコードになる危険がある。

批判:

  • 終了保証は重要だが、極端な制限は可読性と保守性を損なう可能性がある。
  • 無条件の再帰禁止は、不必要な複雑さを招く可能性が高い。

2. すべてのループは明確な上限を持つべき

  • コンパイラがループの繰り返し回数を静的に解析できる必要がある。
  • しかし、単に上限を設定するだけでは実際の実行時間を保証しにくい。
  • 再帰の深さ制限を設けることは、ループの上限を設けるのと同じくらい安全でありうる。

批判:

  • 単に上限を設けるだけでは、現実的な実行可能時間を保証できない。
  • 上限を設定しても値が大きすぎれば、実質的には無限ループと大差ない。

3. 初期化後の動的メモリ割り当てを禁止

  • 組み込みシステムではメモリが限られているため、メモリ不足によるクラッシュを防ぐことが目的。
  • しかし、手動メモリ管理よりも予測可能な動的割り当てのほうが安全な場合もある。
  • たとえば、リアルタイムガベージコレクタ(RTGC)を使えば、動的割り当ても予測可能にできる。

批判:

  • 動的割り当てそのものを禁止するより、メモリ使用パターンを分析して安全性を確保するほうが、よりよいアプローチかもしれない。
  • 現代的な静的解析ツール(SPlint など)を活用すれば、動的メモリ関連のエラーを事前に検出できる。

4. 関数のサイズはA4用紙1枚以内に制限する(約60行)

  • 関数が長すぎると可読性が下がる、という理屈。
  • しかし、現代の開発環境にはコード折りたたみ機能があり、関数の長さより論理的単位の大きさのほうが重要。

批判:

  • 物理的な大きさ(行数)よりも、論理的複雑さを基準にすべき。
  • 関数を小さく分割すること自体が目的になってはいけない → かえって保守を難しくすることもある。

5. 関数ごとに少なくとも2つのassert文を使う

  • assertはデバッグと文書化に非常に有用。
  • しかし、無条件の個数制限は非効率になりうる。

批判:

  • assertの数よりも、どこでデータ妥当性検査が必要かを明確にすることが重要。
  • すべての引数と外部入力を検証するほうが、より実用的。

6. データオブジェクトのスコープを最小化する

  • ローカル変数の使用を推奨する良い原則。
  • しかし、関数だけでなく型や関数のスコープも最小化すべき。

批判:

  • Ada、Pascal、JavaScript、関数型言語では型や関数もローカルに宣言可能 → NASAのルールより優れたアプローチ。

7. 関数の戻り値および引数の妥当性検証を必須にする

  • 戻り値は必ずチェックすべき。
  • しかし、あらゆる場合をチェックするのは現実的に難しい。

批判:

  • 実行時エラーを防ぐには、可能な限り多くの検査が必要だが、実用上の限界も考慮しなければならない。
  • 特にCでは戻り値チェックが重要だが、現代言語(Java、Rust など)では型システムを活用してより安全に扱える。

8. プリプロセッサの使用を制限する(ヘッダのインクルードと単純なマクロのみ許可)

  • 複雑なマクロ、トークン結合、可変引数マクロ(...)を禁止。
  • しかし、可変引数マクロはデバッグツールとして有用なことがある。

批判:

  • プリプロセッサの使用を制限するより、可読性の高いマクロスタイルを推奨するほうが望ましい。
  • #ifdef のような条件付きコンパイルを禁じると、プラットフォーム非依存のコードを書くのが難しくなる可能性がある。

9. ポインタの使用を制限する(二重ポインタ禁止、関数ポインタ禁止)

  • 関数ポインタの使用禁止 → 高い安全性を目指す。
  • しかし、関数ポインタはコールバック、ストラテジーパターン、デバイスドライバなどに不可欠。

批判:

  • 関数ポインタなしで switch-case による関数選択を強制すると、コードの可読性が下がり、保守が難しくなる。
  • OS、ネットワークスタック、ドライバ開発では関数ポインタが不可欠。
  • ポインタ制限よりも、安全なポインタ使用を保証する方法(C++のスマートポインタ、Rust など)のほうが、よりよい解決策。

10. すべてのコードでコンパイラ警告を最大に設定し、静的解析ツールを使う

  • このルールは非常に良い推奨事項。
  • コンパイラ警告の解消 + 静的解析ツールの使用 = 安定性向上。

批判:

  • NASAの他のルール(例: ポインタ禁止、関数サイズ制限)は、単に静的解析ツールの限界を補うことを目的としている。
  • しかし、現代の静的解析ツールは非常に進歩しているため、過度な制限よりも、より精緻な解析手法を活用するほうが有用。

6件のコメント

 
regentag 2025-02-18

リアルタイム、組み込みの観点から見ると、どれも理解できるし必要なルールですね。静的解析器はこれらのルールを代わりに満たしてくれるのでしょうか?

例えば、動的割り当てを許可した場合、すべての使用シナリオでメモリ割り当てが成功することを保証できるのでしょうか?

ソフトウェアテストを学ぶと、いつも初日の最初の時間に言及される命題があります。その一つが「完璧なテストは不可能だ」です。

 
smboy86 2025-02-18

反対のほうがより目についてしまうあたり、
自分には合わないルールみたいですね(笑)

 
rtyu1120 2025-02-17

NASAだけでなく、航空・自動車など人命に直結する産業でも、似たようなコーディング規則を適用していることが多いようですね(笑)

 
ssssut 2025-02-17

https://github.com/kubernetes/kubernetes/…
Kubernetesのソースコードの中で、NASAスペースシャトルのアプリケーションソースコード作成方法で書かれたという「space shuttle style」のコードブロックを思い出しました。
関連HNスレッド: https://news.ycombinator.com/item?id=18772873

 
GN⁺ 2025-02-17
Hacker Newsの意見
  • 原文を読むと、各項目の目的が説明されている
  • 原文は主にC言語を対象としており、Cで書かれた重要なアプリケーションの信頼性をより厳密に検査できるよう最適化しようとしている
  • 原著者は自分が何をしているのかを明確に理解しており、Cコードを検証するいくつかの方法を説明している
  • 原文にある論理はすべて完全に理解できる
    • おそらく小規模なシステムでCを学んだからだろう
    • インプラント医療機器向けハードウェア用のCを学び、研究室でも同様の指針に従っていた
  • 最後の段落は素晴らしい
    • ルールは最初は厳しく感じられるかもしれないが、コードの正確性に命が懸かっている可能性がある場合を考えるべきだ
    • 自動車のシートベルトのように、最初は不便かもしれないが、時間が経てば自然に使うようになる
  • このルールに対する私の批判は、OPとはかなり異なるだろう
    • setjmp/longjmpを擁護する文章を最初から真剣に受け止めるのは難しかった
    • このパターンは、触ったことがある人なら誰にとっても明らかに問題がある
    • 文章はsetjmp/longjmpが例外処理だと主張している
    • 例外処理は良いものだと主張している
    • 2つ目の前提には重大な問題がある
  • ループについて最大反復回数を設定しろという意味だ
    • 10^90はばかげていて無関係だ
    • この時点で以降は読まなかった
  • ルールを批判するなら、次のような点に焦点を当てるだろう
    • 関数本体の長さは理解の単純さと相関せず、むしろルールが示唆するものとは逆かもしれない
    • 2つのアサーションは完全に恣意的であり、アサートできるものはすべてアサートすべきだ
    • Ada、Pascal (Delphi)、JavaScript、あるいは関数型言語を使う人は、可能な限りローカルに型と関数を宣言すべきだ
  • JavaScriptでの個人的なアプローチは、明示的に値をキャプチャしたい場合を除いて、ネストした形で関数を定義しないことだ
    • 古いメンタルモデルのせいかもしれない
    • パフォーマンスプロファイリングでは、関数が呼ばれるたびに再定義されるように見えた
    • 現代のJavaScriptインタプリタがそのように動作しているとは思わない
    • アロー関数の導入以降、深い最適化が行われているはずだ
    • 古い習慣は簡単には消えない
    • ローカル変数をキャプチャしない名前付き関数は、ファイル/モジュールスコープに置いている
  • 他の多くのメモは興味深く、非常に細かい点に及んでいる
    • 「技術的に正確であることが最良の正確さ」という感じで、古参エンジニアが好みそうだ
    • NASAのルールが伝えようとしている慎重さの全体的なトーンは非常に良いと思うし、大半を支持する
  • 文脈上、これらは「ルール」というより提案された慣行だ
    • 公式な「ルール」は「NPR」のような名前の文書にある
    • 開発者にはこの「ルール」に従う義務も無視する義務もない
  • GCCではコンパイル後にスタック使用量と呼び出し元-呼び出し先の関係を取得できる
    • setjmp()longjmp()は例外を処理する悪い方法だ
    • クリーンアップコードが実行されない
    • ルールの精神に従うなら、クリーンアップが必要なリソースは存在しないはずだ
  • 主な問題は各アプリケーションごとに異なる形で現れる
    • 反復制限を超えたとき、または起動時に割り当てた固定リソースが足りないときにどうするのか
  • 今どきのプログラマはコードを画面で読むのだから、紙のサイズがもはや関係ない理由が明確ではない
    • 標準ページと文字サイズについての繰り返しがあった
    • 紙の制約だけでなく、人間の制約のためでもある
  • 再帰に関するルールは、必要なスタック領域の静的に既知な境界を保証するためのものだ
    • コンパイラに依存しているという批判はもっともだが、ランタイムの上限を導くための前提条件でもある
    • 安全性が重要なシステムでは、保証された応答時間が必要だ
  • タイトルはルールに対する_批判_であることを示すべきだ
  • 厳格な型の使用を推奨する
    • すべてのスカラー型に対して厳格な型付け
    • ヤード・ポンド法の単位とメートル法の単位を混在させない
 
roxie 2025-02-21

> タイトルは規則に対する_批判_であることを示さなければならない

222