AI驱动的大型代码重构:Cursor如何实现意图驱动式重构
1. 项目概述:当大型代码重构遇上AI编程助手
“How to Perform Large Code Refactors in Cursor”这个标题乍看是讲一个工具操作技巧,但背后藏着现代软件开发中一个日益尖锐的现实矛盾:业务迭代速度越来越快,而遗留代码库的耦合度、技术债和认知负荷却越来越高。我从2015年开始带团队做后端服务重构,经历过从Spring Boot 1.x升级到3.x的全链路适配,也主导过将单体PHP电商系统拆解为17个微服务的三年战役——每一次大型重构,真正消耗工程师精力的从来不是写新代码,而是理解旧逻辑、识别边界、验证副作用、协调上下游、应对测试失焦。Cursor不是又一个代码补全插件,它是第一个把LLM深度嵌入IDE工作流、支持“意图驱动式重构”的生产级工具。它不替代人做设计决策,但能把人从机械性上下文切换、重复性样板修改、跨文件追踪依赖这些“认知摩擦”中解放出来。关键词里的“Large”很关键——它特指影响50+文件、涉及3个以上模块、需要重审接口契约、可能触发CI/CD流水线连锁反应的重构动作,比如把硬编码的HTTP客户端统一替换为Resilience4j封装的Feign Client,或者将MongoDB聚合管道迁移到Elasticsearch DSL。这类任务在传统流程里往往要开三次以上设计评审、写五页迁移方案、手动改两百行代码再花两天修测试。而Cursor让这个过程变成:你用自然语言描述目标(“把所有UserServiceImpl里调用RestTemplate的地方,替换成通过resilienceClient.execute的包装调用,并自动处理TimeoutException转BusinessException”),它生成可审查的变更建议,你逐块确认,它实时同步更新所有关联文件。这不是魔法,是把工程师最宝贵的注意力资源,重新聚焦到“为什么这么改”和“改完是否安全”这两个真正需要人类判断的问题上。适合谁?不是刚学Java的实习生,而是有3年以上实战经验、正在维护中大型服务、被技术债压得喘不过气的后端/全栈工程师;也不是只写CRUD的码农,而是需要对架构演进负责、要向产品解释“为什么这个需求要排期两周”的技术骨干。
2. 大型重构的本质困境与Cursor的破局逻辑
2.1 为什么“大型重构”在传统流程中必然低效?
先说一个真实案例:去年我们接手一个运行了8年的订单履约系统,核心是Java + MyBatis + MySQL。业务方要求新增“分时段履约优先级”功能,技术评估发现必须重构调度引擎——原逻辑把时间窗口硬编码在Quartz Job里,而新需求要求动态配置、支持多租户隔离、能实时调整权重。按传统方式,我们列出了重构路径:
- 理解阶段:3人×2天 = 6人日,阅读Job类、调度配置类、状态机流转图、数据库表结构,画出依赖关系图;
- 设计阶段:2次评审会,输出UML序列图+接口契约文档,明确新SchedulerService的输入/输出/异常策略;
- 实施阶段:1人主改,2人辅助,手动替换137处调用点,修改9个Mapper XML,重写4个Service方法,补充12个单元测试;
- 验证阶段:本地跑通后,提PR触发CI,因某处未处理的NPE导致集成测试失败,回溯2小时才发现是某个边缘分支的空指针未被覆盖。
整个过程耗时11人日,其中65%的时间花在非创造性劳动上:跨文件跳转查调用链、反复确认参数类型是否一致、手工替换字符串模板、比对新旧SQL差异。问题根源在于传统IDE(IntelliJ/VSCode)本质是“文本编辑器增强版”,它擅长定位(Go to Definition)、重命名(Refactor → Rename),但无法理解“这段代码在业务语义上承担什么职责”。比如orderService.process(order),IDE知道它调用了哪个方法,但不知道这个方法实际在执行“库存预占+风控校验+消息投递”三步原子操作,更无法推断出:如果我要把风控校验抽离为独立服务,哪些地方的调用需要加熔断,哪些地方的异常处理逻辑要前置。
提示:大型重构失败的首要原因从来不是技术方案错误,而是上下文丢失——改A文件时忘了B文件里有个隐式依赖,测C模块时没意识到D模块的缓存策略会因返回值结构变化而失效。
2.2 Cursor如何重构“重构”这件事?
Cursor的核心突破,在于把LLM从“对话机器人”升级为“IDE原生协作者”。它的底层不是简单调用OpenAI API,而是做了三件关键事:
深度IDE集成:直接读取IntelliJ/VSCode的AST(抽象语法树)和符号表,能精确识别
UserService是接口还是实现类、@Transactional注解作用域、@Cacheable的key生成规则。这意味着它生成的代码修改不是字符串拼接,而是基于语法树节点的精准插入/替换/删除。工作区感知(Workspace Awareness):它不只是分析当前打开的文件,而是索引整个项目(可配置范围),构建跨文件的调用图谱。当你指令“把所有DAO层的SQLException转换为自定义DataAccessException”,它能扫描所有
*Dao.java、*Mapper.java,识别出try-catch(SQLException)块,并确保DataAccessException已在common-exception模块中定义且已引入。渐进式执行(Diff-First Workflow):拒绝“一键全自动”。它永远先生成
git diff风格的变更预览(增删改行号、文件路径、上下文代码片段),你点击某一块才能触发实际修改。这解决了工程师最深的恐惧——“我不知道它到底改了什么”。
举个具体对比:传统方式下,要把Spring MVC的@RequestParam参数校验迁移到DTO+@Valid,你需要:
- 手动创建DTO类(复制字段、加注解);
- 修改Controller方法签名(删
@RequestParam,加@RequestBody); - 更新Swagger注解(
@ApiParam→@ApiModel); - 检查所有调用方(Postman脚本、前端AJAX、其他服务Feign调用)是否适配JSON格式。
而Cursor只需一句指令:“将OrderController中所有使用@RequestParam接收参数的方法,重构为接收OrderCreateRequestDTO对象,DTO需包含id、userId、items列表,并添加@Valid注解;同时更新对应Swagger文档说明”,它会在3秒内给出完整diff,包含:
- 新建
OrderCreateRequestDTO.java的完整代码; OrderController.java中方法签名和注解的修改;OrderController.java中对应的Swagger注释更新;- 自动提示:“检测到Feign客户端OrderClient.java中存在同名方法调用,建议同步更新其参数类型”。
这个能力不是炫技,它把重构从“体力活”升级为“决策活”——你不再纠结“怎么改”,而是专注“该不该这样改”“有没有遗漏场景”。
2.3 为什么必须是“Large”?小规模重构用不上Cursor?
这里有个关键认知误区:很多人以为Cursor适合“快速写新功能”,其实它在大规模、高耦合、低文档化的存量系统中价值最大。小规模重构(如单个方法提取、变量重命名)用IDE自带功能足够,因为上下文简单、影响面可控。而Cursor的价值阈值在“需要跨模块理解语义”的场景:
- 领域模型不一致:用户中心的
User实体和订单中心的UserDTO字段名不同(user_idvsuserId),Cursor能基于字段类型、注释、使用位置,智能匹配映射关系; - 技术栈混杂:老系统用XML配置Spring,新模块用Java Config,Cursor能识别
<bean id="userService">和@Bean UserService userService()是同一逻辑,确保重构时两端同步更新; - 隐式契约:某个工具类
DateUtils.format()被20个地方调用,但没人记得它对null输入返回空字符串而非抛异常,Cursor在重构时会标注“检测到17处未判空调用,建议增加null检查”。
换句话说,Cursor不是降低重构门槛,而是把重构的决策成本从O(n²)降到O(n)——n是受影响的文件数,传统方式需要人工建立每两个文件间的映射关系,Cursor用AST+向量检索自动构建这个图谱。
3. 实战拆解:一次真实的大型重构全流程(以Spring Boot服务为例)
3.1 重构目标设定:从模糊需求到可执行指令
我们以一个典型场景切入:将一个使用RestTemplate硬编码调用第三方支付网关的订单服务,升级为使用WebClient+Resilience4j的弹性调用方案。原始代码痛点明显:
PaymentService.java中分散着5处restTemplate.postForObject(url, request, PaymentResponse.class)调用;- 异常处理不统一:有的捕获
HttpClientErrorException,有的直接抛RuntimeException; - 缺少超时和重试:网络抖动时订单卡在“支付中”状态;
- 无法监控:没有调用耗时、成功率等指标埋点。
传统重构会先写技术方案文档,再分步实施。而Cursor要求你用工程师的日常语言直接下达指令,但指令质量决定结果精度。我总结出三条铁律:
- 必须指定作用域:不说“重构所有HTTP调用”,而说“重构
com.xxx.payment.service包下所有使用RestTemplate的类”; - 必须声明约束条件:不说“改成WebClient”,而说“使用
WebClient.builder().baseUrl('https://api.paygate.com').build(),并添加@Retry(name='payment-api')注解”; - 必须定义验证标准:不说“保证功能正常”,而说“所有变更后,原有单元测试
PaymentServiceTest必须100%通过,且新增WebClient调用的@Timeout注解超时时间为3秒”。
最终我的Cursor指令是:
在com.xxx.payment.service包下,将所有使用RestTemplate发起POST请求的方法,重构为使用WebClient.builder().baseUrl('https://api.paygate.com').build(),并添加@Retry(name='payment-api')和@Timeout(name='payment-api')注解;要求:1. 新建PaymentWebClientConfig.java配置类,注入WebClient Bean;2. 原有RestTemplate调用处,改为webClient.post().uri(...).bodyValue(...).retrieve().bodyToMono(PaymentResponse.class);3. 异常处理统一转换为PaymentGatewayException;4. 确保PaymentServiceTest中所有testXXX方法仍能通过。这个指令长度约200字,但它包含了技术选型、配置规范、代码结构、异常策略、测试保障五个维度,是Cursor能精准执行的前提。我试过简化成“用WebClient替换RestTemplate”,结果它只改了1个文件,还漏掉了异常处理——因为LLM无法推断你的隐含约束。
3.2 Cursor执行过程:从Diff预览到分块确认
按下回车后,Cursor开始工作(首次使用需配置API Key和本地模型,推荐Llama 3 70B,响应快且本地隐私可控)。整个过程分三阶段:
第一阶段:上下文加载(3-8秒)
Cursor扫描整个payment-service模块,构建AST索引。它会高亮显示检测到的5处RestTemplate调用点,并在侧边栏列出:
PaymentService.java:45—restTemplate.postForObject("https://api.paygate.com/v1/pay", req, PaymentResponse.class)RefundService.java:67—restTemplate.exchange(...)- ...(共5处)
第二阶段:Diff生成(12-15秒)
生成一个结构化diff视图,按文件组织,每个文件下分“新增”“修改”“删除”三类:
| 文件路径 | 变更类型 | 行号 | 变更内容摘要 |
|---|---|---|---|
PaymentWebClientConfig.java | 新增 | 1-22 | 完整配置类,含@Configuration、@Bean WebClient、@Bean RetryConfig |
PaymentService.java | 修改 | 23-25 | 删除RestTemplate字段注入,新增WebClient字段注入 |
PaymentService.java | 修改 | 45-48 | 替换postForObject为webClient.post().uri(...).bodyValue(...).retrieve().bodyToMono(...) |
PaymentService.java | 修改 | 49-52 | 新增onStatus处理4xx/5xx,转换为PaymentGatewayException |
第三阶段:交互确认(核心环节)
Cursor不会自动写入,而是让你逐块审查:
- 点击
PaymentWebClientConfig.java区块,右侧弹出完整代码预览,你可以直接编辑(比如把baseUrl从https://api.paygate.com改成https://api.paygate.com/v2); - 点击
PaymentService.java的修改区块,它会高亮显示变更前后的代码对比,并标注“此修改会影响RefundService.java中第67行的调用,请确认是否同步更新”; - 如果某处修改你认为不合理(比如它把
@Retry加在了private方法上),可以右键选择“Reject this change”,它会从diff中移除该项。
这个设计极其重要——它把AI的“生成力”和人的“判断力”严格分离。我曾遇到一次,Cursor在RefundService.java中把exchange()方法改成了retrieve().bodyToMono(),但原逻辑需要获取HttpHeaders做签名验证,这种细节必须人工把关。
3.3 关键技术点解析:AST驱动重构的底层原理
为什么Cursor能比传统正则替换更可靠?答案在AST(Abstract Syntax Tree)。我们以一段简单代码为例:
// 原始代码 public PaymentResponse pay(Order order) { String url = "https://api.paygate.com/v1/pay"; return restTemplate.postForObject(url, order, PaymentResponse.class); }传统正则替换restTemplate.postForObject,可能误伤注释或字符串中的相同文本。而Cursor解析后得到AST节点:
MethodDeclaration (pay) ├── ReturnType: PaymentResponse ├── Parameter: Order order ├── Block │ ├── VariableDeclaration (url: String = "https://api.paygate.com/v1/pay") │ └── ReturnStatement │ └── MethodInvocation (restTemplate.postForObject) │ ├── Receiver: restTemplate │ ├── MethodName: postForObject │ ├── Arguments: [url, order, PaymentResponse.class] │ └── ReturnType: PaymentResponse当指令要求“替换为WebClient”,Cursor不是字符串替换,而是:
- 在
Block节点下删除MethodInvocation子节点; - 插入新的
MethodInvocation节点,结构为:MethodInvocation (webClient.post().uri(...).bodyValue(...).retrieve().bodyToMono(...)) - 同时在
ClassDeclaration节点下插入FieldDeclaration(private final WebClient webClient;); - 在
ConstructorDeclaration中插入字段初始化语句。
这种基于语法树的操作,保证了:
- 括号匹配绝对正确(不会出现
webClient.post().uri(后面没闭合); - 类型推导准确(
bodyToMono(PaymentResponse.class)的泛型参数自动继承原返回类型); - 作用域安全(新插入的
webClient字段不会意外覆盖同名局部变量)。
这也是为什么Cursor要求你提供清晰的包路径和类名——它需要准确定位AST的根节点。如果你只说“改所有HTTP调用”,它可能在utils/HttpHelper.java里也执行替换,而那里是工具类,不该耦合WebClient。
3.4 验证与收尾:超越代码层面的完整性保障
重构完成不等于任务结束。Cursor生成的代码只是起点,真正的挑战在验证闭环。我建立了四层验证清单:
第一层:编译通过性(1分钟)
执行mvn compile,检查是否因导入缺失、类型不匹配报错。Cursor通常能自动添加import,但有时会漏掉import io.github.resilience4j.retry.annotation.Retry;,这时需手动补全。
第二层:单元测试覆盖率(5分钟)
运行mvn test -Dtest=PaymentServiceTest,重点关注:
- 是否所有
@Test方法仍通过; - 是否新增了
WebClient相关的测试(如模拟webClient.post()返回特定响应); - 原有
@MockBean RestTemplate的测试是否已更新为@MockBean WebClient。
第三层:集成测试连通性(15分钟)
启动本地服务,用Postman调用/order/pay,检查:
- 日志是否输出
Retrying payment-api call...(验证Retry生效); - 故意向网关返回503,观察是否触发重试(默认3次);
- 调用耗时是否在3秒内(验证Timeout生效)。
第四层:可观测性验证(10分钟)
接入Prometheus,检查指标:
resilience4j.retry.calls{kind="successful",name="payment-api"}是否有计数;http_client_requests_seconds_count{uri="/v1/pay",method="POST"}是否有上报;- 对比重构前后,
payment-api调用的P95延迟是否下降。
这四层验证,Cursor本身不参与,但它在diff预览中会主动提示:“检测到PaymentServiceTest中使用@MockBean RestTemplate,建议更新为@MockBean WebClient”,这就是它作为协作者的价值——不是代替你思考,而是帮你想到你可能忽略的环节。
4. 高阶技巧与避坑指南:让Cursor成为你的重构外脑
4.1 指令工程(Prompt Engineering)的实战心法
Cursor不是聊天机器人,它的指令需要“工程师思维”而非“产品经理思维”。我整理了高频有效指令模板:
| 场景 | 低效指令(别这么写) | 高效指令(推荐写法) | 原理说明 |
|---|---|---|---|
| 接口升级 | “把所有Controller的返回值改成Result ” | “将com.xxx.controller下所有@RestController类中,返回类型为ResponseEntity<?>或Object的方法,重构为返回Result<T>,其中T为原返回类型;要求:1. 新增Result.java通用包装类;2. 原ResponseEntity.ok().body(x)改为Result.success(x);3. 原ResponseEntity.status(400).body(x)改为Result.fail(x)” | 必须明确原类型、目标类型、转换规则、新类位置,否则Cursor会随意泛化 |
| 依赖注入改造 | “用构造函数注入替代@Autowire字段注入” | “将com.xxx.service包下所有@Service类中,@Autowired修饰的private final字段,重构为构造函数参数注入;要求:1. 构造函数必须为public;2. 字段名与参数名一致;3. 移除@Autowired注解;4. 若已有构造函数,追加参数而非覆盖” | LLM容易过度重构,必须用“追加”“移除”“必须为”等强约束词 |
| 日志迁移 | “把System.out.println换成log.info” | “将com.xxx.*下所有System.out.println调用,替换为log.info("xxx"),其中xxx为原字符串;要求:1. 新增private static final Logger log = LoggerFactory.getLogger(ClassName.class);;2. 若类中已有log字段,复用之;3. 原字符串中含变量(如"id="+id),改为"id={}", id格式” | 日志迁移涉及格式转换,必须指定占位符规则,否则会生成log.info("id="+id)这种反模式 |
关键原则:用动词定义动作(替换/添加/删除),用名词定义对象(哪个包/哪个类/哪个注解),用形容词定义约束(必须/禁止/优先)。我试过用“请优雅地重构”这种模糊表述,Cursor直接生成了Lambda表达式替代for循环——完全偏离目标。
4.2 多文件协同重构的黄金法则
大型重构必然跨文件,Cursor对此有独特处理机制。以“将MyBatis XML中的SQL迁移到JPA Repository”为例,涉及三个文件:
OrderMapper.xml(原SQL);OrderRepository.java(新接口);OrderService.java(调用方)。
Cursor的协同逻辑是:
- 先分析
OrderMapper.xml,提取<select id="findOrderByUserId" resultType="Order">SELECT * FROM orders WHERE user_id = #{userId}</select>; - 根据
resultType="Order"和WHERE条件,生成OrderRepository.java中List<Order> findByUserId(Long userId);; - 再扫描
OrderService.java,找到orderMapper.findOrderByUserId(userId)调用,替换为orderRepository.findByUserId(userId)。
但这里有个陷阱:如果OrderMapper.xml中有动态SQL(<if test="status != null">AND status = #{status}</if>),Cursor可能无法100%还原JPA的@Query注解。我的应对策略是:
- 分步执行:先让Cursor生成Repository接口和基础方法;
- 人工补全:对复杂动态SQL,手动编写
@Query("SELECT o FROM Order o WHERE o.userId = :userId AND (:status IS NULL OR o.status = :status)"); - 指令修正:再发指令“将
OrderService.java中所有orderMapper.findOrderByUserId调用,替换为orderRepository.findByUserIdAndStatus,参数保持不变”。
注意:Cursor的跨文件推理能力很强,但不要指望它100%理解业务语义。比如
<where>标签中的AND拼接逻辑,它可能生成findByUserIdAndStatus,但实际业务要求findByUserIdOrStatus——这种决策必须由你拍板。
4.3 常见问题速查表与独家解决方案
| 问题现象 | 根本原因 | 我的解决方案 | 实操心得 |
|---|---|---|---|
| Cursor生成的代码编译失败,报找不到类 | 未正确识别Maven依赖范围,或新类未放入正确包路径 | 1. 在指令中明确写出完整包名,如“新建com.xxx.config.PaymentWebClientConfig”;2. 执行mvn dependency:tree确认resilience4j-spring-boot2已引入;3. 若仍失败,在Cursor设置中启用“Use Maven classpath for context” | Cursor默认只索引源码,不读取依赖jar,必须显式开启classpath感知 |
| 重构后单元测试失败,报NullPointerException | Cursor未识别出原代码中隐式的空值保护逻辑(如if (order != null) { restTemplate.post(...) }) | 1. 在指令中加入约束:“保留所有原if/else条件判断,仅替换内部调用”;2. 使用Cursor的“Explain this change”功能,查看它为何删除了某段代码;3. 对关键分支,手动添加Objects.requireNonNull(order, "order must not be null") | 不要迷信AI的“智能”,对空值、边界值等防御性代码,必须人工加固 |
| Diff预览中出现大量无关文件修改 | 指令过于宽泛,如“重构所有HTTP调用”,Cursor扫描了test/resources/application.yml中的URL配置 | 1. 指令开头强制限定范围:“仅作用于src/main/java/com/xxx/payment/service/目录下的.java文件”;2. 在Cursor设置中关闭“Search in test sources”;3. 执行前用git status确认工作区干净,避免干扰 | 范围控制是Cursor高效的前提,宁可多写10个字符限定路径,也不要省略 |
| WebClient调用后,日志中看不到请求URL和响应体 | Cursor未自动添加ExchangeFilterFunction用于日志记录 | 1. 在指令中追加:“在PaymentWebClientConfig.java中,为WebClient添加ExchangeFilterFunction.ofRequestProcessor(logRequest())和ofResponseProcessor(logResponse())”;2. 提前准备好logRequest()方法代码,粘贴到Cursor聊天框中作为上下文 | Cursor擅长代码生成,但对“最佳实践”的理解有限,需用示例代码引导 |
4.4 性能与稳定性调优:让Cursor跑得又快又稳
Cursor的响应速度直接受本地硬件和模型选择影响。我的实测数据(MacBook Pro M2 Max, 64GB RAM):
| 模型 | 响应时间(中等重构) | 准确率 | 推荐场景 |
|---|---|---|---|
| Llama 3 70B(本地) | 8-12秒 | 92% | 生产环境主力,隐私敏感,网络不稳定时首选 |
| Claude 3.5 Sonnet(API) | 3-5秒 | 88% | 快速原型验证,对中文指令理解更优 |
| GPT-4o(API) | 4-6秒 | 85% | 复杂逻辑推理,如多条件SQL转换 |
关键优化点:
- 禁用不必要的插件:关闭Cursor的“GitHub Copilot”“Tabnine”等同类插件,避免冲突;
- 设置合理的上下文窗口:在
Settings → Model → Context Window中设为32k,太小会导致长文件截断,太大拖慢响应; - 预热模型:首次启动后,先让它处理一个简单任务(如“为UserService.java添加Lombok @Data注解”),让GPU显存预热;
- 定期清理缓存:
Settings → Advanced → Clear Cache and Restart,解决偶发的AST解析错误。
最后分享一个血泪教训:某次重构中,我让Cursor“把所有@Scheduled方法迁移到Quartz”,它真的把@Scheduled(cron="0 0 * * * ?")全部替换成了@QuartzJob(jobName="xxx"),但没生成对应的Quartz配置——因为指令里没提“同时生成quartz.properties”。从此我养成了习惯:任何重构指令,末尾必加一句“请确保所有必要配置文件、依赖声明、测试用例同步更新”。
5. 重构之后:如何让成果持续产生价值
完成一次大型重构不是终点,而是新工作流的起点。Cursor的价值不仅在于“改代码”,更在于它重塑了团队的技术债治理模式。我们落地了三项长效机制:
第一,建立“重构知识库”
每次Cursor执行后,我保存完整的diff记录(含指令原文、生成代码、验证结果),归档到Confluence。例如:
- 文档标题:
【支付网关升级】RestTemplate → WebClient + Resilience4j重构记录; - 内容包含:指令全文、diff截图、测试报告链接、性能对比图表(P95延迟从1200ms→320ms);
- 关键价值:新成员入职时,不用再啃8年老代码,直接看这份文档就能掌握支付调用的全链路设计。
第二,沉淀可复用的Cursor指令模板
我们将高频重构场景固化为模板,存放在团队共享Git仓库:
templates/webclient-migration.prompt:含包路径、超时配置、重试策略等占位符;templates/jpa-migration.prompt:含XML解析规则、Repository方法映射逻辑;- 使用时只需
sed -i 's/{PACKAGE}/com.xxx.payment/g' webclient-migration.prompt,再粘贴到Cursor。
第三,重构即文档(Refactor as Documentation)
Cursor的diff本身就是最精准的技术文档。我们要求PR描述必须包含:
- 原始指令(证明重构目标明确);
- Diff摘要(证明修改范围受控);
- 验证结果(证明功能无损);
- 这样,Code Review不再是“你改得对不对”,而是“你为什么这样改”——把评审焦点拉回到架构决策本身。
我个人在实际操作中发现,最值得投入时间的不是学习Cursor的新功能,而是训练自己用精确的工程语言描述问题。当你能清晰说出“我要把A类中所有调用B.method()的地方,替换成C.service().doSomething(A.param),并确保异常处理策略从try-catch改为统一全局异常处理器”,你就已经掌握了大型重构的七成功力。Cursor只是那个手速极快、永不疲倦、且能记住所有AST细节的搭档。它不会告诉你“要不要重构”,但会确保你一旦决定重构,就再也不会被琐碎的体力劳动拖垮节奏。
