救命!Cursor写的代码总飘着一股“粪围感”

大模型浪潮之下,AI编程工具如Cursor、WindSurf、Trae等如雨后春笋般涌现。“一句话生成应用”、“告别手写代码”、“开发者终将失业”的论调此起彼伏,仿佛开发者已站在失业的悬崖边缘。然而,现实果真如此吗?AI生成的代码,是否真能“拎包入住”生产环境?一旦投入使用,后期的维护迭代,又该由谁来负责?这些问题,如同房间里的大象,我们不得不正视。

AI编程:天使与魔鬼的结合体?

不可否认,诸如Cursor这类的AI编程工具的价值毋庸置疑。它们能将开发者从大量重复性、模式化的编码劳动中解放出来,让我们能更专注于核心业务逻辑的梳理与创新设计。在面对不熟悉的领域或技术栈时,AI也能助我们一臂之力,快速生成原型代码,降低上手门槛。这无疑是“天使”的一面。但是,随着对Cursor等工具的深度使用,一种难以言喻的“粪围感”也开始在我们的代码库中悄然弥漫。

“粪围感”从何而来?

所谓“粪围感”,并非指单一、孤立的代码缺陷,更像是一种综合性的“气质”——指AI生成的代码在结构设计、逻辑清晰度、可读性、以及长期可维护性等方面,所普遍流露出的一种“粗糙”、“欠打磨”甚至“混乱无序”的直观感受。这种感受一旦弥漫开来,整个代码库便仿佛失去了应有的清爽与优雅。笔者就日常使用Cursor当中所遇到的情况做一个分享[注:Cursor使用的配置为Claude3.7模型以及Ask模式]。

1.过多的If-Else条件判断

生成的代码中充斥着深层次的、嵌套的 if-else 语句。过多的分支显著增加了代码的独立路径数量(即高圈复杂度),这会严重损害代码可读性及增加测试难度(测试用例数量呈指数级增长)。同时维护者需要花费大量精力追踪逻辑流,在后续的版本迭代当中容易引入错误(特别是当需要新增条件分支时)。

那么,为何像Cursor这样的AI工具会如此“偏爱”这种深层嵌套的复杂结构呢?一个可能的解释是,大模型在学习过程中,更倾向于模仿其训练数据中海量的、直接通过条件判断处理各种情况的常见代码模式。在更高层次的“设计感”上则有所欠缺,难以有效识别逻辑中潜在的“状态”或“策略”抽象点,以更优雅的方式撰写代码。例如,下图用于静态分析的C++代码,其中包含多层if条件判断。这不仅增加了代码的圈复杂度,也影响了其可读性。此时,可以使用卫语句-在函数或循环的开端,优先处理所有前提不满足或无效的状态-使得主干逻辑不再需要层层缩进,从而提升代码的简洁性。例如将 if (condNode) {...} 的主逻辑嵌套,改为 if (!condNode) continue。

再比如,下图用于文件解析的Python代码,其中包含多重的循环与标签处理逻辑。该代码就仿佛是Cursor通过最"原始"的条件堆砌方式实现而来。

此时,该Python代码可以通过子函数进行重构:将循环体中处理不同tag的代码块,拆分为独立的、职责单一的处理函数(例如 _parse_table_diff())。这种重构不仅可以提升代码的可读性,更重要的是可以增强代码的可维护性。当需要新增标签支持或修改现有标签逻辑时,开发者只需关注对应的单个处理函数,而无需深入理解复杂的主循环结构。这有效降低了意外破坏其他标签逻辑的风险,同时简化了代码审查和测试的负担。

2.逻辑重复与上下文管理缺失

生成的代码当中,相同的或高度相似的代码片段(尤其是条件判断和核心处理逻辑块)在多个地方重复出现。例如,下图中10个相同的条件判断,均来自同一个getPointToInfo函数。对于这类重复的代码,Cursor 并没有将其提炼成单独可复用的模块。在实际使用Cursor的过程中发现,它采用纯粹的需求驱动生成模式,而非设计驱动的代码构建方式。具体表现为:Cursor会通过最简单粗暴的方式来实现开发者所提的需求,而不会对主动将重复逻辑重构为可复用的函数或模块。

