看到一篇讨论如何处理难以复现的 bug 的文章,这应该是面试里会被问到的问题,随手翻译了一下,大家可以酌情参考。

转瞬即逝:什么是海森堡 bug?

你是否遇到过一种似乎违背逻辑、难以复现的缺陷?

如果你的答案是”是的”,请放心,你并不孤单。

这类缺陷往往在看似随机的条件下发生,这意味着我们无法确定重现问题所需的具体步骤。通常,我们能得到的唯一信息可能是一些模糊的描述,比如”我在执行某个特定流程时遇到了这个问题,但之后就再也无法重现了。”

正因如此,这些问题通常被称为”不可重现的缺陷”,或者就像我最近发现的,被称为”海森堡 bug”。海森堡 bug 的一个显著特征是,任何试图观察或调试问题的行为都可能改变应用程序代码的行为。仅仅是观察问题的行为就会无意中改变问题发生的条件。这一点我们将在下面详细讨论。

当密切关注反而适得其反:观察者效应

“海森堡 bug”这个术语是对著名物理学家维尔纳·海森堡的一个俏皮双关语,他首次提出了量子力学中的观察者效应。

“观察者效应(不要与测不准原理混淆)指的是观察某个情况或现象必然会改变它。观察者效应在物理学中尤为突出,因为观察和不确定性是现代量子力学的基本特征。观察者效应不仅存在于物理学,在社会学、心理学、语言学和计算机科学等领域也广为人知。” ——《观察者效应》,IEEE 出版物,K. Baclawski 等人。

一个简单的例子就是检查轮胎气压。仅仅是将气压表连接到气门上,就几乎必然会损失一些空气。所以,单纯尝试读取轮胎气压的行为就会改变轮胎的实际气压。

考虑到这一点,我们可以推断,仅仅是尝试重现缺陷的过程就可能足以改变代码的行为,使得缺陷不再出现。

海森堡 bug 的一个例子:同步失败的故事

同步缺陷是如何产生的

想象我们需要测试一个使用多线程的应用程序。多线程是一种执行方法,允许在一个进程中创建多个线程。每个线程都独立执行,但与其他线程共享进程资源。构建这样的应用程序需要细致的编程,以避免可能出现的问题,如竞态条件和死锁。

在应用程序代码中,开发人员创建了一个修改共享计数器变量的函数。理想情况下,开发人员会使用同步机制来确保每个线程在继续执行之前都达到与其他线程相关的已知操作点。然而,如果没有实现同步机制,两个线程就可能同时修改共享计数器。

简单来说就是:

我们有一个初始值为’0’的计数器变量。线程一将计数器变量更新为’1’,同时线程二将计数器变量更新为’2’。

那么正确的值是多少?

在这种情况下,根本无法确定,这很可能导致应用程序出现不可预测的行为。

观察者效应的实际表现

调试这样的问题可能异常困难。通过添加打印语句、日志记录或使用调试器,线程的时序可能会发生足够大的变化,使得竞态条件的出现概率大大降低。这就给人一种缺陷消失的错觉。

像这样的不可重现缺陷或”海森堡 bug”可能会让开发人员和测试人员头疼不已,因为它们经常出其不意地出现,又消失得无影无踪。这些缺陷的本质特征使得它们极难诊断、记录和修复,可能导致开发人员和测试人员之间的挫折感。

打破”在我的机器上能用…“的论调

处理那些我们无法可靠重现的隐蔽缺陷可能是软件开发中最具挑战性的方面之一。这些问题不仅需要技术专长,还需要团队成员之间的紧密合作和清晰的沟通。所有团队成员都必须以开放的心态来处理这些问题,认识到仅仅因为问题在一个环境中没有出现,并不意味着它在另一个环境中就不存在。

用随意的”在我的机器上能用”的心态来否定问题,这种做法会破坏寻找解决方案和提高项目整体质量所需的协作精神。相反,这些挑战应该被视为加深理解、加强合作和构建更具弹性系统的机会。

软件团队该怎么办?

