Skip to main content

Fail Safe

Fail Safe designs systems so failures land in a deliberate, safe default state — fail-closed for security, fail-open where availability is the safe choice. The direction is domain-specific, trading availability against safety, and must be chosen and tested explicitly.

Type
Resilience
When to Use
Safety Critical, Security Defaults, Uncertain State, Default Deny

When a component fails, the question is not only whether it fails but into what state. The Fail Safe principle holds that failure should leave the system in a safe, predictable condition. A traffic light that loses control logic should flash red, not go dark; a security gate that loses power should deny access (fail-closed) or, where life safety dominates, open (fail-open) — whichever is safe for that context. In software, fail-safe means designing the failure mode deliberately rather than leaving it to chance.

How It Works

Fail-safe design starts by asking, for each failure, "what is the safe default?" and engineering the system to land there:

  • Security defaults (fail-closed): if an authorization service is unreachable, deny access rather than allow it. Default-deny firewalls and access control follow this rule.
  • Availability defaults (fail-open): a non-security middleware that cannot reach its backend may pass traffic through rather than block all users — acceptable only when the function is not a safety or security gate.
  • Safe default values: feature flags default to off, rate limits default to a conservative cap, and unknown states map to the least-harmful behavior.
  • Idempotent, recoverable operations: ensure that a crash mid-operation can be retried or rolled back without leaving corrupt state.

The defining act is choosing the failure direction on purpose, informed by what "safe" means for the domain.

When to Use It

Use fail-safe in safety-critical and security-sensitive systems: access control, payments, industrial control, medical devices, and anywhere an undefined failure state could cause harm or breach. The fail-open vs. fail-closed decision is domain-specific and should be made explicitly, documented, and tested.

Trade-offs

Fail-closed can hurt availability — a flaky auth service denying everyone is itself an outage. Fail-open can create security holes — a bypassed check during failure is an attack window. There is rarely a universally safe direction; the choice trades availability against safety/security and must be reasoned per component. Fail-safe also adds design effort, since every failure path needs a defined, tested outcome.

Related Patterns

Fail-safe contrasts and combines with fail-fast (halt early vs. continue safely), and relates to fallback-pattern (a safe alternative response), graceful-degradation (safe reduced functionality), and circuit-breaker (a controlled, safe failure mode).

Example

Authorization that fails closed:

allowed, err := authz.Check(user, resource)
if err != nil {
  return Deny   // unreachable authz => deny, the safe default
}
return allowed

If the authorization service is down, access is denied rather than granted by accident.