NASAs 10 Regeln für die Softwareentwicklung
(cs.otago.ac.nz)- Eine kritische Analyse der 10 Regeln für die Softwareentwicklung von NASA
- Diese Regeln sind für extrem kritische eingebettete Systeme gedacht, z. B. Raumfahrzeug-Software
- Es muss jedoch diskutiert werden, ob diese Regeln auch in anderen Entwicklungsumgebungen angemessen sind oder sich auf andere Sprachen als C anwenden lassen
1. Einen einfachen Kontrollfluss beibehalten (goto, setjmp/longjmp, Rekursion verboten)
- Diese Regel verbietet Ausnahmebehandlung (
setjmp()/longjmp()) und Rekursion. - Rekursion ist nicht zwangsläufig ineffizient. Mit geeigneten Methoden kann auch bei Rekursion die Terminierung garantiert werden.
- Wenn man Rekursion zwanghaft in Schleifen umwandelt, besteht das Risiko, dass schwer wartbarer Code entsteht.
Kritik:
- Die Garantie der Terminierung ist wichtig, aber extreme Einschränkungen können Lesbarkeit und Wartbarkeit beeinträchtigen.
- Ein pauschales Verbot von Rekursion kann leicht unnötige Komplexität verursachen.
2. Alle Schleifen müssen eine klare obere Grenze haben
- Der Compiler sollte die Anzahl der Schleifendurchläufe statisch analysieren können.
- Doch allein das Setzen einer Obergrenze macht es schwer, die tatsächliche Laufzeit zu garantieren.
- Eine Begrenzung der Rekursionstiefe kann genauso sicher sein wie eine Obergrenze für Schleifen.
Kritik:
- Eine bloße Obergrenze garantiert noch keine realistisch ausführbare Laufzeit.
- Selbst mit einer Obergrenze ist ein zu großer Wert praktisch kaum von einer Endlosschleife zu unterscheiden.
3. Keine dynamische Speicherallokation nach der Initialisierung
- In eingebetteten Systemen ist Speicher begrenzt; das Ziel ist, Abstürze durch Speichermangel zu verhindern.
- Vorhersagbare dynamische Allokation kann jedoch sicherer sein als manuelles Speichermanagement.
- Zum Beispiel kann man mit einem Echtzeit-Garbage-Collector (RTGC) auch dynamische Allokation vorhersagbar machen.
Kritik:
- Statt dynamische Allokation grundsätzlich zu verbieten, kann es besser sein, Speichernutzungsmuster zu analysieren und so Sicherheit zu gewährleisten.
- Mit modernen statischen Analysetools (z. B. SPlint) lassen sich Fehler im Zusammenhang mit dynamischem Speicher vorab erkennen.
4. Die Größe einer Funktion auf höchstens eine A4-Seite begrenzen (ca. 60 Zeilen)
- Die Begründung ist, dass zu lange Funktionen die Lesbarkeit verschlechtern.
- In modernen Entwicklungsumgebungen gibt es jedoch Code-Folding, sodass die Größe logischer Einheiten wichtiger ist als die reine Funktionslänge.
Kritik:
- Maßgeblich sollte eher die logische Komplexität sein als die physische Größe (Zeilenzahl).
- Das Zerlegen von Funktionen in kleine Teile darf kein Selbstzweck werden → es kann die Wartung sogar erschweren.
5. Pro Funktion mindestens zwei assert-Anweisungen verwenden
assertist für Debugging und Dokumentation sehr nützlich.- Eine starre Mindestanzahl kann jedoch ineffizient sein.
Kritik:
- Wichtiger als die Anzahl der
assert-Anweisungen ist, klar festzulegen, an welchen Stellen Daten validiert werden müssen. - Es ist praxisnäher, alle Argumente und externen Eingaben zu prüfen.
6. Den Gültigkeitsbereich von Datenobjekten minimieren
- Ein gutes Prinzip, das die Verwendung lokaler Variablen empfiehlt.
- Allerdings sollte man nicht nur den Gültigkeitsbereich von Funktionen, sondern auch von Typen minimieren.
Kritik:
- In Ada, Pascal, JavaScript und funktionalen Sprachen können auch Typen und Funktionen lokal deklariert werden → ein besserer Ansatz als die NASA-Regeln.
7. Rückgabewerte von Funktionen und Gültigkeit von Parametern müssen geprüft werden
- Rückgabewerte müssen unbedingt überprüft werden.
- Allerdings ist es in der Praxis schwierig, wirklich alle Fälle abzudecken.
Kritik:
- Um Laufzeitfehler zu vermeiden, sind möglichst viele Prüfungen nötig, aber praktische Grenzen müssen berücksichtigt werden.
- Gerade in C ist die Prüfung von Rückgabewerten wichtig, während moderne Sprachen (Java, Rust usw.) durch ihr Typsystem sicherere Wege bieten.
8. Nutzung des Präprozessors einschränken (nur Header-Einbindungen und einfache Makros erlaubt)
- Komplexe Makros, Token-Verkettung und variadische Makros (
...) sind verboten. - Variadische Makros können jedoch als Debugging-Werkzeug nützlich sein.
Kritik:
- Sinnvoller als die Nutzung des Präprozessors zu beschränken ist es, gut lesbare Makro-Stile zu empfehlen.
- Wenn bedingte Kompilierung wie
#ifdefverhindert wird, kann das Schreiben plattformunabhängigen Codes erschwert werden.
9. Einsatz von Zeigern einschränken (keine Doppelzeiger, keine Funktionszeiger)
- Das Verbot von Funktionszeigern zielt auf hohe Stabilität ab.
- Funktionszeiger sind jedoch für Callbacks, das Strategy Pattern und Gerätetreiber unverzichtbar.
Kritik:
- Wenn die Funktionsauswahl ohne Funktionszeiger per
switch-caseerzwungen wird, leidet die Lesbarkeit des Codes und die Wartung wird schwieriger. - Bei der Entwicklung von Betriebssystemen, Netzwerk-Stacks und Treibern sind Funktionszeiger unverzichtbar.
- Bessere Lösungen als Pointer-Beschränkungen sind Verfahren, die eine sichere Nutzung von Zeigern gewährleisten (z. B. Smart Pointer in C++, Rust usw.).
10. Für den gesamten Code Compiler-Warnungen maximal aktivieren und statische Analysetools verwenden
- Diese Regel ist eine sehr gute Empfehlung.
- Compiler-Warnungen beseitigen + statische Analysetools einsetzen = höhere Stabilität.
Kritik:
- Andere NASA-Regeln (z. B. Pointer-Verbot, Begrenzung der Funktionsgröße) sollen teils einfach die Grenzen statischer Analysetools ausgleichen.
- Da moderne statische Analysetools jedoch stark fortgeschritten sind, ist es nützlicher, ausgefeiltere Analysetechniken zu nutzen als übermäßige Einschränkungen zu verhängen.
6 Kommentare
Wenn man alles aus der Perspektive von Echtzeit- und Embedded-Systemen betrachtet, sind das Regeln, die man versteht und die notwendig sind. Könnten statische Analysewerkzeuge diese Regeln ersetzen?
Könnte man zum Beispiel, wenn dynamische Allokation erlaubt ist, garantieren, dass die Speicherallokation in allen Nutzungsszenarien erfolgreich ist?
Wenn man Softwaretests lernt, gibt es Leitsätze, die immer am ersten Tag in der ersten Stunde erwähnt werden. Einer davon ist: „Perfektes Testen ist unmöglich.“
Daran, dass mir eher das Gegenteil ins Auge fällt,
merke ich wohl, dass diese Regeln nicht zu mir passen, haha
Es scheint, dass nicht nur bei der NASA, sondern auch in Branchen wie Luftfahrt und Automobil, in denen Menschenleben direkt auf dem Spiel stehen, oft ähnliche Coding-Regeln angewendet werden, haha.
https://github.com/kubernetes/kubernetes/…
Beim Kubernetes-Sourcecode musste ich an den Codeblock im „Space-Shuttle-Stil“ denken, von dem es heißt, er sei nach der Methode zum Schreiben des Sourcecodes für die NASA-Space-Shuttle-Anwendungen verfasst worden.
Zugehöriger HN-Thread: https://news.ycombinator.com/item?id=18772873
Hacker-News-Kommentar
setjmp/longjmpverteidigt, konnte ich von Anfang an kaum ernst nehmensetjmp/longjmpsei Exception Handlingsetjmp()undlongjmp()sind ein schlechter Weg, Exceptions zu behandeln> Der Titel muss darauf hinweisen, dass es sich um eine Kritik der Regeln handelt.
222