Education MIT

C-From-Scratch

Learn to build safety-critical systems in C — mathematical rigour, not 'Hello World'

GitHub Repository
Published
January 17, 2026 09:00
Reading Time
5 min
The Safety Stack: seven modules building from simple monitors to complete safety-critical systems

Most programming courses start with “Hello World” and work up to toy applications. They teach syntax but not thinking. They produce programmers who can write code that compiles, but not code that can be trusted when lives depend on it.

C-From-Scratch takes the opposite approach. We start with the question: what would it take to build software for a pacemaker? Then we work backwards to the fundamentals.

The Methodology

Core Principle

"Sensors report. The Captain decides."

Every module follows the same pattern:

Problem → Math Model → Proof → Structs → Code → Verification
   │          │          │         │        │          │
   │          │          │         │        │          └── Does code match math?
   │          │          │         │        └── Direct transcription
   │          │          │         └── Every field justified
   │          │          └── Contracts proven
   │          └── State machine defined
   └── Failure modes analysed

This is the “Math → Structs → Code” methodology. We don’t write code until we can prove properties about our design. The code is a transcription of the math, not an invention.

The Safety Stack

Seven modules build from simple monitors to a complete safety-critical system:

7
Modules
42
Lessons
98
Tests
MIT
License

The Sensors (Modules 1-4)

Module 1: Pulse — Is the signal alive?

The simplest possible monitor. A signal either arrives within a timeout or it doesn’t. Two states, deterministic transitions, provable properties. This is the foundation everything else builds on.

Module 2: Baseline — Is the signal normal?

Exponential moving averages with O(1) memory. Statistical anomaly detection without storing history. We prove bounds on memory usage and numerical stability.

Module 3: Timing — Is the rhythm healthy?

Interval analysis and jitter detection. When a heartbeat should arrive every 800ms ± 50ms, we need to detect when intervals drift outside tolerance.

Module 4: Drift — Trending toward failure?

Long-term trend detection. A sensor might be “normal” right now but drifting toward failure. Linear regression with bounded memory and deterministic computation.

The Decision Layer (Modules 5-7)

Module 5: Consensus — Which sensor to trust?

Triple Modular Redundancy (TMR) voting. When three sensors disagree, how do you decide which one is right? Byzantine fault tolerance in 200 lines of C.

Module 6: Pressure — Handle overflow

Bounded queues with backpressure. When events arrive faster than you can process them, what do you drop? We prove the queue never overflows and never loses critical events.

Module 7: Mode — The Captain

System mode management with permission matrices. Some actions are only safe in certain modes. Transitions between modes must be controlled. This is where everything comes together.

The Composition

Sensors (1-4) → Judge (5) → Buffer (6) → Captain (7)

Each module is independently verified. The composition preserves the properties of the components. This is how you build complex systems you can trust.

What You’ll Learn

This isn’t a syntax course. You’ll learn:

  • Failure mode analysis — Enumerate what can go wrong before writing code
  • State machine design — Define all states and all transitions explicitly
  • Property proofs — Prove bounds, invariants, and safety properties
  • Data structure justification — Every field serves a proven purpose
  • TMR voting — Implement Byzantine fault tolerance
  • Backpressure handling — Manage overflow without losing critical data
  • Mode management — Control system behaviour through state transitions
  • Compositional verification — Build complex systems from verified components

Example: The Pulse Monitor

Here’s what a lesson looks like. The Pulse monitor answers one question: is the signal alive?

The Math

State: { ALIVE, DEAD }
Input: { tick, heartbeat }
Transition:
  ALIVE + tick → if (ticks_since_heartbeat > TIMEOUT) then DEAD else ALIVE
  ALIVE + heartbeat → ALIVE (reset counter)
  DEAD + heartbeat → ALIVE
  DEAD + tick → DEAD

The Proof

Property: “If heartbeats arrive faster than TIMEOUT, the system stays ALIVE.”

Proof: Each heartbeat resets the counter. If heartbeats arrive every T ticks where T < TIMEOUT, the counter never exceeds T < TIMEOUT, so the transition to DEAD never fires. ∎

The Struct

typedef struct {
    pulse_state_t state;           // Current state: ALIVE or DEAD
    uint32_t ticks_since_heartbeat; // Counter, bounded by TIMEOUT
    uint32_t timeout;              // Configuration, immutable after init
} pulse_monitor_t;

Every field is justified by the math. No “just in case” fields. No accumulated cruft.

The Code

pulse_state_t pulse_tick(pulse_monitor_t *pm) {
    if (pm->state == PULSE_ALIVE) {
        pm->ticks_since_heartbeat++;
        if (pm->ticks_since_heartbeat > pm->timeout) {
            pm->state = PULSE_DEAD;
        }
    }
    return pm->state;
}

pulse_state_t pulse_heartbeat(pulse_monitor_t *pm) {
    pm->ticks_since_heartbeat = 0;
    pm->state = PULSE_ALIVE;
    return pm->state;
}

The code is a direct transcription of the state machine. No creativity required — creativity in safety-critical code is a bug.

The Test

void test_timeout_triggers_dead(void) {
    pulse_monitor_t pm;
    pulse_init(&pm, 3);  // Timeout after 3 ticks
    
    assert(pulse_tick(&pm) == PULSE_ALIVE);  // tick 1
    assert(pulse_tick(&pm) == PULSE_ALIVE);  // tick 2
    assert(pulse_tick(&pm) == PULSE_ALIVE);  // tick 3
    assert(pulse_tick(&pm) == PULSE_DEAD);   // tick 4 - timeout!
}

The test verifies the proof. If the test passes, the code matches the math.

Why This Approach Works

Traditional programming education teaches you to write code that works. This approach teaches you to write code that cannot fail — because failure modes are eliminated by design, not by testing.

Testing can only show the presence of bugs, not their absence. Proofs show absence. When you prove a property about your state machine, it holds for all possible inputs, not just the ones you thought to test.

This is how aerospace software is written. This is how medical device software is written. And now it’s how you can learn to write software.

Getting Started

git clone https://github.com/williamofai/c-from-scratch
cd c-from-scratch
make
make test

Start with Module 1 (Pulse). Each module builds on the previous. By Module 7, you’ll have built a complete safety-critical monitoring system.

Community Response

The course has resonated with engineers who want more than syntax:

“Finally, a C course that treats the language seriously. The math-first approach changed how I think about all my code.”

“I’ve been writing C for 15 years and I learned more about safety-critical design in Module 5 than in my entire career.”

The repository has seen significant engagement on GitHub, and the LinkedIn posts about the methodology have reached over 66,000 impressions. Turns out there’s demand for rigour.


Prove first, code second. MIT licensed.

About the Author

William Murray is a Regenerative Systems Architect with 30 years of UNIX infrastructure experience, specializing in deterministic computing for safety-critical systems. Based in the Scottish Highlands, he operates SpeyTech and maintains several open-source projects including C-Sentinel and c-from-scratch.

Questions or Contributions?

Open an issue on GitHub or get in touch directly.

View on GitHub Contact
← Back to Open Source