此外,因AI缺乏有效的上下文感知,对同一状态、权限或空值的重复检查也屡见不鲜。例如,下图中反复对于paramNode的可空性检查[注:在进行如下函数生成时,将上游函数数也作为上下文传给了Cursor]。然而,paramNode在传入之前如下函数之前,已经被严格验证为非空。这种过度依赖分散的防御性检查,表明Cursor可能对于变量的状态流转缺乏清晰的认知。

如何去代码的“粪围感”?

面对AI所生成的“粪围感”代码,我们应当采取辩证的态度:既不因缺陷而全盘否定,也不盲目接受将其直接引入生产环境。作为软件工程实践者,应当将AI定位为"智能副驾驶"——它能够显著提升开发效率,但必须处于工程师的严格监管之下。笔者在开发静态分析工具的初期,曾深陷“粪围感”泥潭。由于缺乏相关领域知识,向Cursor提交了模糊的上层需求(例如“获取指针的指向性信息”)。结果生成的代码往往“粪围感”浓烈。更糟糕的是,当代码出错时,因自身也不清楚问题根源,只能寄希望于AI自行修复,从而陷入低效甚至无效的“修复循环”。转折点在于深入学习和实践:当笔者系统掌握了静态分析知识,并对任务目标有了反复的、更清晰的认识之后,才真正具备了有效驾驭AI的能力,能够精准评估、修正和优化其输出,跳出了此前的恶性循环。这段经历给了笔者关于Cursor使用的如下三点启发。

1.AI生成代码 ≠ 生产就绪代码,它只是初稿或灵感来源。对于AI编程工具生成的代码,开发人员要做好Code Review 和 Refactoring。 应该在确保完全理解AI所生成的代码逻辑之后再运用到实际生产当中,后期维护奠定坚实基础。对“黑盒”代码的盲目信任是项目维护的噩梦之源。

2.原子化需求,掌控架构与接口契约。面对上层抽象或不具体的任务需求,工程师需主动进行原子化拆分,将其分解为职责单一、边界清晰的原子性需求。原子化需求不仅使生成结果更可控,更能在代码出现问题时,帮助我们精准定位根源。此外,在AI辅助编码的全过程中,系统架构设计、模块边界划分、关键数据结构定义等核心决策,必须由工程师牢牢掌控,不可假手于AI。例如,清晰规定方法签名(输入参数类型/名称、返回值类型)及预期行为。最后,为防止AI对代码进行过度或不恰当的修改,应将其任务严格限定为实现单一职责的原子操作(即具体接口的内部逻辑实现)。明确禁止AI参与接口设计或处理跨模块的复杂交互——这些核心设计职责必须保留在工程师领域。

3.精准上下文管理。提供高相关度、精确的上下文是有效生成的前提,模糊的上下文必然导致“粪围感”输出。明确告知AI编程工具你的意图、修改目标和相关依赖。这包括目标函数、调用者、被调用者、关键数据结构/类的定义,以及所使用的框架信息(版本间的API差异可能是AI出错的常见原因)。

如上图,在对函数参数指针操作进行静态分析时,切忌将复杂的分析需求一次性抛给Cursor并期望其完整实现。这种“黑盒式”的依赖注定难以产出可靠结果。取而代之的是,将复杂分析拆解为具体的原子性任务。上图中期望Cursor生成一个“判断函数参数指针是否执行过free操作”的函数。虽然初次生成失败了,但得益于需求的原子化拆分,错误被有效隔离在了该函数内部。然而,在后续尝试让AI自行修复时,由于缺乏精确的错误修复上下文,修复始终未能成功。关键的转折点在于工程师的深度介入:笔者通过深入理解代码的实现逻辑,精准提供了修复所需的关键信息,最终引导Cursor成功修复了错误。

拥抱AI带来的效率红利,同时清醒认知其局限。通过工程师的深度领域知识、清晰的设计主导权、严格的审查流程以及对AI任务/上下文的精准控制,我们能有效祛除“粪围感”,将AI这个强大的“智能副驾驶”转化为软件工程实践中的可靠助力。

作者:袁志强

复旦大学CodeWisdom团队2023级博士生

导师:彭鑫教授 娄一翎青年副研究员

研究方向:基于大模型的智能化软件开发

CodeWisdom

一个有知识的软工公众号

发现智能化编程之道

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值