> ## Documentation Index
> Fetch the complete documentation index at: https://meta.niceshare.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 快速失败原则

> 快速失败原则倡导在开发早期检测并报告错误。了解其起源、优势以及如何有效应用。

<Info>
  **类别**: 原则<br />
  **类型**: 软件开发原则<br />
  **起源**: 软件工程，1970年代 / 《程序员 pragmatics》，1999<br />
  **别名**: 快速失败、早失败检测
</Info>

<Note>
  **快速回答** — 快速失败原则指出，系统应在检测到错误时立即报告，而不是尝试用无效状态继续执行。该原则在安迪·亨特（Andy Hunt）和戴夫·托马斯（Dave Thomas）1999年出版的《程序员 pragmatics》中推广，已成为构建健壮软件的基础理念。其核心思想是：立即失败使调试更容易，防止级联失败，并降低修复缺陷的成本。
</Note>

## 什么是快速失败原则？

快速失败原则是一种软件开发哲学，主张在出现问题时立即失败，而不是尝试用损坏或无效的状态继续执行。其背后的原理是务实的：通过快速、明确地失败，开发人员可以在最早的时机识别并修复问题，此时上下文最清晰，修正成本也最低。

> "当你遇到问题时，停止尝试做你原本在做的事情。系统已经失败——立即处理失败。" — 安迪·亨特和戴夫·托马斯，《程序员 pragmatics》

该原则适用于软件开发的多个层面。在代码层面，它表现为严格的输入验证，在检测到无效数据时立即抛出异常。在架构层面，它表现为系统在依赖项失败时优雅地停止，而不是尝试以不可预测的行为进行降级操作。在组织层面，它影响团队如何优先考虑快速反馈周期，而不是在没有验证的情况下进行扩展开发。

### 快速失败原则的三层理解

* **入门**: 编写代码时，在函数入口点验证输入。如果数据无效，立即抛出清晰的错误消息，而不是尝试继续执行，在后面造成更混乱的失败。

* **实践**: 在系统边界设计明确的检查。当外部服务或API失败时，快速失败而不是通过变通方法或静默失败来累积技术债务。

* **进阶**: 在快速失败和弹性模式之间取得平衡。在分布式系统中，区分暂时性失败（重试可能会成功）和永久性失败（快速失败是正确的）。应用断路器来防止级联失败，同时在系统边界维护快速失败的语义。

## 起源

快速失败原则源于计算机科学研究，可追溯到1970年代，尤其是在异常处理和健壮系统设计方面的工作。然而，它通过安迪·亨特和戴夫·托马斯1999年出版的《程序员 pragmatics：从小工到专家》获得了广泛认可，该书将其阐述为务实软件开发的核心理原则。

该原则源于这样的认识：尝试在检测到错误后继续执行往往会导致比立即失败更糟糕的结果。当软件尝试在没有足够上下文的情况下"恢复"错误时，它经常会在下游产生更难诊断的问题。补救措施是快速失败——立即停止，保留错误上下文，让开发人员解决根本原因。

亨特和托马斯将快速失败定位为更广泛"尽早捕获错误"哲学的一部分。他们认为，缺陷的修复成本随着在开发周期中发现时间的推后呈指数增长。通过立即失败，开发人员可以保留宝贵的调试上下文：堆栈跟踪、变量值和程序状态，否则这些会因继续执行而丢失或损坏。

## 核心要点

<Steps>
  <Step title="保留调试上下文">
    当失败立即发生时，开发人员可以准确看到什么问题、提供了什么输入，以及失败时刻的系统状态是什么。
  </Step>

  <Step title="防止级联失败">
    通过在出现问题的第一个迹象时停止执行，快速失败可以防止小错误演变为更大、更难诊断和恢复的系统故障。
  </Step>

  <Step title="降低缺陷成本">
    在开发早期发现和修复缺陷远比在生产中发现它们便宜得多。快速失败加速了这一发现过程。
  </Step>

  <Step title="提高系统可靠性">
    快速失败的系统更加可预测。用户和运维人员理解失败将是可见的、可操作的，而不是静默地损坏数据。
  </Step>
