home / skills / athola / claude-night-market / safety-critical-patterns

safety-critical-patterns skill

/plugins/pensive/skills/safety-critical-patterns

This skill helps you apply safety-critical coding patterns to critical paths and reviews, improving robustness and verifiability in software projects.

npx playbooks add skill athola/claude-night-market --skill safety-critical-patterns

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
4.3 KB
---
name: safety-critical-patterns
description: 'Guidelines from the NASA Power of 10 rules for writing robust, verifiable
  code. Adapted for general software development with context-appropriate rigor. Use
  when writing critical code paths, reviewing for robustness, improving code quality.
  Do not use as rigid requirements for all code - match rigor to consequence.'
category: code-quality
tags:
- safety
- defensive-coding
- assertions
- NASA
- robustness
- verification
tools:
- Read
- Grep
- Glob
complexity: intermediate
estimated_tokens: 600
dependencies:
- pensive:shared
- pensive:code-refinement
---

# Safety-Critical Coding Patterns

Guidelines adapted from NASA's Power of 10 rules for safety-critical software.

## When to Apply

**Full rigor**: Safety-critical systems, financial transactions, data integrity code
**Selective application**: Business logic, API handlers, core algorithms
**Light touch**: Scripts, prototypes, non-critical utilities

> "Match rigor to consequence" - The real engineering principle

## The 10 Rules (Adapted)

### 1. Restrict Control Flow
Avoid `goto`, `setjmp/longjmp`, and **limit recursion**.

**Why**: Ensures acyclic call graphs that tools can verify.
**Adaptation**: Recursion acceptable with provable termination (tail recursion, bounded depth).

### 2. Fixed Loop Bounds
All loops should have verifiable upper bounds.

```python
# Good - bound is clear
for i in range(min(len(items), MAX_ITEMS)):
    process(item)

# Risky - unbounded
while not_done:  # When does this end?
    process_next()
```

**Adaptation**: Document expected bounds; add safety limits on potentially unbounded loops.

### 3. No Dynamic Memory After Initialization
Avoid heap allocation in critical paths after startup.

**Why**: Prevents allocation failures at runtime.
**Adaptation**: Pre-allocate pools; use object reuse patterns in hot paths.

### 4. Function Length ~60 Lines
Functions should fit on one screen/page.

**Why**: Cognitive limits on comprehension remain valid.
**Adaptation**: Flexible for declarative code; strict for complex logic.

### 5. Assertion Density
Include defensive assertions documenting expectations.

```python
def transfer_funds(from_acct, to_acct, amount):
    assert from_acct != to_acct, "Cannot transfer to same account"
    assert amount > 0, "Transfer amount must be positive"
    assert from_acct.balance >= amount, "Insufficient funds"
    # ... implementation
```

**Adaptation**: Focus on boundary conditions and invariants, not arbitrary quotas.

### 6. Minimal Variable Scope
Declare variables at narrowest possible scope.

```python
# Good - scoped tightly
for item in items:
    total = calculate(item)  # Only exists in loop
    results.append(total)

# Avoid - unnecessarily broad
total = 0  # Why is this outside?
for item in items:
    total = calculate(item)
    results.append(total)
```

### 7. Check Return Values and Parameters
Validate inputs; never ignore return values.

```python
# Good
result = parse_config(path)
if result is None:
    raise ConfigError(f"Failed to parse {path}")

# Bad
parse_config(path)  # Ignored return
```

### 8. Limited Preprocessor/Metaprogramming
Restrict macros, decorators, and code generation.

**Why**: Makes static analysis possible.
**Adaptation**: Document metaprogramming thoroughly; prefer explicit over magic.

### 9. Pointer/Reference Discipline
Limit indirection levels; be explicit about ownership.

**Adaptation**: Use type hints, avoid deep nesting of optionals, prefer immutable data.

### 10. Enable All Warnings
Compile/lint with strictest settings from day one.

```bash
# Python
ruff check --select=ALL
mypy --strict

# TypeScript
tsc --strict --noImplicitAny
```

## Rules That May Not Apply

| Rule | When to Relax |
|------|---------------|
| No recursion | Tree traversal, parser combinators with bounded depth |
| No dynamic memory | GC languages, short-lived processes |
| 60-line functions | Declarative configs, state machines |
| No function pointers | Callbacks, event handlers, strategies |

## Integration

Reference this skill from:
- `pensive:code-refinement` - Clean code dimension
- `pensive:code-refinement` - Quality checks
- `sanctum:pr-review` - Code quality phase

## Sources

- NASA JPL Power of 10 Rules (Gerard Holzmann, 2006)
- MISRA C Guidelines
- HN discussion insights on practical application

Overview

This skill packages adapted guidelines from the NASA Power of 10 rules into practical patterns for writing robust, verifiable code. It helps engineers apply rigorous practices where consequence demands them, while encouraging a pragmatic, context-sensitive approach. Use it to raise code safety, testability, and auditability in critical paths without overapplying controls to non-critical code.

How this skill works

The skill codifies ten core patterns — e.g., bounded loops, limited recursion, assertion density, minimal variable scope, and strict validation — and explains how to adapt each for modern languages like Python. It inspects code and design decisions mentally or via checklist during reviews, recommending concrete changes: add bounds, pre-allocate resources, tighten scopes, enable strict linters and type checks. The guidance is framed so you can match rigor to consequence: full rigor for safety-critical flows, selective rigor for core components, and light touch for prototypes.

When to use it

  • When implementing or reviewing safety-critical modules, financial transactions, or data-integrity paths
  • While hardening core algorithms, API handlers, or stateful services
  • During code reviews where traceability, verifiability, or static analysis are required
  • When writing tests or designing for formal verification or auditability
  • When tuning production hot paths that must avoid runtime allocation failures

Best practices

  • Apply full rules only where risk justifies the overhead — match rigor to consequence
  • Prefer explicit, bounded constructs: bounded loops, pre-allocated pools, and clear ownership
  • Keep functions short and focused; extract helpers to maintain comprehension and testability
  • Use defensive assertions for invariants and boundary conditions, not as control flow
  • Enable strict linting and type checking from day one; treat warnings as defects
  • Document any deliberate relaxation (e.g., recursion, GC usage) and its mitigation

Example use cases

  • Implementing a funds-transfer service: validate inputs, assert invariants, limit call graph complexity
  • Reviewing a real-time control loop: add explicit bounds and pre-allocated buffers to avoid allocations
  • Hardening an API gateway: narrow variable scope, check all return values, and enforce strict linters
  • Refactoring a complex function: split into <60-line units to improve reviewability and testing
  • Preparing code for formal review or certification: eliminate metaprogramming and document any exceptions

FAQ

Should I apply all ten rules everywhere?

No. Treat the rules as a risk-based toolbox: use full rigor for high-consequence code and relax selectively for prototypes or low-risk scripts, documenting exceptions.

How strict should linters and type systems be?

Start strict (treat warnings as defects) for critical code. You can relax rules where justified, but maintain a baseline of checks and record deviations.