
去年冬天接了个私活给一套跑了七八年的订单系统加一个对账状态字段。功能本身半小时的活我却在里面耗了整整两天。真正让我改变工作方式的是第三天早上我换了个思路新功能那部分继续在 Cursor 里写老代码那部分交给 Claude Code 去啃。结果当天下午就提了 PR。从那以后我的机器上就固定跑着这么一套双工具的工作流——Cursor 负责从零到一的产出Claude Code 负责把历史包袱翻译成人话中间靠一份手写的交接文档串起来。IDE 装两个不稀奇稀奇的是你要清楚什么活派给谁以及怎么让它们互相不打架。1. 我为什么把写新功能和读懂老代码拆成两条线1.1 一个真实的下午给十年前的订单模块加个字段需求很简单订单表加一个reconcile_status字段下单时默认PENDING支付回调时改成MATCHED。听起来是三个文件的事。实际打开项目之后是这样的Controller 层有一层拦截器做参数脱敏Service 层继承了一个泛型基类基类里又调了一层模板方法最后落到一个 DaoDao 里的 SQL 不是写在注解里而是由 XML 加上一堆if拼出来的而且同一个字段名在两个不同的 XML 里都出现过其中一个已经被废弃但没删。我盯着这个链路看了四十分钟脑子里全是问号这个字段到底该在模板方法的哪一步塞进去改了基类会不会影响另外十几个子类Cursor 在这种场景下给我的体验是分裂的。我敲下第一行代码它的 Tab 补全跟读心术一样准但当我问它这个基类的模板方法被哪些子类重写过它给的答案里有明显编造的成分——因为它能看到的上下文只有我当前打开的几个文件剩下的靠猜。这不是它不行是任务类型不对。1.2 两类任务对工具的诉求几乎是相反的我把这两类活儿的差别列了个表列完就明白为什么一个工具干不了全部维度写新功能读懂老代码输入规模局部几个文件足够全局要扫全仓起点一张白纸一堆历史决策核心诉求速度、补全、即时预览检索、溯源、证据链输出的东西能跑的代码能信服的结论最容易出的错代码风格不一致幻觉出的事实人该干的活审 diff定边界写新功能是发射动作你要的是快的反馈循环敲一行看到补全Tab 接受跑起来看效果。这个时候任何需要你去终端里敲命令、等它检索、再回头切窗口的动作都是打断心流。读老代码是考古动作你要的是广度和耐心一次把所有相关的文件都读进来顺着调用关系往上往下捋找不到证据就继续找。这个时候编辑器里那点补全能力毫无用处你需要的是一只能替你跑 grep、能一次吞下几万 token 的机械臂。1.3 分工线画在哪里一张判断表具体怎么分我自己用的是下面这张表干了一段时间基本没有犹豫过任务交给谁原因从零写一个新模块/新页面Cursor上下文干净补全效率高补单元测试Cursor有现成的被测代码做参照改样式、调布局Cursor需要即时视觉反馈这个函数是谁调的Claude Code全仓检索 调用链为什么这里要加个 ifClaude Code需要看提交历史和相邻代码老逻辑的重构方案Claude Code先出方案人来拍板两套老代码的行为差异Claude Code需要同时读两边修一个已知的 bug看情况定位用后者改动用前者注意这张表里最容易被忽略的是最后一行。很多人修 bug 时用一个工具从头干到尾结果定位阶段嫌它慢、改动阶段嫌它不准。拆开之后定位的活儿交给 Claude Code它在终端里跑几条检索命令就能把嫌疑范围缩到两三个文件改动交回 Cursor你在熟悉的编辑器里几秒钟改完。2. 环境落地让两个工具在同一台机器上各就各位2.1 Cursor 侧的中文界面与基础设置Cursor 本身就是从 VS Code 分支出来的所以扩展生态和快捷键体系基本通用这一点让上手成本几乎为零。新装完第一件事是切中文按CtrlShiftP打开命令面板输入Configure Display Language选简体中文如果列表里没有就去扩展市场搜 Chinese装那个简体中文语言包重启即可。也可以直接在设置里搜language在 Display Language 那一栏改。我个人的偏好是代码区保持英文界面只有菜单和设置用中文——因为大量的技术名词翻译过来反而增加理解成本。这一点不影响使用语言包是整体的装了就都变了看个人习惯。比语言设置更重要的是三件事补全的开关和强度。Cursor 的 Tab 补全是它的核心能力但如果你在写老代码它会顺着老代码的坏习惯给你补出同样的坏味道。我的做法是改老代码时把Cursor Tab临时关掉或者只在写新文件时开。模型选择。简单补全用小模型跨文件重构用大模型这个差别在响应速度上非常明显。隐私相关的选项。设置里有是否允许把代码用于训练的开关涉及公司项目的机器上这个选项我建议在入职第一天就确认清楚别等到出问题才想起来看。2.2 Claude Code 的安装与终端接入Claude Code 是一个命令行工具走的是在终端里跟它对话、它自己读写文件、自己跑命令的模式。安装方式我当时用的是 npm 全局安装npm install -g anthropic-ai/claude-code claude --version装完之后最容易卡住的地方不是安装本身而是PATH。如果你用的是 nvm 管理 Node 版本npm 的全局 bin 目录不在系统默认路径里终端会提示找不到命令。解决办法是把它加进 shell 配置# 先看看全局 bin 在哪 npm config get prefix # 假设输出是 /Users/you/.nvm/versions/node/v20.x.x # 把它加进 ~/.zshrc 或 ~/.bashrc export PATH$HOME/.nvm/versions/node/v20.x.x/bin:$PATH然后在项目根目录直接敲claude就能起来。它跑在终端里所以你在 VS Code 或者 Cursor 的内置终端里用都行——我更喜欢在 Cursor 的内置终端里跑它这样两个工具在同一个窗口切窗口的成本降到最低。用Esc可以打断它正在做的事CtrlC两次退出。提示如果你的项目在 Windows 上建议在 WSL 里跑这个命令行工具文件路径和权限问题会少很多Linux 那一套命令也能直接用。2.3 一份两边都要读的项目约定文件这是我认为整套工作流里最值钱的一步。两个工具各自都支持项目级的上下文文件——Cursor 侧是.cursor/rules目录下的规则文件Claude Code 侧是根目录的CLAUDE.md。我的做法是写一份主文档两边都指向它而不是维护两份内容。这份文档里我固定放四类东西# 项目约定 ## 1. 技术栈与版本 - 后端 Java 17 Spring Boot 3.x - 前端 Vue 3 Vite - 数据库 PostgreSQL 15 ## 2. 目录职责新人/新工具看这里 - src/main/java/.../controller 只做参数校验和转发不写业务 - src/main/java/.../service 业务逻辑都在这里 - src/main/resources/mapper 所有 SQL 都在 XML不要用注解写 SQL ## 3. 禁止事项 - 不要改动 legacy/ 目录下的任何文件除非明确要求 - 不要引入新的第三方依赖 - 不要重命名已有的 public 方法 ## 4. 命名与风格 - 新字段一律加 biz_ 前缀 - 时间字段统一用 timestamptz为什么要写这份东西因为两个工具最大的共同问题不是能力不够而是不知道你的规矩。它默认会用通用最佳实践来给你建议而你的项目可能有一堆反常识的约定。把约定写下来相当于给两个新来的同事发了一本员工手册后面每次对话都省掉一大段解释。2.4 手不能离开键盘切换动作的肌肉记忆双工具流最大的敌人不是技术问题是切换成本。如果每次交接都要鼠标点三下、等窗口加载用不了两天你就会退回单工具。我固化了几个动作Cursor 里CtrlL打开对话面板、CtrlK做内联改写、CtrlI打开跨文件的 Agent 模式终端里跑claude之后所有提问都是纯文字不需要记额外快捷键用 Cursor 的内置终端跑 Claude Code这样编辑器里改代码和终端里问老代码在同一个窗口Ctrl~就能切焦点。这三个动作练熟之后整个工作流就变成了左边写新代码右边问老逻辑中间靠Ctrl~来回跳。3. Cursor 那一侧从一句话需求到可提交的代码3.1 先让它复述需求再让它动手这一步是我踩了坑之后加上的。以前我直接把需求丢进去它噼里啪啦生成一堆代码我看着挺像那么回事合进去之后发现它理解的需求和我要的不是一个东西——比如我说下单时初始化对账状态它理解成每次查询订单时都重新算一遍对账状态逻辑完全跑偏。现在的固定流程是第一句话不要求写代码只要求它复述。先不要写代码。 请用你自己的话复述一遍下面这个需求并且列出你认为有歧义的地方 需求订单新增对账状态字段下单时初始化为 PENDING 支付成功回调时改为 MATCHED退款时改为 REFUNDED。它复述完我扫一眼就能发现理解偏差。有歧义的地方当场拍掉后面生成的代码质量会高一个台阶。这个动作只花三十秒能省掉半小时的返工。3.2 Tab 补全和 Agent 模式不是一回事很多人把这两个能力混着用结果两边都没用好。它们的适用范围差别很大能力适用场景不适用场景Tab 补全你已经开始写了它在后面接从零生成整个模块内联改写CtrlK选中一段代码局部修改跨多个文件的改动Agent 模式CtrlI新建多个文件、跨文件重构精细的逐行调整我自己的习惯是写新功能时 80% 的时间在 Tab 补全上。因为我对代码结构很清楚就是手速跟不上思路Tab 正好把中间那段机械化的工作接过去了。只有当我需要一次性生成实体类 DTO Service Controller 测试这一整套的时候才会切到 Agent 模式。Agent 模式有个必须知道的特性它会在你没明确授权的情况下顺手修改相邻的文件。我遇到过一次让它加个校验方法它顺手把同一个类里另外三个方法的命名风格统一了——从它角度看这叫顺手优化但对我意味着 review 成本翻倍。应对办法是在指令里写死边界只允许修改 OrderService.java 这一个文件。 不要重命名任何已有方法不要调整任何已有代码的格式 不要修改 import 顺序。新增内容只允许追加。3.3 写新功能时我的提示词骨架用了几个月之后我的提示词基本固定成五个部分。这个骨架对任何语言都通用[目标] 我要实现什么一句话说清楚。 [约束] 必须遵守的项目约定引用项目约定文档里的条目。 [边界] 允许改哪些文件明确不允许碰哪些。 [验收] 什么情况下算完成能编译、测试通过、某个接口返回什么。 [参照] 项目里已有的同类实现是哪个文件照着它的风格来。其中参照这一条最容易被忽略但效果最明显。老项目里往往已经有一个写得很标准的同类功能你把它指出来当模板生成出来的代码风格就自动对齐了不需要你一行行去改格式。3.4 它为什么会顺手改别的文件这个问题值得单独说因为它是最容易让你在 code review 时翻车的点。原因是模型的工作方式决定的它不是只改你指的那一行而是重写它认为相关的一段内容。如果它读到了同一个文件里的其他代码觉得那里有点问题就会一并处理。这不是 bug是能力边界。我的防御手段有三个按成本从低到高写完立刻看 diff在 Cursor 的源代码管理面板里逐块 review看到不该动的地方直接丢弃那个 hunk提交前用git diff --stat扫一眼被改动的文件数量和你预期不符就停下来查把大改动拆成多次小指令一次只让它动一个文件虽然慢一点但可控。提示如果你的项目有严格的 lint 或者格式化配置接完 AI 生成的代码之后跑一遍prettier --write或者spotless:apply能一次性消掉大量格式噪音让 review 时真正需要看的内容浮出来。4. Claude Code 那一侧在几万行老代码里定位那一句判断4.1 老代码最耗时间的三类陷阱啃老代码的难度不在代码量在于它总会用三种方式骗你第一类是命名撒谎。方法叫getOrderStatus()实际返回的是订单的支付渠道编码。这种代码在经历过多次需求变更的项目里到处都是。你在编辑器里搜getOrderStatus跳过去发现返回的东西跟名字完全不搭。第二类是多层包装。一个简单的校验从 Controller 到 Service 到 Manager 到 Helper 到 Util中间每层都只加了一点点逻辑。你在任意一层停下都看不明白它在干什么。第三类是动态拼接。SQL 是字符串拼的、分支是配置驱动的、字段名是反射取的。这类代码你没法靠找引用来定位因为编译器根本看不到这一层关系。这三类问题靠编辑器里的跳转定义和查找引用基本瘫痪。而 Claude Code 的价值就在这儿它能一次读进几十个文件然后顺着你的问题地毯式搜索。4.2 用检索式提问替代通读一遍新手最容易犯的错是把整个模块丢给它然后问这个模块是干什么的。这种问法得到的是一篇通顺的废话——它会把类名和注释串起来编一段看起来很像但你没发验证的描述。有效的问法是把问题变成一个可验证的检索任务无效问法有效问法这个模块干什么的reconcile_status这个字段在哪些文件里被读、在哪些文件里被写全部列出来为什么这么设计找出所有给status赋值为MATCHED的位置逐个说明触发条件帮我重构一下列出调用OrderHelper.buildSql()的所有位置标注每个位置的入参差异有 bug 吗找出所有对amount做乘法的地方检查有没有漏掉精度处理差别在哪前者得到的是一段话后者得到的是一份清单。清单可以逐条验证验证不了的当场标出来继续追。整段话你只能选择信或者不信而信在改造老代码这件事上是奢侈品。4.3 一次完整的溯源排查链路举个我实际遇到的问题。现象是某个订单在对账页显示为MATCHED但数据库里明明是PENDING。我交给 Claude Code 的问题是页面显示的reconcile_status和数据库里的值不一致列出所有可能影响这个字段展示结果的代码路径。它给出的过程大致是四步第一步定位读路径。它在全仓里搜reconcile_status和对应的驼峰命名找到了三个地方一个 Mapper 的 resultMap、一个 VO 的字段、前端的一个格式化函数。第二步检查中间有没有转换。它发现 VO 里那个字段的 setter 被重写过里面做了一层映射把PENDING映射成了MATCHED。原因是有个历史遗留的状态码表两套状态值对应的枚举不同。第三步验证影响面。它搜出了所有使用这个 VO 的接口确认只有对账页这一个地方受影响。第四步给出结论和证据。结论是读路径上的 setter 做了状态映射展示层用的是另一套枚举并且附上了具体的文件路径和行号。整个过程大概两分钟。如果我自己手动查光是搜那些命名变体就得反复试十几次。这四步里最关键的是它每一步都给了可验证的落点——我只需要打开那几个文件对照一眼就能确认结论真假。4.4 让它交方案别让它直接改这是我在被坑过之后立下的规矩在老代码上Claude Code 只出方案不直接改文件。原因很直白。老代码里到处是隐式约定模型看到的只是代码文本看不到那些当年客户提的这个需求、这个分支线上跑着之类的背景。它动手改改出来的东西可能逻辑上没毛病但会破坏你根本不知道存在的约束。所以我的指令里会明确写先不要修改任何文件。 请输出一份改造方案包含四部分 1. 需要改动的文件和具体位置文件路径 方法名 2. 每一处改动的理由 3. 这次改动可能影响的调用方清单 4. 你不能确定的地方单独列出来让它输出方案之后我拿这份方案去对比自己的理解把不确定的条目挑出来继续追问。确认无误之后方案的执行环节我再切回 Cursor——因为在编辑器里改代码、看 diff、跑测试手感比在终端里舒服得多。5. 交接环节上下文在两个工具之间怎么不丢5.1 用一份中间产物当交接单两个工具之间没有共享上下文这是个硬约束。Cursor 不知道你刚才在 Claude Code 里问出了什么Claude Code 也不知道你在编辑器里改到哪一步了。所以中间的传递必须靠人来完成形式就是一份文件。我在项目根目录建了个notes/目录每次改动开一个 markdown结构固定# 任务订单对账状态字段 ## 一、老代码事实来自 Claude Code 的结论已人工核实 - 状态字段存在两套枚举DB 用 order_reconcile展示层用 display_reconcile - 映射发生在 OrderVO 的 setReconcileStatus 里 - 影响接口/api/order/detail、/api/order/list ## 二、改造边界 - 允许改动OrderService、OrderVO、order_mapper.xml - 不允许改动legacy/ 目录、BaseService 模板方法 ## 三、接入点 - 下单入口在 OrderService.create()第 87 行附近 - 支付回调入口在 PayCallbackHandler.handle()第 42 行附近 ## 四、待验证 - display_reconcile 的枚举在哪儿初始化尚未确认这份东西看着朴素但它是整套流程的枢纽。写的时候你被迫把思路理一遍很多模糊的地方在落笔时就暴露出来了。5.2 术语与命名对齐有个细节很容易被忽略同一件事在两个工具里的叫法可能不一样。Claude Code 在描述老代码时用的是旧的术语Cursor 生成新代码时用的是新术语两边对不上后面就会出问题。我的办法是在项目约定文件里加一个术语对照表旧称呼新称呼说明statusreconcile_status老代码里的 status 专指对账不要跟订单状态混check()verify()同名不同义新代码一律用 verifyOrderHelper已废弃新代码不要调用这张表在两边对话时都会被读到能消掉一大半的沟通偏差。5.3 分支与提交节奏老代码改造和新功能开发我强制走两条分支。原因不是洁癖是回滚粒度。老代码的改动一旦出问题影响面往往大得多你需要能干净地回退掉它而不影响新功能的进度。提交节奏上我用的是一步一提交Claude Code 给的方案确认之后我把它拆成若干个独立的改动点每完成一个就提交一次提交信息写清楚做了什么 为什么。这个习惯在后期排查时价值巨大——你可以直接git log看你当时是怎么想的。5.4 模型互相圆谎时的打断方法最后说一个很隐蔽的坑。当你把 Claude Code 的结论转述给 CursorCursor 又基于这个结论生成代码时如果那个结论本身是错的Cursor 不会质疑它会顺着错误结论编出一套自洽的实现。两个工具互相圆谎最后交付的东西看起来逻辑完整实际上建在沙子上。打断链路的办法只有一个回到可执行的事实。具体就是写一个最小的复现——一个单元测试、一段 SQL、一个 curl 请求。能跑通的才是事实跑不通的结论一律打回重查。我在交接单的待验证那一栏里强迫自己至少写一条就是为了防止这种连锁错误。6. 坑单与成本账我把踩过的问题列了一遍6.1 上下文不是塞得越满越好很多人有个直觉给模型的信息越多越好。实际用下来完全不是这样。当你一股脑塞进去十几个文件模型对中间部分的关注度会明显下降问它一个具体问题它可能给你答一个文件开头的内容。我现在的做法是按问题范围控制上下文问一个字段的读写路径就只让它读读写这个字段相关的文件问一个调用链就顺着链子读。真要全仓扫描的时候用检索式提问让它自己去找而不是你手动把文件全贴进去。6.2 大仓库的索引与响应速度仓库一大各种隐式成本就冒出来了。编辑器侧的代码索引在几万个文件上会明显变慢命令行工具每次启动都要重新扫一遍目录结构。几个具体的应对把构建产物、依赖目录、日志目录全部写进忽略规则别让工具去扫它们。抓到一个几万行的dist/目录响应速度直接砍半把历史遗留的、已经不维护的目录单独圈出来在约定文件里写明这些目录不要读别在仓库根目录直接跑工具命令尽量在子模块目录里跑扫描范围小一个数量级。6.3 看起来很对的代码最危险AI 生成的代码有个共同特征语法对、风格对、逻辑自洽但调用的 API 可能不存在。它会根据命名规律编出一个看起来非常合理的方法名比如orderService.fetchReconcileByOrderNo()实际上你项目里叫queryByNo()。防御手段没有捷径就是编译和测试兜底。我现在的流程里有一条硬规定AI 生成的代码在提交前必须过一次完整的编译不能靠肉眼扫。测试能补的尽量补哪怕只补一个最粗糙的冒烟测试也比没有强。6.4 代码外发的三条基本纪律用这类工具绕不开一个问题你的代码要送到哪里去。我的做法是三条第一密钥、配置里的密码、证书、生产环境地址绝不粘进任何对话框。要问就先把这些值替换成占位符再问。第二跟公司确认规则再动手。不同公司对代码外发的要求差别很大入职的时候问清楚一句话的事别自己猜。第三优先用工具提供的隐私相关设置能关掉数据用于训练的开关就关掉。涉及核心算法的部分宁可不问也不外发。7. 这套工作流不该用的几种场合7.1 三五行的改动如果改动范围就是三五行的函数体用这套流程是自找麻烦。你写交接单的时间比改代码的时间还长而且模型看完还要问你一堆确认问题。这种活直接编辑器里手敲两分钟搞定。7.2 性能敏感的热点路径热点路径上的代码模型给的方案往往是能跑但不快。它对常数因子、缓存友好性、内存分配次数这些东西没有直觉生成的循环里可能藏着一个不易察觉的重复分配。这类代码我坚持自己写最多用它帮忙生成测试用例和压测脚本。7.3 你完全不懂的领域这是最重要的一条。如果你对一个领域没有判断力你就无法验证它给的答案对不对。而这类工具的输出永远是自信的——错误的答案和正确的答案语气一模一样。在没有判断力的领域用它等于闭着眼睛开车速度越快越危险。我的判断标准很简单如果我不能在三分钟内看出它给的东西哪里不对那这个任务我就不该交给它。判断力是你自己的工具替不了。用了大半年这套工作流我最大的体会是这两个工具解决的是完全不同的问题把它们硬凑成一个反而互相拖后腿。Cursor 是手负责产出Claude Code 是眼睛负责看清。中间那份手写的交接单看着笨但它是唯一能让两边不跑偏的东西。另外分享一个小习惯每次用 Claude Code 啃完一块老代码我都会顺手把结论写进那份交接单哪怕这次不改它。攒上半年你就有了一个自己项目的考古笔记下次谁再问起这段逻辑你不用再重新查一遍。这个副产品可能比省下来的那点时间更值钱。