AI辅助编程实践体会

我用ai辅助写代码已经很多年了,从刚开始的FittenCode和通义灵码,到后来的Copilot,再到现在的chatgpt+cursor,都在不断地提升编程效率。也有了一些使用ai的编程实践体会,希望对你有帮助。

频繁提交代码

切记,生成的代码质量始终是不稳定的,无法保证下次执行结果的正确性,在使用过程务必养成习惯频繁提交代码,方便出现问题时随时回滚。我个人的操作习惯大致是:

  • 生成代码后,先做一遍简单的代码 Review,修改变量名、循环结构等基础问题;
  • 在软件上下文中集成调试,初步验证正确性后立即提交代码;
  • 审阅代码设计,重点关注:函数输入输出结构、模块导入导出内容、逻辑与循环分支处理等等,在这个节点通常会持续想法子优化代码,或者自己上手做些小修改,迭代多次持续提交代码,这个过程通常耗时最多;
  • 代码初步稳定后,继续生成单测代码,这个阶段通常需要迭代多次,提交多次,直至单测通过,覆盖率达标为止;
  • 功能代码、测试代码均准备就绪后,提交 PR 准备合码;

这个过程中,每次都可能得到不符合预期的结果,但只要基本达标我都会立即 Commit,后续遇到问题随时 Revert 即可,虽然这会产生大量 Commit History ,如果在意这点在合码前可以使用 Rebase 等操作调整历史记录。

重视 Code Review

其次,建议将更多时间精力放在 Code Review 上,因为生成的代码可能是局部最优,但可能因为缺失相对抽象的全局架构信息,而并不能做到全局最优,例如重复编写组件,或破坏某些约定俗成的架构规则等,日积月累同类代码可能最终导致可维护性、可读性都逐步劣化到无法继续迭代的程度(公正点说,人类智能也会导致这类问题)。

因此非常建议重视建立严谨的 Code Review 文化,理想情况应该是由AI生成尽可能多的代码,尽可能代替人类完成编码工作,再由人类智能负责验证、审查这些代码的合理性,保证结果正确且长期可维护。

更好的工程化体系

如前所述, AI 生成的结果是随机的,无法保证每次都得到正确的结果,每次变更都有可能影响存量代码的稳定性,如果这些变更都必须由人类手动验证,那么测试成本会搞起不下,测试环节会成为工程中新的效率卡点。

更好的方式,应该是投入更多精力建设更稳健的工程化体系,使用自动化工具代替人力完成诸多基础质检任务,例如引入 UT/E2E 完成运行质量测试,CI/CD 中加入 TS 类型检查、ESLint 风格检查等保证代码质量,等等。重点在于,使用更高效、成本更低的方式不断验证 AI 产物质量,更敏捷地应对变化。

使用 AI 友好的技术栈

在使用 AI 辅助编程工具时,应尽可能选用各类 AI 友好的技术栈,以前端为例,如:Tailwind 优于原生 CSS/Less 等;Typescript 优于 JavaScript、CoffeeScript 等;React 或 Vue 等 MVVM 框架优于原生 JS + HTML + CSS;GraphQL 优于 Restful 等。

为什么技术栈之间会出现这种差异呢?关键在于 LLM 底层是基于概率推导实现内容生成的,结果好坏取决于模型质量、训练语料、上下文完整度、Prompt 等诸多因素,单就辅助生产代码这一任务而言,LLM 对技术栈的理解越充分必然效果越好;代码结构越聚焦,推导时信息噪音越低,结果也会越好;业务实现中的特化场景越少,通用规则越多,LLM 需要理解的内容越少,效果通常也会越好;等等。

基于这些维度,我个人总结了几类简单规则可用于辅助评估某种技术栈是否更适用于 AIGC 场景,包括:

社区热度

社区越繁荣,意味着使用者越多,相关的技术讨论、技术资料也必然越丰富,LLM 训练或执行时所能索引的信息就越完整,那么也就越容易推导出较好的结果。举个例子,假设你工作中遇到了某个非常具体而棘手的问题,若刚好有人也遇到过,并将该问题形成的底层原因、解决方案整理成文章并发布到互联网上,若 LLM 运行时能检索到该文章,则可以基于文章内容推导给出最终解决方案;若网络上没有这类信息,考虑到 LLM 并不具备复杂逻辑推理能力,那么大概率无法给出有效的解决方案。

