36 puntos por GN⁺ 2025-02-17 | 6 comentarios | Compartir por WhatsApp
  • Análisis crítico de las 10 reglas de la NASA para el desarrollo de software
    • Estas reglas están pensadas para sistemas embebidos extremadamente críticos (por ejemplo, software de naves espaciales)
    • Pero hace falta debatir si estas reglas también son adecuadas en otros entornos de desarrollo, o si pueden aplicarse a otros lenguajes (distintos de C)

1. Mantener un flujo de control simple (goto, setjmp/longjmp, recursión prohibidos)

  • Esta regla prohíbe el manejo de excepciones (setjmp()/longjmp()) y la recursión.
  • La recursión no es necesariamente ineficiente. Si se usa de forma adecuada, también puede garantizarse su terminación.
  • Forzar la conversión de la recursión a bucles puede generar código difícil de mantener.

Crítica:

  • Garantizar la terminación es importante, pero restricciones extremas pueden perjudicar la legibilidad y el mantenimiento.
  • Una prohibición incondicional de la recursión tiene muchas probabilidades de introducir complejidad innecesaria.

2. Todos los bucles deben tener un límite superior claro

  • El compilador debe poder analizar estáticamente cuántas veces se repetirá el bucle.
  • Sin embargo, establecer un límite superior por sí solo no basta para garantizar el tiempo real de ejecución.
  • Imponer un límite a la profundidad de recursión puede ser tan seguro como poner un límite superior a un bucle.

Crítica:

  • Tener un límite superior por sí solo no garantiza un tiempo de ejecución realista.
  • Aunque se fije un límite, si el valor es demasiado grande, en la práctica no es muy distinto de un bucle infinito.

3. Prohibir la asignación dinámica de memoria después de la inicialización

  • En sistemas embebidos, la memoria es limitada, así que el objetivo es evitar fallos por falta de memoria.
  • Pero una asignación dinámica predecible puede ser más segura que la gestión manual de memoria.
  • Por ejemplo, si se usa un recolector de basura en tiempo real (RTGC), la asignación dinámica también puede volverse predecible.

Crítica:

  • En vez de prohibir la asignación dinámica en sí, puede ser mejor analizar los patrones de uso de memoria para garantizar la seguridad.
  • Si se aprovechan herramientas modernas de análisis estático (como SPlint), es posible detectar por adelantado errores relacionados con memoria dinámica.

4. Limitar el tamaño de las funciones a una hoja A4 (aprox. 60 líneas)

  • La lógica es que, si una función es demasiado larga, la legibilidad empeora.
  • Pero en entornos de desarrollo modernos existen funciones de plegado de código, así que el tamaño de la unidad lógica importa más que la longitud de la función.

Crítica:

  • Más que el tamaño físico (cantidad de líneas), debería tomarse como criterio la complejidad lógica.
  • Dividir funciones en partes más pequeñas no debe convertirse en un objetivo en sí mismo → incluso puede dificultar el mantenimiento.

5. Usar al menos dos sentencias assert por función

  • assert es muy útil para depuración y documentación.
  • Pero un requisito incondicional de cantidad puede resultar ineficiente.

Crítica:

  • Más importante que la cantidad de assert es dejar claro en qué puntos hace falta validar los datos.
  • Validar todos los argumentos y entradas externas es más práctico.

6. Minimizar el alcance de los objetos de datos

  • Es un buen principio que recomienda usar variables locales.
  • Pero no solo debe minimizarse el alcance de las funciones, sino también el de los tipos y las propias funciones.

Crítica:

  • En Ada, Pascal, JavaScript y lenguajes funcionales, los tipos y las funciones también pueden declararse localmente → es un enfoque mejor que las reglas de la NASA.

7. Verificación obligatoria de valores de retorno y validez de parámetros

  • Los valores de retorno siempre deben revisarse.
  • Pero en la práctica es difícil comprobar absolutamente todos los casos.

