
最近几周一直在使用 Trae
,利用其基于 React 19 + Next.js 的 SSG 模式生成过电商网站,也正在使用 Trae 做大模型云平台的开发(管理 MCP 的部署、大小模型的部署以及知识库等)。随着和 AI 合作编程的时间增加有了一些心得。
心得一:AI 是效率杠杆,而非培养对象
不要把 AI 当成需要培养的下属,指望它通过"学习"来逐步改进。对于简单的任务,比如一行调整,最好自己动手完成。AI 是工具,它的价值在于帮我们更快完成工作,而不是被反复"调教"去处理那些琐碎、非标准化的细节------你教了这次,下次它大概率还是不会。优化模型是开发者的责任,不是使用者的任务。花时间"训练" AI 适应非标准需求,往往只是浪费时间,下次遇到同样情况,你很可能还要从头再来,这会导致效率非常低,而你也会非常得 Frustrating 。
记住:AI 是效率杠杆,而非培养对象。
心得二:不要人云亦云
关于 rules 不要人云亦云。比方 project_rules.md 网上很流行根据《重构》这本书的核心要求形成 Rule,但是你的项目真的需要这些吗?你的 AI 真的这么"笨"吗?
我们举几个例子:
md
### 1. 神秘命名
- **问题**:变量、函数、类或模块的名称不能清晰表达其用途和含义
- **处理**:重命名为具有描述性的名称,使代码自解释
- **示例**:将`fn p()`改为`fn calculate_price()`
AI 命名明明已经比我们人类好太多了,fn p() 这种单字母神秘命名是"你我"懒惰为之,但 AI 已经吸收了开源社区此方面的优秀实践,而且 ta 对英文甚至全世界各国语言或者知识储备足够丰富让其能够在当下的代码环境下生成最适合最具描述性的名字,可以说计算机上两个最著名的历史难题已经被 AI 攻克了一个。而你的冗余指导只会浪费 token 和 CPU 资源。
还有这些 AI 不会发生的问题:
md
### 5. 过长参数列表
- **问题**:函数参数过多,难以理解和使用
- **处理**:引入参数对象,将相关参数组合成对象
- **示例**:将`fn create_user(name, email, phone, address, city, country)`改为`fn create_user(user_info: UserInfo)`
你见过 Trae 会生成这种 5 个甚至更多参数的函数吗?
对话引导,分而治之
这面这些规则,人应该先谙熟于心,然后通过合适的对话技巧指导 AI
md
### 3. 过长函数
- **问题**:函数过长,难以理解和维护
- **处理**:提取函数,将大函数分解为多个小函数
- **示例**:将200行的处理函数分解为多个职责单一的小函数
### 4. 过大的类/结构体
- **问题**:类或结构体承担了过多责任,字段和方法过多
- **处理**:提取类,将相关字段和方法组合成新的类
- **示例**:将User类中的地址相关字段提取为Address类
### 6. 发散式变化
- **问题**:一个类因为多种原因而被修改
- **处理**:按照变化原因拆分类
- **示例**:将既处理数据库又处理业务逻辑的类拆分为两个类
### 7. 霰弹式修改
- **问题**:一次修改需要改动多个类
- **处理**:将相关功能移到同一个类中
- **示例**:将分散在多个类中的订单处理逻辑合并到一个OrderProcessor类
### 8. 依恋情结
- **问题**:一个函数对其他类的兴趣超过自己所在的类
- **处理**:移动函数或提取函数
- **示例**:将过度使用另一个类数据的方法移动到那个类中
这些总结而言即 SRP(Single Responsibility Principle),何时 AI 会出现这个问题,当你试图让 AI 生成一个完整应用 的时候,这倒是可能发生。但其实这是问题是"重构期 "才应该关注的事情,项目从 0 到 1 最重要要的是功能正确(POC & MVP),这个阶段我们是允许违反 SRP 甚至允许重复代码(DRY - Don't Repeat Yourself),也允许代码 warning 和一部分的 To Implement 和 TypeScript 类型问题等"不完美"存在的。
- POC - Proof of Concept(概念验证)不计较质量:代码可能很粗糙,没有用户界面,只是一个 demo 或原型,唯一的目标是证明"能做"。
- MVP - Minimum Viable Product(最小可行产品)
当然过了从 0 到 1 ,往一个老代码库新增代码时候我们需要遵守 SRP 和 DRY,这个规则应当是在我们事先已经做好了"粒度适中"的组件划分,然后对话过程以 prompt 的形式告知:
请生成一个函数/组件,实现 F1 功能,在页面 P1 的 X1 处引入。
其实就是我们人应该首先具备组件化思维(底层是 SRP 和 DRY)。
模棱两可,AI 无法决策
还有一类问题具备主观性。比如:
md
### 10. 基本类型偏执
- **问题**:使用基本类型表示有特定含义的数据
- **处理**:使用小对象替代基本类型
- **示例**:用PhoneNumber类替代表示电话的字符串
如果 AI 按照这个思路严格执行,Ta 会将所有的 string number boolean 都视为"特定含义的数据",因为 AI 很难理解"特定含义"甚至人类都对其模棱两可。"使用基本类型表示有特定含义的数据"确实是好的实践,但只是在关键的地方才需要。比如业务存在多个 id,都是 string,就很容易传错却浑然不知,如果将 id 用 TypeScript 的 Tagged type 或 Brand type 区分这种问题就能在写代码的那一刻就消除!
ts
import type {Tagged} from 'type-fest';
type AccountNumber = Tagged<number, 'AccountNumber'>;
type AccountBalance = Tagged<number, 'AccountBalance'>;
更多阅读
- 关于 AI 的一些实践,比如我的 project_rules,详见我的 AI 工作流 ------ project_rules.md 代码规范篇,让 AI 自省自动跑起来。
- 重构 rules 详见 zhuanlan.zhihu.com/p/193938405...
Tagged type或Brand type解释见我的另一篇文章 TypeScript 类型系统的缺陷:结构化类型之殇,从鸭式辨型到鹅鸭之辨Tagged type或Brand type使用见 sindresorhus 的 type-fest ~ Tagged,这个库周下载量居然达到了惊人的两亿多。