结构化

技术栈本身的结构化、模块化水平越强,则其信息表现形式越是聚焦,越容易被 LLM 正确推导。比如,原子 CSS(如 Tailwind) 就是一个很好的例子,原生 CSS 是通过具体的属性的键值对表达页面元素的视觉效果,而原子化 CSS 则是通过原子类名表达某类样式规则集,信息更聚焦更容易被 AI 理解;并且受层叠规则影响,使用原生 CSS 时,元素样式可能受全局、祖先级元素、多种选择器等层级的样式规则影响,这对 LLM 而言意味着具体信息分散在项目的多个角落,需要消费、理解更多上下文才能推导出正确的结果,相对而言原子化 CSS 框架下,大部分样式信息都聚焦在元素对应的 Class 列表上,信息高度聚焦,推理成本更低,结果也会更可靠。

通用规则优于特化设计

技术栈的设计规范越是通用,则越容易被 LLM 理解,也就越是适用于 AIGC 场景。例如,GraphQL 明显优于 Restful,因为 GraphQL 提供了一套用于描述实体 + 实体关联关系的通用语言规则,足够用于表达绝大多数数据存、取、删、改等常规业务操作,因而对 LLM 而言只需理解这套通用语言规则,配合具体业务领域中的实体与实体关系即可基于 GraphQL 灵活编写出各类数据操作逻辑;而 Restfull 规范则更多聚焦在实体上,除几种基础的数据操作外,涉及复杂数据结构场景时,出于实用性、性能等角度考虑,通常不得不特化设计、特化开发,而这些特化处理对 LLM 而言会显得过于具体,相应的上下文复杂度与噪音也会更高,也就更难以推导出正确的答案。

当然,上述规则仅仅是我的一家之言,随着大模型的迭代发展,具体规则后续必然还会新增或删改,这不重要,重要的是非常建议读者后续在做技术选型时,不要只是基于个人 or 团队喜好做决定,应该更多考虑技术栈对 AI 的适用性,甚至可以以此为首要原则,尽可能选用对 AI 友好的技术与工具,使得 LLM 更好、更准确地辅助完成各类开发任务,充分融入到日常工作中,提升个体与团队的整体效率。

降低预期

对,没写错,你需要降低预期!前面也说过几次,LLM 并不是魔法,它有幻觉,有知识漏洞,有随机性,不擅长解决复杂问题,有各种各样的缺点,从我的使用经验来说,多数时候它还远没有达到我预期的状态,需要我参与到各种代码细节中。

例如,生成单测通常都跑不通,需要各种修改微调测试;生成的文档可能也会缺失某些重要内容,需要调整 Prompt 后多次重试,才有可能达到预期效果;生成的代码,即使在 Cursor 各种上下文引用能力的加持下,也经常会在细节处犯错,导致执行失败。以 LLM 的底层实现逻辑来说,这些结果都太正常不过了。

因此,在当前阶段,建议降低对 LLM 工具的预期,它并不是魔法,无法完全代替人类智能,当下你还是需要认真做好导演角色,引导 Cursor 更准确更好地解决你的问题,认真研读理解仓库已有的代码与 LLM 生成的代码。

更进一步的说,你自身还是需要有比较强的技术能力,理解各类技术底层细节,才能及时发现、修复问题。某种程度上,Cursor 就像一把刀,它的锋利程度取决于你自身的功力。

最后

这几年用下来,此类工具 虽称不上完美,但它确实能完成许多基础而重复的任务,让我把注意力更多聚焦在业务、架构等高纬度,而不必过多关注具体细节;能够在我遇到知识盲点的时候提供真正有意义的提示与帮助;能帮我完成许多重复、低级任务。这不是玩具,而是真正提升开发效率的生产力工具,目前cursor还是我用过最好的 AI 辅助编码工具,建议读者也上手试试。

不过,始终只是辅助生产工具,人类智能才是本体,工具能让你更快、更强、更好,但重要前提在于“你”,不要本末倒置。