代码问题诊断:当错误不在编译器而在你的代码时

资深开发者Jon Skeet分享调试经验:当代码行为异常时,首先应假设问题出在自己的代码而非编译器。文章详细介绍了从问题复现、最小化测试用例到查阅文档的系统化诊断流程,并探讨如何以建设性方式沟通潜在的外部库问题。

不,错误在你的代码里(还有我的)

2024年10月22日 | Jon Skeet

我可能之前就这个话题发表过文章,至少肯定在社交媒体上讨论过。
时不时地——幸好频率不高——我会在Stack Overflow上看到这样的帖子:

“这看起来像是VS.NET的bug”
“我100%确定我的代码是正确的”
“这似乎是个明显的缺陷”
“这是编译器的bug吗?”

最后一条至少是以问句形式提出的,但通常上下文都暗示提问者期待的回答是"对,这就是编译器bug"。

先假设问题出在自己身上

确实存在库、JIT编译器、C#/Java编译器或其他工具存在bug的情况。我自己也报告过不少bug,包括之前写过的相机固件问题,以及仅在Linux上失败的单元测试等有趣案例。但我始终保持着这样的心态:

当代码行为不符合预期时,我首先假设是自己哪里出错了

大多数情况下,这个假设都是正确的。

系统化调试流程

我的问题诊断步骤始终是:

  1. 确保能稳定复现问题
  2. 简化复现步骤(例如不再需要启动移动应用或在CI上运行测试)
  3. 最小化复现代码量

如果问题确实出在我的代码,这些步骤能帮助定位;如果是编译器/库/框架的问题,完成这些步骤后也能更高效地提交报告。

但要注意:即使创建了最小复现案例,也不等于发现了真正的bug。问题可能仍然出在自己身上。此时常见的错误类型不再是"我想做X但代码实际做了Y",而更可能是:

  • 使用的库行为与预期不符(但符合设计)
  • 编程语言的工作机制与预期不同(但编译器行为符合规范¹)

查阅文档的学问

接下来我会:

  • 如果是某个方法行为异常,反复阅读完整文档,寻找可能解释现象的备注或免责条款
  • 检查自己做出的各种假设是否存在模糊点
  • 对于编译器问题,隔离具体令人困惑的代码行,查阅语言规范每个细节 这个阶段必要时会做笔记,特别是当信息量超过大脑即时处理能力时。

何时确认是外部bug

完成上述步骤后如果仍无法理解观察到的行为,才可能确实是别人代码的bug。此时我不仅拥有最小复现案例,还能清晰说明为什么认为行为应该不同。只有到这个时候,我才会提交bug报告——而且是以最方便维护者处理的方式呈现。

但大多数情况下,最终发现还是自己的代码或理解有误。保持"问题可能出在自己代码"的心态,通常能比"怀疑编译器bug"更快定位真正问题。

沟通的艺术

最后一个难题是如何传达这个观点而不显得居高临下。如果告诉别人"问题可能在你的代码",听起来像是在暗示自己编程水平更高。其实完全不是——当我遇到意外行为时,第一反应也是怀疑自己的代码。这也是我写本文的原因之一:希望通过在Stack Overflow评论中引用此文,能更积极地传达这个观点。


¹ 这种情况在C#中依然存在——我已不再为此感到尴尬。作为ECMA C#标准化工作组召集人,我们团队包括Mads Torgersen、Eric Lippert、Neal Gafter和Bill Wagner等对C#理解远胜于我的大牛。但在每月会议中,我们仍经常发现某些行为让部分或所有人惊讶,或者对着现行标准也无法就编译器行为达成一致。这种经历既令人谦卑,又充满乐趣和欢笑。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计