Crítica:

  • Para evitar errores de ejecución, hacen falta tantas comprobaciones como sea posible, pero también hay que considerar los límites prácticos.
  • Especialmente en C es importante revisar los valores de retorno, pero en lenguajes modernos (Java, Rust, etc.) puede manejarse de forma más segura aprovechando el sistema de tipos.

8. Restringir el uso del preprocesador (solo se permiten inclusiones de encabezados y macros simples)

  • Se prohíben macros complejas, concatenación de tokens y macros variádicas (...).
  • Pero las macros variádicas pueden ser útiles como herramienta de depuración.

Crítica:

  • Más que restringir el uso del preprocesador, conviene promover un estilo de macros legible.
  • Si se bloquea la compilación condicional como #ifdef, puede volverse difícil escribir código independiente de la plataforma.

9. Restringir el uso de punteros (prohibidos los punteros dobles y los punteros a función)

  • Se prohíbe el uso de punteros a función → el objetivo es lograr una estabilidad alta.
  • Pero los punteros a función son indispensables para callbacks, el patrón estrategia y los controladores de dispositivos.

Crítica:

  • Si se obliga a seleccionar funciones con switch-case en lugar de usar punteros a función, la legibilidad del código empeora y el mantenimiento se vuelve más difícil.
  • En el desarrollo de sistemas operativos, stacks de red y controladores, los punteros a función son indispensables.
  • Más que restringir los punteros, una mejor solución es garantizar su uso seguro mediante métodos como punteros inteligentes de C++ o Rust.

10. Configurar al máximo las advertencias del compilador para todo el código y usar herramientas de análisis estático

  • Esta regla es una recomendación muy buena.
  • Eliminar advertencias del compilador + usar herramientas de análisis estático = mayor estabilidad.

Crítica:

  • Otras reglas de la NASA (por ejemplo, prohibir punteros o limitar el tamaño de las funciones) buscan simplemente compensar las limitaciones de las herramientas de análisis estático.
  • Pero las herramientas modernas de análisis estático han avanzado muchísimo, así que en vez de imponer restricciones excesivas, resulta más útil aprovechar técnicas de análisis más sofisticadas.

6 comentarios

 
regentag 2025-02-18

Parece que todas son reglas comprensibles y necesarias si se ven desde una perspectiva de tiempo real y sistemas embebidos. ¿Un analizador estático podría reemplazar estas reglas?

Por ejemplo, si se permitiera la asignación dinámica, ¿se podría garantizar que la asignación de memoria tendrá éxito en todos los escenarios de uso?

Cuando estudias pruebas de software, siempre hay proposiciones que se mencionan en la primera hora del primer día. Una de ellas es que "las pruebas perfectas son imposibles".

 
smboy86 2025-02-18

Como lo que más me llama la atención es lo contrario,
parece que son reglas que no van conmigo jaja

 
rtyu1120 2025-02-17

Parece que no solo la NASA, sino también industrias como la aeronáutica y la automotriz, donde la vida está directamente en juego, suelen aplicar reglas de programación similares jaja

 
ssssut 2025-02-17

