← Back to articles

SOFTWARE TESTING · V&V · SAFETY

20 min read

Why we test: when one software assumption destroys a system.

A technical reconstruction of Ariane 5 Flight 501, and what it teaches about requirements, reuse, numeric ranges, exception handling, representative testing and system-level verification.

Technical illustration of a launch vehicle, inertial guidance data path and software failure chain

Software defects become dangerous when software is connected to a physical system. A wrong conversion, an invalid state estimate or an unchecked assumption can move actuators, shut down equipment or feed nonsense into a controller. That is why serious testing is not about proving that code can execute. It is about discovering whether the complete system behaves correctly under the conditions it will actually experience.

The key lesson: Ariane 5 did not fail because nobody had tested anything. It failed because important assumptions survived the development process and the test environment was not representative enough to expose them.

1. The event: 37 seconds of normal flight

On 4 June 1996, the first Ariane 5 launch initially followed its planned trajectory. Roughly 37 seconds after main-engine ignition, both inertial reference systems stopped providing valid guidance information. The flight-control computer received diagnostic data where attitude data was expected, commanded extreme nozzle deflections, and the launcher rapidly departed its intended path. Structural loads increased until the vehicle broke up and its onboard destruction system terminated the flight.

T+0 ignition / lift-offT+36 s numeric conversion failsT+37 s inertial guidance lostT+39 s extreme steering commandsFinal breakup / destruction

2. The software defect was a numeric-range problem

A reused inertial-reference function converted a 64-bit floating-point horizontal-velocity-related value into a 16-bit signed integer. Under Ariane 4 flight dynamics, the value stayed inside the assumed range. Ariane 5 had a different early-flight trajectory and the value exceeded the representable range.

Signed 16-bit range−32,768 ≤ n ≤ 32,767

When the conversion overflowed, the software raised an exception. The exception was not handled in a way that allowed the inertial system to continue producing safe guidance data; instead the unit shut down. The backup inertial unit ran identical software and failed for the same reason almost immediately. Redundancy existed in hardware, but not in failure diversity.

// simplified illustration — not Ariane source code
int16_t convert(double value) {
    // hidden assumption: value is always representable
    return (int16_t)value;
}

// safer engineering intent
if (value < INT16_MIN || value > INT16_MAX) {
    raise_range_fault();
    enter_defined_safe_behavior();
}

3. Reuse transferred code — and transferred assumptions

The inertial-reference software came from Ariane 4. Reuse can reduce cost and risk when the environment is truly equivalent, but reuse is not evidence of validity. The old code contained a function related to alignment before launch. Ariane 5 continued executing that function after lift-off even though it was no longer needed during flight.

Reuse questionWhat must be re-verified
Same input ranges?maximum/minimum values, rates, timing and units
Same operating modes?startup, flight, degraded and transition states
Same failure semantics?what happens on overflow, timeout, invalid data or reset
Same interfaces?message format, meaning, timing and consumers
Same environment?dynamics, loads, hardware and mission profile

A component can be perfectly verified against yesterday’s assumptions and still be invalid in today’s system.

4. Why unit testing alone would not have been enough

A unit test can prove that a conversion throws an exception when fed an out-of-range value. But the real engineering questions are larger: could Ariane 5 produce that value? What happens to the inertial system after the exception? What does the flight-control computer do when both inertial units fail? Can diagnostic words be misinterpreted as flight data?

Unitconversion, bounds, exception branch
Componentinertial-reference behavior under real trajectories
Integrationguidance interfaces and invalid-data propagation
Systemcomplete launcher simulation with representative flight dynamics

5. Representative test data is not optional

The investigation concluded that the development programme had extensive reviews and tests, but the inertial reference system and complete flight-control system were not tested with sufficiently representative Ariane 5 conditions. This distinction matters. High test volume does not compensate for a weak test basis.

Coverage is multidimensional. Code coverage asks which instructions executed. Requirement coverage asks which obligations were verified. Scenario coverage asks which operating situations were exercised. Range coverage asks whether critical numeric domains and boundaries were explored.

For a numeric conversion, a useful test design would include nominal values, exact limits, one-step-outside limits, physically reachable extremes, transients and model-generated trajectories.

boundary_cases = [
    INT16_MIN,
    INT16_MIN - 1,
    -1,
    0,
    INT16_MAX,
    INT16_MAX + 1,
    worst_case_trajectory_value,
]

for x in boundary_cases:
    assert_defined_behavior(convert_or_reject(x))

6. Two identical backups can fail identically

The system had two inertial reference units. This protects well against some random hardware failures. It does not protect against a deterministic software defect present in both units. Common-cause failure is a systems problem: redundancy only helps when the duplicated channels do not share the same vulnerability.

Random hardware faultredundancy can provide tolerance
Common software defectboth channels can fail on the same input

For critical systems, engineers therefore analyse independence, diversity, fault containment and failure propagation—not merely component count.

7. Exception handling is part of the safety architecture

An exception handler is not just a programming detail. In embedded and safety-related systems it determines what the physical system does when assumptions fail. Options include rejecting the data, saturating a value, keeping the last trusted state, entering a degraded mode, switching channels or initiating a controlled shutdown. The correct strategy depends on hazards and system dynamics.

Failure responseEngineering risk
Crash / stopmay be safe for an offline tool, catastrophic for active guidance
Ignore exceptioncan propagate invalid state silently
Saturate valueprevents overflow but may create biased state estimates
Use last valid valuecan support graceful degradation for a limited time
Switch mode/channelrequires validated transition logic and healthy alternative

8. Verification should attack assumptions

A requirement says what the system shall do. An assumption says what engineers believe will always be true. Unwritten assumptions are dangerous because they rarely receive explicit verification. A strong review asks: what must remain true for this design to work, and what evidence proves that each condition is guaranteed?

  • Numeric ranges and units are explicit.
  • Every conversion has defined behavior at and beyond its limits.
  • Unused legacy functions are removed or re-justified.
  • Failure modes are exercised, not merely documented.
  • Backup channels are analysed for common-cause failure.
  • System simulations use representative mission trajectories.
  • Interface consumers reject diagnostic or invalid data correctly.

9. Testing is evidence for a decision, not a ritual

The lesson from Ariane 5 is not “test more.” It is “test the right claims at the right level with the right environment.” A thousand passing unit tests cannot prove a system-level assumption that was never encoded in the test basis.

RequirementAssumptionRiskTest conditionEvidenceRelease decision

10. What this means for everyday engineering

Most of us are not launching rockets, but the pattern repeats in smaller systems: an API field overflows, an unsigned value wraps, a timeout is interpreted as zero, a reused module receives a new range, or two redundant services share the same configuration defect. The consequences may be less spectacular, but the engineering logic is identical.

Good testing turns assumptions into executable evidence. Good verification makes requirements traceable to that evidence. Good validation asks whether the complete system remains suitable in its real operating context. When software touches the physical world, those disciplines are not paperwork around engineering—they are part of engineering itself.