开发者用 AI 和传统方式各构建一个项目,通过真实案例展示 AI 编程对开发效率和代码质量的实际影响。
速度提升与代码所有权焦虑之间的权衡
在实现气象站系统时,我问自己:如果再做一遍,但这次使用 AI,会怎么样?
我的想法是,对比用两种方式实现同一个项目。AI 开发真的像宣传中所说的那么快吗?代码质量如何?使用 AI 时,我会遇到同样的挑战吗?作为一名开发者,我的感受会更好还是更糟?
在本文中,我会尽可能坦诚地讲述使用 AI 重新构建气象监测系统的过程,以及我的真实感受。
一个 Python 程序,负责从传感器读取天气数据、显示当前读数,并将数据发送到 Web 应用。
一个使用 PHP + Symfony 构建的 Web 应用,负责接收气象站的数据,并将其展示在 Dashboard 中。
我不想花钱使用 AI,因此尝试了 Gemini 和 OpenRouter 的免费方案,也尝试在本地运行 Ollama。但 Gemini 和 OpenRouter 的 token 很快就用完了,而 Ollama 在我的电脑上也无法正常工作。
于是,在一位同事的推荐下,我最终选择了 OpenCode,以及它的默认模型 Big Pickle。
必须承认,无论最终结果如何,它整体上运行得相当不错。
你可以在这里查看两种实现方式的源代码:
Horrameteo
Horrameteo AI
Horrameteo Web
Horrameteo Web AI
诚然,重新构建一个项目时,你不再需要反复试验,因为你已经非常清楚自己想要什么。为了抵消这一优势,我尽量假装那些“手工编写”的项目并不存在,同时把自己当成一名“vibe coder”。也就是说,我只想让项目运行起来,不亲自编写任何一行代码,也不关心它具体是如何构建的。
我通过把每个提交过代码的日期按 2 小时计算,估算了投入的时间。
“哇!!使用 AI 快了四倍!!”
但实际情况是,在不使用 AI 的开发过程中,我必须学习 Python,以及 Pimoroni library 的工作方式。一开始,我也不清楚应该如何组织代码。随着逐渐了解如何在 Python 中应用最佳编程实践,我对代码进行了多次重构。
另一个需要考虑的因素,是我在两种开发方式下对版本控制系统的使用方式。使用 AI 时,我的 commit 比不使用 AI 时更少,原因如下:
不使用 AI 时,每完成一个我认为没问题的小步骤,我就会 commit。
使用 AI 时,我会等到一项完整功能能够正常工作后再 commit,因此 commit 更少,但每次包含的改动更大。
对于 Webapp 项目,我大约用了 16 小时就完成了主体开发。剩余时间一直持续到 28 小时,主要用于反复尝试:确定除了当前数值外,我还希望看到哪些天气统计数据,以及这些信息应该如何展示。使用 AI 时,我直接采用了自己最终在非 AI 方案中确定的解决方案。
考虑到这一点,再加上作为一名 vibe coder,我没有花时间审查代码,我的“蜘蛛感应”提醒我:这些数字可能并没有看上去那么惊人。
我使用 Sonarqube 获取了一些代码质量指标,包括可维护性、安全性、可靠性和代码重复率。使用 AI 到底会不会更好?让我们来看一看。
非 AI 项目存在 45 个可维护性问题,原因是没有遵循 snake_case 命名约定。我当时决定使用 Camel Case,这可能是因为我主要是一名 Java 和 PHP 开发者。对我来说,这并不算问题,因此我将它们标记为误报。
我还排除了 Weather Hat display class 中的一些问题,因为它只是 Pimoroni 示例代码的封装副本。我只做了少量修改,让它成为 DisplayInterface 的一个实现。
将这些因素考虑在内,下面是不使用 AI 时的结果:
下面是使用 AI 时的结果:
AI 项目中的可靠性问题来自 factory.py 文件里一个无用的赋值。
在我看来,不使用 AI 时的代码质量似乎更好,但两者之间确实没有太大差异。
至于复杂度和技术债,结果如下。
AI 代码更难理解,复杂度为 87,而非 AI 代码为 19;但非 AI 项目的技术债更高,这是因为我对 Python 缺乏了解。Python 中并不存在接口这一概念,而我的处理方式并不正确。
两者的结果非常相似,但 AI 方案存在 3.6% 的重复代码。
复杂度和技术债的结果如下:
在这些指标上,AI 明显更差:技术债超过非 AI 方案的三倍,代码理解起来也稍微更加困难。
使用 AI 时,我的感受很复杂。一方面,我惊讶于仅凭一组简单的需求就能完成如此多的工作;另一方面,我又感到不知所措,并且相当不信任最终结果。
AI 在几个关键问题上陷入了困境。最终,我不得不亲自指出问题出在哪里,因为它无法找到解决方案。
在 Sensor Reader 项目中,AI 错误地应用了温度偏移量。它没有在读取传感器之前把偏移值传给 Pimoroni library,而是在读取温度之后才应用偏移量。正因如此,相对湿度的数值出现了错误。在经历多轮迭代之后——我不断告诉 AI 结果有误,并要求它阅读 Pimoroni 的文档——最终还是不得不由我来告诉 AI 应该如何修复这个问题。
在 Web 应用中,我遇到了多个与身份验证有关的问题:
第一次运行时,用户身份验证无法工作。AI 不得不一个接一个地修复多个问题,包括 404 和 500 错误、访问登录表单时陷入无限循环等。在其中一次迭代中,AI 甚至建议我移除登录表单里的 CSRF token!
身份验证问题解决后,logout 仍然无法正常工作。由于 AI 始终无法让它运行起来,最终只能由我亲自实现。
引入 JWT authentication 时,AI 删除了基于用户 session 的 authentication!
AI 无法让 JWT authentication 与基于 session 的 authentication 同时工作。它多次坚称问题出在 Apache web server 的配置上,但那其实只是项目为了在 Apache web server 上运行所必需的配置方式。再一次,我不得不亲自让它正常工作。
这个过程非常令人沮丧,我有好几次都被气得不轻。
SDD(Spec-Driven Development,规范驱动开发)的体验同样令人沮丧。
自然语言天生就不精确。真正的开发者可以运用自己的判断力和领域知识,补全 specification document 中缺失的信息,而 AI 只会产生幻觉。为了尽可能避免这种情况,编写 specification 时必须做到极其精确。而恕我直言,这比写代码复杂得多。
最终,我不再让 AI 直接使用 specification file,而是让它一步一步地完成任务。
AI 很适合解决范围有限的任务和问题,例如查找 bug、实现一个接口,或者连接某个 endpoint。根据一份 specification document 构建整个项目,则完全是另一回事。对于范围有限的任务,代码很容易审查,我也能或多或少地对结果负责。但面对大量需要审查的代码时,我会觉得自己对最终结果失去了掌控。
别误会,AI 是一个很好的工具,甚至可以说是一个了不起的工具,但你仍然需要知道如何编写代码。而我所知道的、唯一能让自己更擅长使用 AI 的方法,就是继续亲自面对并解决问题。
关键在于,我们需要弄清楚:哪些问题应该由自己解决,哪些问题可以交给 AI。
对于后续操作,你可以考虑屏蔽此人和/或举报滥用行为。