https://github.com/kubernetes/kubernetes/…
Me recordó al bloque de código de estilo "space shuttle style" del código fuente de Kubernetes, que supuestamente fue escrito siguiendo la forma de redactar código fuente de la aplicación del transbordador espacial de la NASA.
HN Thread relacionado: https://news.ycombinator.com/item?id=18772873

 
GN⁺ 2025-02-17
Opinión de Hacker News
  • Al leer el original, se explica el propósito de cada punto
  • El original está dirigido principalmente al lenguaje C y busca optimizar para poder verificar con mayor rigor la confiabilidad de aplicaciones importantes escritas en C
  • El autor original entiende claramente lo que hace y explica varios métodos para verificar código C
  • La lógica del original se entiende perfectamente
    • Probablemente porque aprendí C en sistemas pequeños
    • Aprendí C para hardware de dispositivos médicos implantables, y en el laboratorio también seguíamos lineamientos similares
  • El último párrafo es excelente
    • Las reglas pueden sentirse estrictas al principio, pero hay que considerar los casos en los que la vida puede depender de la corrección del código
    • Como el cinturón de seguridad en un auto, puede resultar incómodo al principio, pero con el tiempo se vuelve natural usarlo
  • Mi crítica a estas reglas sería muy distinta a la del OP
    • Desde el inicio me costó tomar en serio una defensa de setjmp/longjmp
    • Este patrón tiene problemas evidentes para cualquiera que lo haya intentado usar
    • El texto afirma que setjmp/longjmp es manejo de excepciones
    • Afirma que el manejo de excepciones es bueno
    • Hay un problema serio con la segunda premisa
  • Significa que se debe establecer un número máximo de iteraciones para los bucles
    • 10^90 es una tontería y no es relevante
    • Dejé de leer el texto a partir de ese punto
  • Si fuera a criticar las reglas, me enfocaría en cosas como estas
    • La longitud del cuerpo de una función no se correlaciona con la simplicidad de comprensión, e incluso puede ser lo contrario de lo que la regla sugiere
    • 2 aserciones es completamente arbitrario, y se debería afirmar todo lo que se pueda afirmar
    • Quienes usan Ada, Pascal (Delphi), JavaScript o lenguajes funcionales deberían declarar tipos y funciones de la manera más local posible
  • Mi enfoque personal en JavaScript es no definir funciones de forma anidada, salvo cuando quiero capturar valores de manera explícita
    • Puede ser por un modelo mental antiguo
    • En perfiles de rendimiento se mostraba que se redefinían cada vez que se llamaba a la función
    • No creo que los intérpretes modernos de JavaScript funcionen así
    • Seguramente se han hecho optimizaciones profundas desde la introducción de las funciones flecha
    • Los hábitos viejos no desaparecen fácilmente
    • Las funciones con nombre que no capturan variables locales las mantengo en el ámbito del archivo/módulo
  • Muchas otras notas son interesantes y muy detallistas
    • A los ingenieros de la vieja escuela les gusta ese estilo de "lo técnicamente correcto es la mejor clase de correcto"
    • Creo que el tono general de prudencia que las reglas de NASA intentan transmitir es muy bueno, y apoyo la mayor parte
  • En contexto, estas son más bien prácticas sugeridas que "reglas"
    • Las "reglas" formales están en documentos con nombres como "NPR"
    • Los desarrolladores no tienen la obligación de cumplir ni ignorar estas "reglas"
  • GCC puede obtener el uso de pila y las relaciones llamador-invocado después de compilar
    • setjmp() y longjmp() son una mala forma de manejar excepciones
    • El código de limpieza no se ejecuta
    • Si se sigue el espíritu de la regla, no debería haber recursos que requieran limpieza
  • El problema principal aparece de forma distinta en cada aplicación
    • Qué hacer cuando se excede el límite de iteraciones o cuando los recursos fijos asignados al inicio no son suficientes
  • Hoy en día los programadores leen el código en pantalla, así que no está claro por qué el tamaño del papel sigue siendo relevante
    • Había una repetición sobre la página estándar y el tamaño de letra
    • Se debe no solo a las limitaciones del papel, sino también a las limitaciones humanas
  • La regla sobre recursión busca garantizar un límite conocido estáticamente para el espacio de pila necesario
    • La crítica de que depende del compilador es válida, pero es una condición previa para derivar un límite superior en tiempo de ejecución
    • En sistemas de seguridad crítica se necesitan tiempos de respuesta garantizados
  • El título debería indicar que es una crítica de las reglas
  • Se recomienda el uso estricto de tipos
    • Uso estricto de tipos para todos los tipos escalares
    • No mezclar unidades imperiales con unidades métricas
 
roxie 2025-02-21

El título debe indicar que es una crítica a las reglas

222