NASA Power of 10 — это 10 строгих правил кодирования, разработанных в 2006 году Gerard Holzmann из NASA/JPL для safety-critical C-кода в космических миссиях. Они упрощают статический анализ, устраняют сложные конструкции и повышают надёжность, минимизируя ошибки в системах, где сбой стоит жизни. В TypeScript правила адаптируются через типы, ESLint/TSLint и инструменты вроде SonarQube, сохраняя фокус на предсказуемости и верифицируемости.[1][2][3][4][5]

Правило 1: Простой контроль потока

Запрещены goto, setjmp/longjmp, рекурсия (прямая/косвенная). Цель — ациклический граф вызовов для лёгкого анализа.[5]

Плохо (рекурсия):

function factorial(n: number): number {
  return n <= 1 ? 1 : n * factorial(n - 1); // Рекурсия → запрет
}

Хорошо (итерация):

function factorial(n: number): number {
  let result = 1;
  for (let i = 2; i <= n; i++) { // Фиксированный bound
    result *= i;
  }
  return result;
}

Правило 2: Фиксированные границы циклов

Все циклы с compile-time verifiable верхней границей; инструменты (ESLint) должны статически доказать отсутствие бесконечности

Плохо (нефиксированный while):

function processArray(arr: number[]): number {
  let i = 0;
  while (i < arr.length) { // Длина runtime → нарушение
    arr[i++] *= 2;
  }
  return arr.length;
}

Хорошо (for с const max):

const MAX_ARRAY_SIZE = 1000;
function processArray(arr: number[]): number | Error {
  if (arr.length > MAX_ARRAY_SIZE) return new Error('Array too large');
  for (let i = 0; i < Math.min(arr.length, MAX_ARRAY_SIZE); i++) {
    arr[i] *= 2;
  }
  return arr.length;
}

Правило 3: Без динамического выделения после init

Запрет new после инициализации; использовать фиксированные массивы или пулы объектов для предсказуемой памяти (в TS — избегать runtime new)

Плохо:

function createCache(size: number): Map<string, string> {
  return new Map(); // Динамическое выделение
}

Хорошо (фиксированный пул):

interface CacheEntry { key: string; value: string; used: boolean; }
const CACHE_POOL: CacheEntry[] = Array(100).fill({ key: '', value: '', used: false });
 
function getCacheEntry(key: string): CacheEntry | null {
  for (let i = 0; i < CACHE_POOL.length; i++) {
    if (!CACHE_POOL[i].used) {
      CACHE_POOL[i].key = key;
      CACHE_POOL[i].used = true;
      return CACHE_POOL[i];
    }
  }
  return null;
}

Правило 4: Функции ≤60 строк

Одна логическая единица на лист бумаги; разбивать на мелкие функции

TS-инструмент для проверки (из ):

function measureFunctionSize(fn: Function): number {
  const lines = fn.toString().split('\n')
    .filter(line => line.trim().length > 0 && !line.trim().startsWith('//'));
  return lines.length; // Должно быть ≤60
}

Правило 5: ≥2 ассершена на функцию

Ассершены (assert) для pre/post-условий, без side-effects; при фейле — recovery (return Error).[5]

function divide(a: number, b: number): number | Error {
  assert(a >= 0 && b > 0, 'Invalid inputs'); // Assertion 1: pre-condition
  const result = a / b;
  assert(Number.isFinite(result), 'Division overflow'); // Assertion 2: post-condition
  return result;
}
 
function assert(condition: boolean, message: string): asserts condition {
  if (!condition) throw new Error(message);
}

Правило 6: Минимальный scope переменных

Объявлять в самом узком scope; в TS — let/const внутри блоков.[5]

Плохо:

function process(items: number[]) {
  let sum = 0; // Широкий scope
  for (let i = 0; i < items.length; i++) {
    sum += items[i]; // Переиспользование
  }
  // sum используется дальше?
}

Хорошо:

function process(items: number[]) {
  let total = 0;
  for (let i = 0; i < items.length; i++) {
    const item = items[i]; // Локальный scope
    total += item;
  }
  return total;
}

Правило 7: Проверка возвратов и параметров

Все non-void функции проверять; параметры валидировать в callee

function safeParseInt(str: string): number | Error {
  assert(typeof str === 'string' && str.length > 0, 'Invalid param');
  const num = parseInt(str, 10);
  if (isNaN(num)) return new Error('Parse failed');
  return num;
}
 
function useIt(str: string) {
  const result = safeParseInt(str); // Обязательная проверка
  if (result instanceof Error) return; // Recovery
  console.log(result * 2);
}

Правило 8: Ограниченный препроцессор

Только include и простые макросы; без token pasting, ellipses, рекурсии.

В TS — избегать сложных const enum

Хорошо (простые const):

const MAX_ITER = 1000;
const ERROR_CODE = 1;

Правило 9: Ограниченные указатели

≤1 уровень dereference; без function pointers. В TS — избегать any[], предпочитать typed arrays

Плохо:

type Nested = { data: { value: number }[] };
 
function bad(nested: Nested) {
  return nested.data[0].value; // Двойной deref
}

Хорошо:

interface FlatData { value: number; }
 
function good(data: FlatData[]) {
  if (data.length > 0) return data[0].value; // Один уровень
  return 0;
}

Правило 10: Полная компиляция с warnings

Компиляция с max pedantic warnings (tsc —strict); ежедневный static analysis (ESLint, SonarTS) с 0 warnings.

tsconfig.json

{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "noUnusedLocals": true
  }
}

Инструменты: ESLint + typescript-eslint для автоматизации

Зачем и как применять

Правила снижают дефекты на 10-100x за счёт верифицируемости, используются в JPL для миссий вроде Toyota throttle (243 violations нашли). В TS внедрять через ESLint plugins, CI checks, code reviews; профит — в предсказуемом коде для high-reliability apps (IoT, finance).