Cursor、Copilot 与 Claude Code 结对编程实测对比
原文:https://dev.to/elsie-rainee/i-tried-pair-programming-with-three-different-ai-tools-for-a-month-2nnc(作者 @elsie-rainee)
AI编码工具能在几秒内写出一个函数。更难的问题在于,这个函数是否真的适合放在你的代码库里。它是否遵循了现有的架构?是否能处理边界情况?测试还能通过吗?当问题在三个文件之后才暴露时,AI是能帮忙找出根本原因,还是只会再生成一个补丁?
为了回答这些问题,我花了一个月时间,在处理实际开发任务时,分别使用Cursor、GitHub Copilot和Claude Code作为结对编程工具。这些任务包括:编写代码、调试错误、重构函数、创建测试以及进行跨文件修改。
我测试的不是哪个工具能产出最多的代码,而是哪个工具能真正加速实际的编程工作,而不会在之后带来更多麻烦。
简要结论
在用这三个工具处理了真实的开发任务后,我不会说有一个工具是绝对的赢家。
每个工具在编程的不同环节表现更优:
- Cursor 在专注于AI的编辑器内进行交互式编码和多文件修改方面最为强大。
- GitHub Copilot 在日常编码、自动补全、样板代码和小型函数编写方面最为便捷。
- Claude Code 在需要理解更大代码库、跨文件调试或从终端完成多个步骤的任务时最为强大。
最大的区别不在于它们生成代码的速度,而在于它们在生成代码之前能利用多少有用的上下文信息。
这成为了整个测试中最重要的收获。
我的实际测试内容
我想避免那种常见的AI编码对比,即每个工具都用同一个简单的提示词:
"构建一个待办事项应用。"
这并不能告诉你关于真实开发的多少信息。
相反,我使用的任务更接近于在一个现有项目中正常进行的工作。
任务1:添加一个新函数
我从现有代码开始,让每个工具实现一个缺失的函数。
例如:
async function getUserById(id) {
// 需要实现
}
需求很明确:获取用户数据,处理失败的响应,验证返回的数据,并返回一个可预测的结果。
这测试了一个基础但重要的能力:
AI能否遵循现有项目的编码风格,而不是自创一套?
三个工具都能生成一个可用的起点。
差异在清理阶段显现。
Copilot 在快速生成第一个实现方面非常出色。Cursor 让我更容易引用相关文件,并使函数适应周围的项目。Claude Code 在我想让它先检查类似函数在其他地方是如何实现的,然后再做任何修改时,特别有用。
这种区别在一个现有应用中很重要。
从零开始写代码很容易。
写出适合现有代码库的代码则要困难得多。
调试是一个更好的测试
代码生成并不是我看到最大差异的地方。
调试才是。
我提供给工具的是实际的错误,而不是让它们去发明一个解决方案。
一个典型的任务看起来像这样:
`TypeError: Cannot read properties of undefined
at UserList.jsx:42`
我没有问:
"修复这个错误。"
而是提供了相关的组件、API函数和数据结构,并让工具找出根本原因。
这产生了更有用的结果。
GitHub Copilot
当问题就在我当前编辑的代码附近时,Copilot 表现不错。
如果错误是由缺少空值检查或一个明显的错误变量引起的,它能很快建议修复方法。
当根源在其他地方时,它的局限性就出现了。
我有时不得不手动提供额外的文件和上下文。
Cursor
当相关代码已经存在于项目中时,Cursor 能更好地处理这些情况。
我可以要求它检查组件、API调用和相关类型,并解释数据结构在哪里与预期不符。
这让调试感觉不那么像自动补全,而更像有了第二双眼睛。
Claude Code
当调试任务跨越多个文件时,Claude Code 尤其有用。
它不仅仅关注抛出错误的那一行代码,我可以要求它追踪数据流。
这很有价值,因为许多真正的Bug并不位于应用程序崩溃的地方。
崩溃通常只是最终的症状。
重构:AI 能节省时间与制造时间的地方
重构是另一个有用的测试场景。
我选取了能运行但已变得难以维护的代码,要求每个工具在不改变其行为的前提下进行改进。
例如:
function calculateTotal(items) {
let total = 0;
for (let i = 0; i < items.length; i++) {
if (items[i].active) {
total += items[i].price * items[i].quantity;
}
}
return total;
}
一个简单的重构很容易。
但真实的重构通常伴随着约束条件:
- 不要改变 API。
- 保留现有行为。
- 保持当前的数据结构。
- 不要引入新的依赖。
- 维持测试覆盖率。
- 遵循项目现有的编码规范。
这正是工具们开始表现各异的地方。
Copilot 对于较小的重构建议非常出色。
当我想要进行更广泛的修改并同时检查受影响的文件时,Cursor 表现更好。
当重构需要理解该函数在整个代码库中的使用方式时,Claude Code 就很有用了。
关键部分:审阅差异
这成了我的一条准则。
永远不要在不阅读差异的情况下接受一个大型的、由 AI 生成的重构。
一个看起来更清晰的实现,并不自动等于一个更安全的实现。
AI 可能在消除重复代码的同时,不小心改变了行为。
它也可能去“改进”某些故意那样编写的部分,因为那部分与应用程序的另一个部分有关。
用 AI 编写测试
测试是这三个工具都能为我节省时间的领域之一。
我可以提供一个现有函数,并要求编写涵盖以下方面的单元测试:
- 正常输入
- 空输入
- 无效输入
- 缺失值
- API 失败
- 边界条件
生成的第一组测试通常看起来合理。
但有一个明显的问题。
AI 倾向于根据它看到的实现来编写测试。
这可能导致测试只是确认了代码当前所做的事,而不是证明了应用程序应该做什么。
例如,如果实现中有一个不正确的默认值,AI 生成的测试可能会将该行为编码进去。
所以我停止问:
“为这个函数写测试。”
我得到了更好的结果,通过问:
“根据下面描述的预期行为编写测试。包含边缘情况和失败场景。不要假设当前实现是正确的。”
这个小小的改变产生了更有用的测试。
多文件修改改变了我的看法
当我开始要求工具们不只是编写单个函数时,这些工具之间最大的差异变得显而易见。
我交给它们一个功能需求。
例如:
为现有的用户列表添加分页功能。保持当前的 API 响应格式,添加加载和错误状态,更新 API 请求,保留现有的过滤器,并为新行为添加测试。
现在,AI 需要理解:
- API 请求在哪里发生。
- 用户列表在哪里渲染。
- 当前状态是如何管理的。
- 过滤器如何工作。
- 测试位于何处。
- 哪些文件需要修改。
- 现有的 API 是否支持请求的功能。
这更接近于真实的软件开发。
Cursor
当我希望停留在编辑器内并交互式地指导修改时,Cursor 表现良好。
我可以检查建议的修改,并在过程中调整实现。
GitHub Copilot
Copilot 仍然有用,但我发现对于较大的修改,我需要提供更多的指导。
当我已经知道需要做什么并希望得到实现帮助时,它表现得很出色。
Claude Code
当任务需要在实现之前进行代码库级别的调查时,Claude Code 尤其有用。
这使得它对于大型修改很有价值,因为第一步不是编写代码;而是找出在哪里需要修改代码。
哪个工具需要的修正最少?
这比计算生成的代码行数更难衡量。
我开始关注一个更实际的指标:
AI 完成之后,我还需要做多少工作?
这包括:
- 修正不正确的假设
- 移除不必要的代码
- 修正 API
- 更改变量名
- 添加缺失的错误处理
- 重写测试
- 修复回归问题
- 撤销不必要的更改
这改变了我对生产力的看法。
一个一分钟生成 200 行代码的工具,如果我还需要再花 30 分钟去修正第一个结果,它未必比一个生成 80 行有用代码的工具更快。
对我来说,有用的代码比生成的代码更重要。
我的实际对比
| 编程任务 | 最佳选择 | 原因 |
|---|---|---|
| 行内自动补全 | GitHub Copilot | 输入时提供快速建议 |
| 小型函数 | GitHub Copilot | 低摩擦,快速生成 |
| 交互式重构 | Cursor | 基于编辑器的强工作流 |
| 多文件编辑 | Cursor | 更容易指导和审查修改 |
| 调试简单错误 | GitHub Copilot | 快速提供上下文相关的建议 |
| 跨文件调试 | Claude Code | 更适合代码库级别的调查 |
| 理解不熟悉的代码库 | Claude Code | 用于追踪项目结构很有用 |
| 编写单元测试 | 三者皆可 | 良好的起点,但仍需人工审查 |
| 大型实现任务 | Cursor / Claude Code | 更适合多步骤的工作 |
| 最终代码审查 | 人类开发者 | AI 不应是最终的权威 |
AI 结对编程真正改变了什么
最大的生产力提升并不是我不再编程了。
而是我的编程方式发生了变化。
在大量使用 AI 之前,很多时间花在了:
- 搜索文档
- 查找语法
- 编写重复代码
- 创建测试样板
- 追踪不熟悉的函数
- 构建解决方案的第一个版本
AI 减少了很多这样的摩擦。
但另一类工作变得更加重要:
- 审查生成的代码
- 检查假设
- 测试边缘情况
- 阅读差异
- 编写更好的提示
- 将大任务分解为小需求
所以 AI 并没有消除工程工作。
它只是转移了我花时间的地方。
我必须警惕的错误
一个月后,我对一些反复出现的问题变得更加小心。
1. AI 会假设事情
如果需求不清楚,工具会填补空白。
这可能意味着选择不适合项目的 API 模式、库、命名约定或架构。
2. 能工作的代码可能仍然是坏代码
有些代码可以通过编译、通过基本测试,但仍然不必要地复杂。
3. 测试可能带来虚假的信心
生成的测试套件并不自动意味着良好的覆盖率。
4. 大变更需要更小的检查点
当我把大任务分解成几个阶段,而不是在一个提示中要求整个功能时,我得到了更好的结果。
5. Git 变得更加重要
随着 AI 做出更多更改,审查提交和差异变得至关重要。
我想确切地知道什么变了以及为什么。
最适合我的结对编程工作流
最可靠的工作流出人意料地简单。
步骤 1:解释现有代码
在做出任何更改之前,给 AI 相关文件,并让它解释当前行为。
步骤 2:定义需求
确切地说明什么应该改变,什么必须保持不变。
步骤 3:请求计划
对于更大的任务,让 AI 在编写代码之前识别哪些文件需要修改。
步骤 4:分小块实现
不要无条件接受一个巨大的更改。
步骤 5:审查差异
检查每一个有意义的修改。
步骤 6:运行测试
永远不要仅仅因为生成的代码看起来正确就认为它完成了。
步骤 7:让 AI 质疑自己的解决方案
一个有用的提示是:
"审查这个实现,检查边缘情况、回归问题、不必要的复杂性和可能不正确的假设。"
这通常能发现我没有注意到的问题。
那么,我会选择哪个 AI 结对程序员?
如果我今天开始一个项目,我不会仅基于基准分数或功能列表来选择。
我会基于我的工作流来选择。
- 用于日常编码和自动补全: GitHub Copilot。
- 用于以编辑器为中心的工作流,提供交互式 AI 辅助: Cursor。
- 用于仓库级调试、调查和更大的终端任务: Claude Code。
但有一个重要的注意事项。
我不会让它们中的任何一个成为最终决策者。
AI 可以建议实现方式。
我仍然决定实现是否正确。
这就是将 AI 用作结对程序员与用作代码生成器的区别。
结论
在使用三种不同的 AI 工具进行结对编程一个月后,我得到了一个不那么令人兴奋但更有用的答案:AI 并没有让编程消失;它只是让编程的某些部分变得极其快速。
当我需要快速协助编写代码时,GitHub Copilot 非常出色。当工作涉及交互式编辑和多文件时,Cursor 变得更有用。当我需要调查仓库、追踪问题或通过终端处理更大的任务时,Claude Code 脱颖而出。
真正的生产力提升来自于将 AI 生成与正常的工程纪律相结合。
我仍然阅读代码。
我仍然审查差异。
我仍然运行测试。
我仍然调试故障。
我仍然做出架构决策。
这是今天思考 AI 结对编程最现实的方式。目标不是让 AI 在你闲坐时编写整个应用程序。目标是消除重复工作,缩短从想法到可工作实现的距离,并为你提供另一个工具来思考困难的编程问题。
最好的 AI 结对程序员不是编写最多代码的那个。而是帮助你花更多时间解决工程问题,更少时间对抗重复实现工作的那个。
原文:https://dev.to/elsie-rainee/i-tried-pair-programming-with-three-different-ai-tools-for-a-month-2nnc(作者 @elsie-rainee)