Regras da NASA para Programação Crítica (explicadas)

Ao contrário do que a mitologia sugere, as diretrizes da NASA para software crítico de segurança não apenas valorizam a simplicidade extrema; elas a tornam obrigatória. Uma disciplina férrea guia todo o trabalho dos programadores.

Membro feminio da equipe da Artemis II trabalha em seu laptop.
Engenheira da equipe da missão Artemis II em ensaio geral de lançamento no dia 2 de fevereiro – NASA/Kim Shiflett

Programar para uma espaçonave não é como programar um app qualquer. Cada bug pode custar milhões de dólares e anos de missão. Por isso, a NASA desenvolveu um conjunto de 10 regras chamadas Power of Ten, criadas por Gerard J. Holzmann no Jet Propulsion Laboratory (JPL) e publicadas na IEEE Computer em 2006.

Essas regras não buscam performance nem elegância, mas simplicidade, previsibilidade e verificabilidade, características essenciais em programas críticos.

O que são as regras Power of Ten?

Elas são diretrizes para escrever código tedioso, seguro e confiável:

  1. Use apenas um subconjunto seguro da linguagem
    Evite recursão, alocação dinâmica de memória, loops infinitos e fluxos complicados. Isso torna o código mais previsível e fácil de verificar.
  2. Todos os loops devem ter limites fixos
    Cada loop precisa ter um limite superior claro. Isso previne loops infinitos e garante tempo de execução previsível.
  3. Não use memória dinâmica
    Nada de malloc, free, new ou delete. Alocação dinâmica pode falhar ou fragmentar a memória, o que é inaceitável para sistemas espaciais.
  4. Funções curtas e simples
    Idealmente, menos de 60 linhas, com estado local mínimo e propósito único. Isso facilita leitura, testes e verificação.
  5. Controle de fluxo claro e visível
    Evite goto, aninhamento profundo e múltiplos pontos de saída. Fluxos simples reduzem chances de erros lógicos.
  6. Limite o uso de ponteiros
    Eles aumentam risco de referências inválidas e corrupção de memória. Prefira arrays e índices.
  7. Cheque todos os retornos de função
    Toda função que pode falhar deve ser verificada. Se um sensor ou leitura retornar erro, ele precisa ser tratado.
  8. Sem fluxos de dados ocultos
    Evite variáveis globais ou efeitos colaterais “surpresa”. Todas as dependências devem ser explícitas.
  9. Código sem avisos do compilador
    Nenhum aviso deve passar despercebido. Eles muitas vezes indicam problemas reais.
  10. Uso mínimo do pré-processador
    Evite macros complexas e compilação condicional. Prefira constantes simples e estruturas claras.

Por que essas regras funcionam

Software de espaçonaves precisa ser previsível, verificável, robusto e seguro. A ideia da Power of Ten é domar a linguagem C/C++ para que ela se torne uma mini-linguagem interna, onde qualquer programador pode entender e revisar o código, nas piores condiçoes, mesmo anos após o lançamento. O conceito-chave é controlar a complexidade para tornar o software quase matematicamente verificável. A questão não é sobre elegância, esperteza ou performance, mas sobre segurança e confiabilidade.

Filosofia da NASA

  • Código simples vence código esperto: a melhor prática em sistemas críticos é a óbvia, linear e transparente.
  • Programadores humildes prosperam: nada de “rock-star devs”; o trabalho é feito sobre métodos consistentes e previsíveis.
  • Alta habilidade é redirecionada: para arquitetura, simulação, testes e análise de segurança, não truques ou abstrações complexas.

Exemplos práticos

1. Loop seguro e limitado

int sum_array(const int *data, int length)
{
int sum = 0;
if (!data || length <= 0) return 0;
for (int i = 0; i < length; i++) sum += data[i];
return sum;
}
  • Sem aritmética de ponteiros
  • Sem memória dinâmica
  • Limites fixos, fácil de verificar

2. Máquina de estados clara

typedef enum {IDLE, RUNNING, ERROR} State;
State update_state(int sensor_ok)
{
switch(sensor_ok)
{
case 1: return RUNNING;
case 0: return ERROR;
default: return ERROR;
}
}
  • Transições explícitas
  • Sem “fall-through”
  • Fácil de testar e auditar

3. Checagem de retorno

int safe_read_sensor(int *out)
{
if (!out) return -1;
int rc = read_sensor_hardware(out);
if (rc != 0) { *out = 0; return -1; }
return 0;
}
  • Nenhum erro é ignorado
  • Comportamento definido em falhas

4. Buffer estático seguro

#define LOG_SIZE 256
static char log_buffer[LOG_SIZE];
static int log_index = 0;
void log_message(const char *msg)
{
if (!msg) return;
int i = 0;
while(msg[i] != '\0' && log_index < LOG_SIZE - 1)
log_buffer[log_index++] = msg[i++];
log_buffer[log_index] = '\0';
}
  • Sem heap
  • Escritas limitadas
  • Terminador nulo explícito

5. Aritmética segura

int safe_multiply(int a, int b, int *res)
{
if (!res) return -1;
long long temp = (long long)a * (long long)b;
if(temp > INT_MAX || temp < INT_MIN){ *res=0; return -1; }
*res = (int)temp; return 0;
}
  • Detecta overflow
  • Evita comportamento indefinido
  • Comprovável mecanicamente

Enfim

O estilo Power of Ten não é elegante, não é divertido e não é esperto; é seguro, previsível e auditável. Em sistemas críticos como espaçonaves, o código não precisa ser impressionante; precisa funcionar sempre e ser compreensível mesmo sob pressão extrema.

Se você programar seguindo essas regras, mesmo como um desenvolvedor “humilde”, você terá o mesmo impacto que os “rock stars” do código, mas com robustez garantida.

Deixe um comentário