</Steps>

## 应用场景

<CardGroup cols={2}>
  <Card title="输入验证">
    在函数边界验证所有输入。如果必需参数缺失、为空或超出预期范围，立即抛出异常。
  </Card>

  <Card title="配置检查">
    在启动时验证配置文件和环境变量。如果必需设置缺失或无效，立即失败，而不是以不一致的状态启动。
  </Card>

  <Card title="API契约测试">
    验证API响应是否符合预期的模式。在收到意外数据结构时快速失败，而不是尝试处理它们。
  </Card>

  <Card title="数据库约束">
    使用数据库约束（NOT NULL、FOREIGN KEY、CHECK）在持久化层强制数据完整性，立即捕获违规。
  </Card>
</CardGroup>

## 经典案例

亚马逊早期的电子商务架构著名地体现了快速失败原则。在1990年代末，亚马逊的面向服务的架构要求依赖项不可用时服务快速失败。服务不会实现复杂的回退逻辑来掩盖失败，而是立即向调用者返回错误。这种方法虽然最初导致更多可见的失败，但使亚马逊团队能够快速识别和修复可靠性问题。结果是系统虽然偶尔返回明确的错误，但保持了数据完整性，比那些尝试以降级功能"继续前进"的系统恢复得更快。这种架构哲学后来成为亚马逊描述的"从失败中倒推"的基础——其云计算基础设施的核心原则。

## 边界与失效场景

快速失败原则虽然强大，但需要深思熟虑地应用。首先，并非所有失败都应该导致立即终止。在面向用户的应用程序中，较小的验证错误可能需要友好的错误消息，而不是应用程序崩溃。该原则最强烈地适用于系统级错误，而非所有可能的错误情况。

其次，如果错误处理不当，快速失败可能会造成糟糕的用户体验。应用程序应在适当的边界捕获异常，向用户提供可操作的反馈，而不是技术错误转储。

第三，在分布式系统中，快速失败必须与弹性模式相平衡。在每一个短暂的网络抖动时立即失败的服务，将比实现适当重试逻辑的服务可用性更低。关键在于区分致命失败（快速失败是正确的）和暂时失败（重试可能会成功）。

## 常见误区

<AccordionGroup>
  <Accordion title="快速失败意味着让应用程序崩溃">
    快速失败是关于有意地、有意义地失败，而不是造成戏剧性的崩溃。目标是以受控的方式失败，提供最大的调试信息。
  </Accordion>

  <Accordion title="快速失败始终是最佳方法">
    在面向用户的应用程序中，优雅降级可能比可见失败更可取。必须根据用户体验考虑来平衡该原则。
  </Accordion>

  <Accordion title="快速失败消除了错误处理的需要">
    快速失败不会消除错误处理——它只是改变了处理发生的位置。应用程序仍然必须在服务边界适当捕获和响应失败。
  </Accordion>
</AccordionGroup>

## 相关概念

<CardGroup cols={3}>
  <Card title="防御性编程">
    编写验证输入和假设的代码，在不变量被违反时明确失败。快速失败是防御性编程的关键技术。
  </Card>

  <Card title="提前返回">
    一种代码模式，当前置条件不满足时函数立即退出，而不是将逻辑嵌套得更深。这在函数层面体现了快速失败。
  </Card>

  <Card title="断路器">
    一种弹性模式，停止向失败的服务发送请求，防止级联失败，同时允许系统优雅地恢复。
  </Card>

  <Card title="契约式设计">
    一种方法学，软件组件指定明确的前置条件、后置条件和不变量。违规立即触发失败。
  </Card>

  <Card title="《程序员 pragmatics》">
    普及快速失败原则的书籍，以及其他软件开发最佳实践。
  </Card>
</CardGroup>

## 一句话总结

<Tip>
  快速失败、明确失败——当出现问题时，立即停止并保留上下文。修复缺陷的成本随着未被检测时间的延长呈指数增长。
</Tip>