不可重现的缺陷可能源于各种因素,包括罕见的时序条件、特定的硬件或软件配置,或软件内部的复杂交互。理解和修复这些缺陷需要敏锐的洞察力、系统的方法和大量的耐心。每次尝试调试问题都可能感觉像是徒劳无功,但耐心和沟通是关键。

如果缺陷仍然无法重现,我们该怎么办呢?

作为团队理解缺陷的上下文

"上下文:事情发生的情况,有助于你理解它。" - 牛津学习词典

所有提出的缺陷都需要对上下文有深入和广泛的理解。上下文包括各种因素,如软件运行的环境、发现缺陷时的具体条件,以及导致问题的操作序列。没有这些信息,就很难把握缺陷的全部影响,这可能会显著改变对其风险和影响的认知。

以下是一些可能有助于确定是否应该继续调试不可重现缺陷的考虑因素:

评估潜在影响

  • 评估缺陷的潜在严重性。如果缺陷可能导致数据丢失、安全漏洞或重大用户中断,即使很难重现也可能值得进一步调查。
  • 考虑缺陷可能发生的频率。一个罕见但灾难性的缺陷可能仍然值得深入调查,而一个罕见且轻微的问题可能不值得。

评估继续调查的投资回报

  • 评估尝试重现缺陷所消耗的时间和资源。如果付出的努力与潜在影响不成比例,可能就不值得继续追究。
  • 考虑如果团队专注于重现单个缺陷,可能会忽视或延迟哪些其他工作或改进。有时候,关注已知的、可重现的问题或新功能可能是更好的时间利用方式。

收集所有可用信息

  • 确保缺陷详情得到记录并传达给团队。这应该包括预期与实际行为的对比、问题出现的位置、环境信息(如软件版本、操作系统、硬件和配置)、发生频率以及任何其他相关信息。
  • 检查是否有足够的日志、错误报告或遥测数据。如果信息太少或没有信息,可能很难取得进展。如上所述,这些缺陷并不总是能获得统计数据和信息,因此团队不应该仅仅依赖日志提供的信息。
  • 有时最终用户可以提供有助于重现问题的宝贵见解或模式。如果有持续的用户报告,可能值得进一步调查。
  • 向组织内外的其他人寻求建议。你可能不是唯一遇到这个问题的人。在网上研究,与组织内的其他团队交流,检查在线缺陷报告(例如 GitHub issues)。你可能会在第三方包和开源软件的代码库中找到关于这个问题的好文档以及如何重现它。

监控系统并减轻缺陷的潜在影响

  • 是否可以实施一个变通方案或保护措施来最小化缺陷的潜在影响?例如,如果你在代码中使用了第三方包,而缺陷存在于这个包中,你能否使用一个完全不同的包来解决问题?这样就消除了进一步调查的需要。
  • 设置监控以捕获更详细的数据,以备缺陷再次出现时使用。这可能为未来的调查提供线索。
  • 考虑创建自动化脚本来监视特定的词或对象,并计划它们在一天中定期运行。这样做可能有助于深入了解导致缺陷发生的原因。它还可能突显出问题是否在特定时间发生。这些信息随后可以与后台触发的其他服务和操作(例如备份或防病毒更新)相关联。

协调团队和利益相关者的优先级

  • 考虑如果缺陷后续在生产环境中出现是否会对用户或利益相关者体验造成损害。如果缺陷对用户或业务利益相关者来说是高优先级的,可能值得继续努力。
  • 确保团队充分了解情况,并与他们合作决定是否继续调查。有时一个全新的视角可能会带来不同。

总结:尝试找出海森堡 bug 的根本原因是否值得?

关于是否应该花更多时间调查海森堡 bug 的决策过程应该是一项协作努力,涉及所有相关利益相关者,包括开发人员、QA 工程师、产品经理,甚至在适当的情况下包括最终用户。这确保了各种观点都得到考虑,从而做出更明智和平衡的决定。此外,采用一种视每个缺陷(即使是那些难以重现的缺陷)为加强团队调试实践机会的心态,可以带来软件质量的长期改进。

最终,是否继续或停止调查的选择应该在风险、用户体验和资源效率之间取得平衡。应对这些挑战不仅仅是为了解决眼前的问题,还关系到促进协作努力来改进开发过程,最终产出更有韧性和可靠的软件。