深入讨论 AI 生成代码在实际项目中的缺陷,揭示 Claude 辅助编程的实战风险和能力边界。
为了避免被指责"只是 Claude 为自己辩护""AI 垃圾""可能全是幻觉"等,我认为值得解释几个关键要点,说明本报告的创建方式:
所有指标、方法论和数据源均由我独家选择,并与我的妻子协商——她拥有宾州州立大学统计学硕士学位。
方法论直接基于我妻子的建议:她指出,简单地比较改革前后单位代码行数的 bug 数量可能会受到过度噪音影响,因为 Claude 后期样本数量较少,并且出于类似原因,尝试建立某种线性回归模型以确定不同变量的相对影响也可能行不通。她特别建议我,查看 Claude 后期版本在历史分布中的位置,以及从历史分布看我们多大概率会获得与 Claude 后期版本同样差或更差的版本,这可能是能做到的最好方法。
我花了好几天时间,其中两天是在创建 GitHub 仓库之前,并进行了至少一次重大的报告全面改写以采用更好的方法论(基于上述妻子的反馈)。这对我来说是大量的手工和认知工作。
用于获取数据、将其整合到 DuckDB 数据库文件、构造该数据库的视图,以及对数据进行统计分析的脚本,确实是由 Grok 5.1 编写的,最终报告网页的 HTML 和大部分原始文字亦是如此。
然而,至关重要的是,本报告中的所有数字、统计数据、卡片和图表都由运行统计分析的 Python 脚本自动直接模板化,从而避免了数字中出现幻觉或不一致的任何可能。
在 Hacker News 上发布此文后,几乎没有收到关于文章实际内容的实质性意见、讨论或回应,我决定用自己的文风重写所有文字。如果有人抱怨我的冗长或句子结构——正如他们通常会做的那样,这正是我最初让 AI 写文字的原因之一(尽管被模板化废弃了)——他们可以滚蛋。
如果你想复制这里的数据和结果,并检查它们的精确计算方式,可以在这里找到仓库。我有意让整个管道可以从零开始完全运行,让你看到端到端的完整过程,没有神秘的数据库 blob 强制你相信我没有篡改或搞砸数据。如果你要对这些数字不满,先在那里看。
2026 年 5 月末,rsync 激起了风波。首先,一篇毫无根据的 Mastodon 帖子指出了某用户升级后遭遇的回归与该版本包含 Claude 提交之间的虚假相关性。这篇帖子的浏览次数不详,但点赞和转发轻易突破数千,引起了显著关注——正如所有虚假反 AI 仇恨帖一样——收到来自 32 个独特用户的 58 条回复。有人无根据地大喊"认知投降";另有人建议将 rsync 加入著名的开源垃圾软件黑名单。随后它传播到 Hacker News,引来 81 条评论,充满恐惧、愤怒,以及对"这终于证明没人能安全使用大语言模型"的欢呼。在众多评论中,有一条特别的回复进一步推动了认为回归和 bug 由 Claude 导致的观点。
到 2026 年 5 月 30 日,这种新兴的愤怒汇聚成了一个单一焦点:一个标题为"请不要凭感觉搞砸这个软件"的 GitHub issue,针对 rsync 仓库提出。它只附加了那篇 Mastodon 帖子的截图,批评项目使用 Claude。仅此而已。没有 bug 报告,没有技术内容,没有任何尝试验证问题是否真实或正当;只有 350+ 条评论,从理性关切到公然骚扰不等(最严重、不理智和彻底暴力的评论大多已被删除,很少有人保存过它们)。
讨论不仅停留在言论阶段。它最终升级到暴力幻想的视觉表现,当某用户发布了一条(现已删除的)评论,其中包含他们对"推送凭感觉编码提交的项目维护人"进行谋杀的小马驹漫画:
完成了互联网舆论周期,这个 issue 随后传播到 Hacker News,引发了数百条更多评论。一些试图指向 Claude 引入后的回归数量作为证据——"Linux Mint Timeshift 工具的一个 issue 记录了多个当前在 rsync issue 页面开启的回归,这些回归仅在凭感觉编码后引入"——其他人指出这些回归并非由 Claude 导致,作为回应,话题被又一次转移了。这种模式反复出现,核心主题始终是一个中心论断,到处重复:Claude 辅助开发在一个先前稳定的工具中引入了 bug。AI 就是认知投降、就是精神鸦片、就是手艺丧失,用户的愤怒是有道理的:
人们对一个曾经非常稳定、广为信任的工具如今开始立刻走下坡路感到完全正当的愤怒……只因主开发人员凭感觉编码了这个软件。
然而,这不一定是一个仅凭——讽刺地——感觉就能解决的问题。这是某种程度上可以经验论证的。有些人甚至指出了这一点:
在 Lobste.rs 上,针对 Tridge 本人发布的 Medium 文章,最后有些用户如 boramalper 开始真正要求证据:
如果有人实际做出一份回归时间表,按每次发布统计(如果可能的话),看看这个数字最近是否真的上升了,那会很有意思。
用户 bitshift 回复:"我也很想看到这样的图表。它不会完全有用……但至少它将是某个我们可以客观衡量的东西。"
这个分析就是那个图表。或者说,就数据的限制而言(见上一节),它是最好的近似。
36 个版本含有 bug 数据,从 v2.4.6 到 v3.4.3
2 个版本有 Claude 提交:v3.4.2(9 个 Claude 提交,0.00 sev/10c)和 v3.4.3(28 个 Claude 提交,3.29 sev/10c)
Claude 版本分别在四分位距的两侧:v3.4.2 低于四分位距,v3.4.3 高于四分位距。两者都不是异常值。
精确排列检验 p 值 = 46%:随机选择任意 2 个版本,你有 46% 的概率得到同样差或更差的结果。这是可用的最强检验,但未发现任何异常。
Fisher 精确检验 p 值 = 74%:Claude 版本高于历史中位数的概率不比其他版本高(odds ratio 1.06)。
历史平均值是 Claude 平均值的 1.8 倍(2.95 对 1.65 sev/10c)
v3.4.1(59 个 bug / 9 个提交,无 Claude)是异常值但属于基线——它是一个版本,分布已包含它
该分析使用单一指标:严重程度加权的每 10 个提交的 bug 数(sev/10c)。每个 bug 被标准化为 0–1 的严重程度得分(其大语言模型分配的严重程度除以 100),这些得分按版本求和,而非简单计数。原始 bug 计数也在表中显示供参考,但 sev/10c 驱动所有统计检验。
默认分支上的每个提交都按提交者日期排序,形成顺序时间线。每个 git 标记指向该时间线中的某个特定提交。版本的范围是前一个标记和该版本标记之间的所有提交。预发布标记("pre"、"rc")作为边界被跳过并合并到其最终版本。每个提交恰好属于一个版本。
Bug 报告来自三个来源:
rsync 仓库的 GitHub issues(通过 GitHub REST API 收集),
rsync Bugzilla 实例(通过其 API 收集),
以及 rsync 邮件列表。
GitHub issues 和邮件列表中的 bug 归属于报告该 bug 前发布的最新版本。对于 Bugzilla,每个条目都有一个"Version"字段明确说明 bug 针对的发布版本,bug 被归属于该版本。
为了控制 bug 严重程度——这样按钮拼写错误和 CVE 就不会被平等对待,正如 Hacker News 上某人所说——每个 bug 报告都在 0–100 的尺度上评分严重程度。评分者是 Qwen 3 35B,一个小型开放权重大语言模型,以高级可靠性工程师的身份来评估实际影响。每个 bug 报告的标题和正文文本(截断至 3,000 字符)连同以下标准被提交给模型评分:
三个 bug 来源——GitHub issues、Bugzilla 和 rsync 邮件列表——都进行了评分。Bugzilla 和邮件列表报告仅有标题(无正文),因此模型仅从标题对这些进行评分。模型被指示在正文信息不足时,回退到标题并倾向于范围的中间值(40–60)。模型还被设置为仅通过结构化输出(JSON schema)输出一个严重程度整数,因此没有自由文本响应需要解析。评分在温度 0 下进行以确保确定性——相同的输入始终产生相同的分数。
评分为严重程度 0 的 issue——功能请求、垃圾邮件、关于 AI 的离题言论、空白提交——默认从 bug 计数中排除。这很重要,因为某些发布在 GitHub 上吸引了大量噪声。v3.4.2 有四个 issue 被提交;模型将这四个都评分为严重程度 0(一个功能请求选项、一个缺失 tarball 问题和两个功能请求)。
来自数据库的示例评分,每个层级一个:
为什么按发布版本分组提交、按发布版本分组 bug,然后通过发布版本的中介来确定 Claude 提交和 bug 之间的相关性(或缺乏相关性)?这有两个原因。
首先,因为批评者提出的声明本身也是用发布版本的方式表述的:在一个发布版本中有任何 Claude 提交会以显著的方式使整个发布版本变得更容易出现 bug,而不仅仅是 Claude 编写的提交可能引入更多 bug;后者是一个不同的指标,因为稍后的 Claude 或人类编写的提交可以在同一发布版本内修正这些 bug,那么没有人会注意到,总体上对用户也无关。此外,如前所述,与批评者的声明在其所在之处相遇很重要。如果这迫使他们使自己的声明更加细致,或以其他方式改变立场,那就算是成功了。
其次,这是一个归因的问题:绝大多数 bug 没有准确说明是哪个提交导致的,因为这样做需要进行通常不值得的广泛研究和分析,不如直接向前修复;即使进行了那种分析——通过类似 git bisect 之类的方式——也不一定会产生任何有用的东西。许多 bug 可能由多个提交的组合导致,这些提交通常在时间上相隔甚远,其中不清楚哪个提交真正引入了 bug。或者,一个提交可以同时揭示由其他提交引入的多个潜在 bug,等等。
批评者的声明简单、绝对且普遍化:Claude 暴露发布版本中的 bug 率上升了。因此,最简单诚实的回应是精确分析正在声称的内容:bug、提交、发布版本和 Claude 暴露的提交。如果 Claude 发布版本位于历史分布的中间,批评者就需要解释为什么这个特定的中间值比以前的所有其他中间值都更糟。即使按严重程度加权,我觉得我在这一切中对反 AI 的观点给予了广泛的慷慨,但足够聪明的批评者提出了它,我认为值得这样做。
即使这导致了将对话转向对发布版本中 bug 的质量、类型和用户影响的更细致讨论,对于 pro-AI 阵营来说已经是一个重大胜利,对反 AI 阵营来说是改变立场,然后我们可以基于此进行进一步分析。对于这个游戏,球现在在反 AI 阵营的一边。
我意识到这个指标不能控制提交的复杂性或安全强度。这是一个钝器。但批评者的指责也是钝的:"Claude 使事情变得更糟。"钝器是回应所需的。
在我们深入分析之前,让我们看看这两个 Claude 发布版本本身,以了解它们:
如果这对你来说不像一个危险信号,那你是对的。
所以问题是:Claude 发布版本是否异常有 bug,或者你能否轻易地从历史分布中幸运地找到一个同样糟糕的发布组?统计上回答这个问题的方式是精确排列检验,它只是列举所有两个发布版本对,并问:有多少比例的平均 bug 率与我们实际观察到的一样糟糕或更糟?这个比例是所测试假设的 p 值。
这个 p 值告诉我们的是,Claude 使发布版本变得更糟的假设,至少到目前为止,其预测力与掷硬币差不多:如果你闭上眼睛随机选择 2 个发布版本,你会有将近一半的时间表现得一样糟糕或更糟。Claude 组没有什么不寻常的。
排列检验问的是:随机发布组的评分与 Claude 组一样糟糕的可能性有多大?但还有另一种提出问题的方式:Claude 发布版本比非 Claude 发布版本更可能位于历史中位数之上吗?这是一个教科书式的 2×2 列联表,其标准检验是 Fisher 精确检验。
需要强调的是,这并不意味着将来所有 Claude 发布版本都不会更有 bug。我们没有足够的数据来建立模型并像这样推断,这也不是 Fisher 精确检验的用途。这里要说明的一点是,这些特定的发布版本根本不值得注意;如果没有人知道它们是 AI,没有人会在乎或注意到任何不寻常的东西,而且没有证据得出 Claude 使任何东西变得更糟的结论,这与批评者提出的客观、绝对、普遍的声明不同。
如果你还没有被说服,这里有一个视觉辅助,显示这些发布版本在所有先前发布版本分布中的位置:
如何阅读此图表:每个点是一个发布版本。阴影绿色带是四分位数范围——历史发布版本的中间 50%,从 0.29 到 2.59 sev/10c。两侧的较深区域是下四分位数和上四分位数。
这是另一种方式来表达前两个测试所说的同样内容,但更直观:Claude 发布版本(绿色点)以相反的方向括住 IQR。v3.4.2(有零个真实 bug)位于 IQR 下方;v3.4.3 位于其上方。它们以相反的方向括住分布的中间部分。两者都不是负异常值,由于它们位于 IQR 的两侧,没有证据表明 Claude 使用会以任何方向弯曲发布版本。
我看到的一个可能的反对意见是,虽然 Claude 编写的提交的缺陷率可能不比人类编写的差,但 Claude 加速了开发——可能是由于"凭感觉编程"——以至于每个发布版本中的 bug 总数变得太高而无法接受,在这种情况下,每个提交的缺陷率不那么糟糕这一事实无关紧要,因为这不是下游用户经历的。我们可以检查这一点:
所以看起来 Claude 发布版本没有明显更多的提交。如果有的话,它们的提交少得多,但当然,数据不足以确定这一点;就像这篇文章的其他部分一样,我只是试图显示对所谓使用 Claude 造成的大规模伤害的令人惊人的缺乏证据,而不是试图证明必然没有任何伤害,或者它甚至在有重点的发布版本方面是"有益的"。
但提交计数是一个钝的衡量标准。一个提交可以是一行拼写错误修复,也可以是整个协议处理程序的重写。那改变的代码行数呢?GitHub 的比较 API 为我们提供了每对连续标签之间的添加和删除:
改变的代码行数大幅增加。但绝对 bug 数量呢?
所以:Claude 发布版本改变了比历史发布版本多得多的代码行,但没有更多的 bug。更多代码,相同的 bug。如果 Claude 使事情变得更糟,这不是你所期望的。
明显的反驳是,也许早期的 rsync 发布版本在维护模式下较少,因此有更多 bug,但最近的 rsync 发布版本更稳定,所以将两个 Claude 暴露的发布版本与完整的历史分布进行比较会掩盖它们实际上是其时代的异常值这一事实。幸运的是,有一种方式可以在统计上测试这一点。
是的,历史均值(2.95 sev/10c)由双峰分布驱动:v2.x 发布版本平均 1.11 sev/10c;v3.x 发布版本平均 4.23。但即使在 v3.x 时代内,Claude 发布版本也位于队伍的中间或更好的位置:
所以制度转变论证不仅失败了——它是反向失败的。v3.x 时代的平均严重性/10次提交远高于 v2.x。如果你将比较限制在仅 v3.x 范围内,Claude 发布版本根本没什么特别的——其中一个甚至比大多数都好。要让 Claude 看起来像个异常值,唯一的办法就是把它与更平静的时代对比,然后把转变归咎于 Claude,尽管数据表明这个转变早于 Claude 就已经开始了。
我们可以通过进行游程检验来进一步检验版本历史中是否存在明显不同的时代,从而验证使用完整历史数据是否合理。如果存在这样的时代,游程检验会检测出非随机聚类:
不过,我最喜欢的部分在这里。深入挖掘数据,有一件事让我豁然开朗——rsync 历史上最差的发布版本,完全是在引入 Claude 之前的:
然而没有人注意到。没有 AI 可以指责,所以没有 GitHub issue 有 300 条评论,没有死亡威胁,没有威胁要 fork 或迁移到 openrsync。维护者发布了一个破损的版本并修复了它,就像常常发生的那样。唯一让 v3.4.3 特别的是有一个敌人——每个人都已经决定了要讨厌它。
那么,为什么人们会感到被背叛,并如此确信事情"显然"变得更糟了——Claude"破坏了"rsync——当没有证据支持这一点,而且除了两个完全平凡的发布版本外没有其他数据时呢?
很大程度上这只是对使用 LLM 的纯粹、盲目的愤怒。然而,有一些混淆因素可能导致人们产生这种感受:
在 HN 讨论中,用户 zos_kia 直接指出了这个混淆因素:
粗略一看,这似乎是对 CVE 响应的安全修复,该修复暴露了自 2007 年以来一直存在于代码中的编码错误。这太平凡了,看到人们对此如此失控实际上很有趣。
在 Lobsters 上,用户 jbert 阐明了因果链:
导致变更数量增加(因此回归数量增加)的触发因素是大量(主要是)LLM 驱动的安全问题的涌入。也就是说,因果链是:LLM → 更多已知安全问题 → 需要比平时更多的变更 → 比平时更多的回归。
本质上,这不是一个"Claude"问题,而是一个"更多安全工作"问题,Tridge 本人在他的回应中确认了这一点,描述了大量 AI 生成的 CVE 报告如何迫使 rsync 的攻击面进行了快速、广泛的更改。
但是,与所有与 AI 相关的事物一样,这并不重要。最终,愤怒不是关于 rsync 现在是否更糟或更好,而是人们不喜欢 AI,并从既定的定义出发,而不是从经验结果出发,来得出想要的结论:AI 是坏的:
就像我说的,作者"试图在安全和功能回归之间取得平衡"。我不否认他试过了。我仅仅否认的是聊天机器人善于写代码;实际上,它们不擅长写代码。如果作者用心智模型(Naur 理论!)手工处理这些安全漏洞,该模型会保留他们想要的功能和特性,那么它们会导致更少的回归……
面对这个在没有证据支持的情况下提出的、泛泛而谈、绝对的因果主张——事实上与证据相反——基于一个关于编程认识论的古老哲学主张,也许最好的做法是让这场愤怒的受害者自己说最后的话:
……对于那些说"我是 xyz 大学的博士,我告诉你 LLM 只是胡乱生成东西的随机工具,如果你使用它们世界就会崩溃"这样话的人,我在这里告诉你,你已经过时了。在过去的几个月里,软件工程的世界已经发生了巨大的变化。IT 安全和在面对报告洪流时维护软件的世界在仅仅过去的几周里已经彻底改变了。你去年学到的关于这类东西的任何知识可能已经过时……底线是我确实知道(大致!)LLM 是如何工作的,但这不会让它们没有用处。这确实意味着你必须谨慎,而我正在谨慎——或者在我想要航海而不是处理所谓互联网专家的垃圾洪流的愿望下尽可能谨慎。