SuperAPI AI 工具集

fail-fast

编程与开发

查看项目

更新于 10-16 03:31

技能介绍

用途

fail-fast 是一个用于让代码库更确定性的技能。它的目标是找出并移除隐藏的兜底(fallback)行为——那些本应成为显式契约、校验错误、迁移或测试夹具的逻辑。

使用该技能时,应在开始时声明你正在使用 fail-fast 技能。

核心规则

优先采用一条规范路径和一种显式失败模式。

只有当兜底满足以下条件时才允许保留:真实的产品需求、有文档记录的迁移窗口,或有意为之的本地测试夹具。其余一切都应转变为校验错误、必填输入、显式分支、删除的兼容层,或带类型的配置契约。

工作流

  1. 识别目标仓库及其包管理器。
  2. 编辑前先运行扫描器(路径相对于该技能目录):
python3 scripts/scan-fallbacks.py /path/to/repo

可用参数:--json(机器可读输出,用于大型审计或后续工具)、--include-tests 和 --include-docs(扫描默认跳过的文件)、--fail-on medium|high(作为 CI 门禁,非零退出)。

python3 scripts/scan-fallbacks.py /path/to/repo --json

当发现涉及环境变量、遗留兼容、宽泛 catch 块或分阶段迁移时,阅读 references/remediation.md。

  1. 对每个发现进行分类:
  • remove:已死的兼容代码、遗留别名、重复的选项名、没有活跃调用者的兜底分支。
  • require:缺失的配置、缺失的依赖、无效的用户输入,或应属于环境 schema 的环境变量。
  • validate:必须接受未知数据但应清晰拒绝无效状态的边界输入。
  • keep:有文档记录的产品行为、真实的外部 API 兼容,或带有负责人和移除日期的临时迁移。
  1. 窄范围编辑。移除兜底状态,并更新调用点/测试以使用规范路径。
  2. 添加或更新测试,证明当依赖、输入、配置或 schema 声明的环境变量缺失时,代码会确定性地失败。
  3. 再次运行扫描器,以及仓库相关的测试、类型检查、lint 或构建。
  4. 显式报告剩余的兜底。不要让它们不可见。

环境变量

环境变量是配置契约,不是建议字段。

在具有已验证环境 schema 的项目中,必填值应在应用代码使用它们之前通过校验失败。从项目的环境模块导入带类型的值。如果 schema 中缺少某个必填键,应在那里声明并校验,而不是添加应用层兜底。

使用项目现有的环境校验和构建检查。查看其脚本和校验器文档以获取实际命令;不要仅为遵循此技能而安装新包。

仅在以下情况接受直接读取 process.env:环境 schema 模块、生成的环境接线、诸如 NODE_ENV 和 CI 之类的窄系统键,或明确记录的迁移逃生舱。

移除如下模式:

const apiKey = process.env.OPENAI_API_KEY || "dev-key";
const databaseUrl = process.env.DATABASE_URL ?? "postgres://localhost";

替换为带类型的环境访问:

const apiKey = env.OPENAI_API_KEY;

当不存在环境 schema 时,使用项目现有约定在配置边界显式校验必填值。

错误处理

fail fast 不意味着模糊地崩溃。它意味着在边界处以有用的、带类型的或带上下文的错误失败。

优先使用:

  • 对外部输入进行 schema 校验,
  • 对不可能的内部状态显式抛出错误,
  • 窄 catch 块,带上下文重新抛出,
  • 对必填配置和服务的启动预检。

避免:

  • 空 catch 块,
  • 返回虚假成功的 catch 块,
  • 静默降级必需功能的可选依赖导入,
  • 让测试通过但生产行为已损坏的兜底数据。

兼容性

向后兼容必须是有意为之的。

区分永久支持的契约与临时迁移。保留公共 API、协议或受支持产品行为所需的兼容;它不需要移除日期或逐个命名的消费者。临时兼容应有负责人、迁移窗口和两条路径的检查。缺少文档是调查并记录契约的理由,而不是删除安全的证明。只有在确认没有受支持的调用者或契约仍需要它们之后,才移除别名、遗留分支和适配器。

当兼容必须保留时,让它可见:

// TODO(compat): remove after API clients stop sending `workspaceId` on 2026-07-01.

让标记以 TODO/FIXME/HACK 开头,以便扫描器的 todo-compat 规则能发现它——单独的 // Compatibility: 注释有意不被标记(关键词规则会跳过注释行)。

扫描器

scripts/scan-fallbacks.py 对常见兜底气味执行确定性源码扫描:

  • 环境变量默认值(os.getenv/os.environ.get/process.env.X ||)和直接环境读取,
  • || / ?? 兜底值,
  • legacy、deprecated、backwards-compatible 和 migration 关键词,
  • 空 catch 或吞异常 catch 块(JS/TS)以及吞异常 except 块(Python),
  • 可选依赖兜底,
  • 别名和双配置键。

检测注意事项:legacy/兼容关键词规则只在代码行触发,不在注释行触发,因此单独的 // Compatibility: ... 标记有意不被标记。临时兼容注释只有在以 TODO/FIXME/HACK/XXX 开头时才会被呈现(todo-compat 规则)——参见兼容性部分的标记格式。关键词规则还会跳过声明式清单(package.json、tsconfig.json),以避免匹配依赖名。--fail-on 只接受 medium 或 high(没有低严重度规则)——之前接受的 --fail-on low 现在会以 argparse 错误退出,因此请更新任何传入它的脚本。

扫描器不能替代判断。把它当作需要检查的位置索引,然后让代码更简单、更确定性。

完成检查

在结束前,验证:

  • 每个发现都被分类(remove / require / validate / keep),而不仅仅是列出。
  • 被移除的兜底已更新调用点和测试,证明当依赖、输入、配置或环境变量缺失时会确定性失败。
  • 保留的兜底有明确理由(产品需求、外部契约,或带负责人和日期的迁移)。
  • 编辑后重新运行了扫描器以及仓库相关的测试/类型检查/lint/构建。
  • 任何剩余的兜底都被显式报告,而不是不可见。