Systematic Debugging 是一套系统化的根因调试协议,强调在修复任何技术问题之前必须先理解问题。其核心原则是:始终先找到根因,再尝试修复;只修复症状属于失败。该协议提出一条“铁律”:没有完成根因调查,不得提出修复方案。
用途
该技能适用于任何技术问题,包括测试失败、生产环境缺陷、意外行为、性能问题、构建失败和集成问题。尤其在以下情况下应使用:时间压力大、看似只需“快速修一下”、已经尝试过多次修复、之前的修复没有生效、或者尚未完全理解问题。即使问题看起来简单、时间紧迫或有人要求立即修复,也不应跳过流程。
四个阶段
协议分为四个阶段,每个阶段完成后才能进入下一阶段。
第一阶段:根因调查。在尝试任何修复之前,需要:仔细阅读错误信息,不要跳过错误或警告,完整阅读堆栈跟踪并记录行号、文件路径和错误代码;稳定复现问题,确认能否可靠触发以及确切的复现步骤,若无法复现则应收集更多数据而非猜测;检查近期变更,包括 git diff、近期提交、新依赖和配置变更;在多组件系统中收集证据,在提出修复前先添加诊断埋点,记录每个组件边界的数据流入和流出,验证环境与配置传播,检查每一层的状态;检查模式与环境差异,当问题在生产环境复现但在测试中不复现时,对比测试夹具模式与生产模式,查找缺失的 NOT NULL 列、外键、唯一约束或默认值,检查 SQLite 日志模式、连接标志或 PRAGMA 设置的差异;检查回退链中的异常类型特异性,注意 OperationalError 与 IntegrityError 是兄弟关系而非父子关系,捕获一个不会捕获另一个;逐步刻画工具与 API 行为,从可工作的输入开始逐步扩展直到失败,以隔离导致失败的变量;在修改库代码前检查依赖来源,使用 pip show 确认包是否来自可编辑的开发副本,若并非有意为之则应停止并询问;进行系统性网络检索,使用确切错误信息、症状加平台上下文、组件或服务名称进行搜索,优先查看近期讨论并完整阅读整个线程;处理 macOS 沙盒应用问题,定位容器目录、检查 XPC 服务伴随进程、直接读取数据库、检查系统日志、重置 TCC 权限、了解 I/O 边界、区分本地与云同步损坏,并按升级顺序采取恢复措施;追踪数据流,从调用栈深处向上游追踪坏值的来源,在源头而非症状处修复。第一阶段完成清单包括:错误信息已完整阅读并理解、问题已稳定复现、近期变更已识别并审查、证据已收集、问题已隔离到特定组件或代码、根因假设已形成。
第二阶段:模式分析。在修复前先找模式:寻找同一代码库中类似的可工作代码;对照参考实现,完整阅读而非略读;识别可工作与损坏之间的差异,列出每一个差异,无论多小;理解依赖关系,包括需要哪些其他组件、设置、配置和环境,以及它做了哪些假设。
第三阶段:假设与测试。采用科学方法:形成单一假设,明确陈述“我认为 X 是根因,因为 Y”,并写下来;做最小化测试,只做一个变量,不要同时修复多个问题;在继续之前验证,若成功则进入第四阶段,若失败则形成新假设,不要在原有基础上叠加更多修复;当不知道时,直接说“我不理解 X”,不要假装知道,应寻求帮助或进一步研究。
第四阶段:实施。修复根因而非症状:创建失败测试用例,尽可能使用自动化测试,测试环境必须镜像生产环境;实施单一修复,一次只做一个更改,不要顺手做其他改进或捆绑重构;验证修复,运行特定回归测试和完整测试套件;若修复无效,遵循“三次法则”:统计已尝试的修复次数,若少于三次则返回第一阶段重新分析,若达到或超过三次则停止并质疑架构,不要在未讨论架构的情况下尝试第四次修复;若三次以上修复失败,应质疑架构,识别架构问题的模式包括每次修复都在不同位置揭示新的共享状态或耦合、修复需要大规模重构、每次修复都在别处产生新症状,此时应停止并质疑根本假设,讨论是否应重构架构而非继续修复症状。
危险信号
如果发现自己出现以下想法,应立即停止并回到第一阶段:“先快速修一下,之后再调查”、“先试试改 X 看看行不行”、“同时做多个更改然后跑测试”、“跳过测试,我手动验证”、“大概是 X,我来修一下”、“我不完全理解但这样可能行”、“主要问题有这些(未调查就列出修复方案)”、在追踪数据流之前就提出解决方案、“再试一次修复”(已经尝试两次以上)、每次修复都在不同位置揭示新问题。若三次以上修复失败,应质疑架构。
调查流程与推进
当调查正在进行且用户表示“有进展但还没完成”时,不要通过提出澄清问题打断流程,应继续推进,收集更多证据、尝试下一个诊断步骤、检查另一个角度。应继续推进的信号包括:用户说“有进展但还没完成”、用户忽略澄清问题、用户说“好的”或“还是坏的”。此时应从日志、数据库或文件状态中获取信息,而不是让用户充当仪表层。只有在确实无法在没有输入的情况下继续且已穷尽所有自助选项时,才提出问题。
常见借口与现实
该技能列出了常见借口及其现实:问题简单不需要流程——简单问题也有根因,流程对简单缺陷很快;紧急情况没时间走流程——系统化调试比猜测式反复尝试更快;先试这个再调查——第一次修复就设定了模式,应从开始就做对;确认修复有效后再写测试——未经测试的修复不牢固,测试先行才能证明;同时做多个修复节省时间——无法隔离哪个生效,还会引入新缺陷;我看到问题了让我来修——看到症状不等于理解根因;再试一次修复(已失败两次以上)——三次以上失败意味着架构问题,应质疑模式。
快速参考
第一阶段根因:阅读错误、复现、检查变更、收集证据、追踪数据流,成功标准是理解是什么和为什么。第二阶段模式:寻找可工作示例、对比、识别差异,成功标准是知道差异所在。第三阶段假设:形成理论、最小化测试、一次一个变量,成功标准是确认或形成新假设。第四阶段实施:创建回归测试、修复根因、验证,成功标准是缺陷解决且所有测试通过。
参考资料
references/dependency-source-example.md 提供了检测可编辑开发分支导致库依赖模式漂移的完整示例,当第一阶段步骤 5d 将你引向第三方包作为疑似缺陷来源时阅读。references/macos-sandbox-debug-example.md 提供了调试 Apple Books.app 导入失败的完整演练,演示第一阶段步骤 6b 中的 macOS 沙盒技术,当调试 macOS 沙盒应用时阅读。