作者提出AI-native开发工作流原则:能由脚本可靠完成的任务(如Prettier格式化、ESLint规则检查)不应委托给AI,应让AI专注于真正需要推理的工作,避免浪费上下文和推理能力。
我摸索出了一套工作流程,我称之为:
半自动化、AI 原生开发流程,人始终参与其中。
听起来像是一堆 AI 流行词汇的堆砌,但每个部分都代表着一个非常实用的原则。
如果一项任务可以由一个简单的脚本可靠地执行,就不应该委托给 AI。
想让代码格式始终统一?配置 Prettier。
想要自定义命名规范、导入限制或架构约束?配置 ESLint 或编写自定义规则。
想要在不同编辑器间保持一致的格式?添加一个 EditorConfig 文件。
不要让 AI 把上下文和推理能力浪费在检查缩进、分号或命名规范上。确定性的问题应该有确定性的解决方案。
我为当前项目写了几千个单元测试。作为一个完美主义者和前 QA 工程师,我关心测试中元素的选择方式。
只要可行,测试应该优先使用可访问的选择器,比如角色和可访问名称。测试 ID 只应作为后备方案。
起初,我把这当作 AI 应该记住的东西,在代码审查时再纠正错误。
但我注意到这类问题被纠正的频率太高了。
为什么要反复审查那些可以自动检查的东西?我写了一条自定义 lint 规则。
几行代码就永久解决了这个问题。
现在 lint 工具知道了需要的选择器优先级,并自动报告违规。
最棒的是,AI 在几乎所有情况下都能修复这些问题。
因为在我的工作流程中,运行项目的验证流程是强制性的。即使 AI 犯了错,它也会运行 linter,收到精确的错误信息,然后修正代码。
这就是我对"半自动化"的理解:
AI 处理需要推理的任务。脚本处理需要一致性的任务。
一个代码库可以主要为人方便而组织,也可以组织得让人类和 AI 都能高效地导航。
AI 原生的代码库将上下文视为有限的工程资源。
有些开发者熟悉代码高尔夫:用尽可能少的字符解决问题。
AI 原生开发并不完全是代码高尔夫,但背后的约束是相似的:
每个 token 都有成本。
这并不意味着把代码压缩到无法阅读的程度。而是在可读性、一致性和上下文效率之间找到平衡。
项目标准化在这里扮演着重要角色。
一个组件不应该在没有充分理由的情况下,与另一个组件使用完全不同的结构、风格或范式。
可复用的抽象应该被明确定义并一致地使用。
这样 AI 就能找到一个现有的实现,理解它作为项目标准,并将其作为可靠参考。
另一个重要原则是我所说的上下文隔离。
一个功能应该被拆分成有意义的文件,这样 AI 就可以只加载当前任务所需的上下文。
例如,一个测试实现可能有:
有时候 setup 比实际的测试套件还要大。
当我只需要 AI 检查测试名称或了解覆盖了哪些行为时,没有理由把整个 setup 都塞进它的上下文。
我用同目录的 suffix 文件来解决这个问题。一个较大的功能可以在它旁边放置多个支持文件,用清晰的 suffix 描述各自的角色。
根据任务的不同,我可以给 AI 提供它恰好需要的文件——不多也不少。
还有,请在使用桶导出(barrel exports)有意义的地方使用它。
当一个稳定的公共模块 API 可以用更少的 token 和更少的耦合暴露相同的组件时,没有理由反复给 AI 喂送冗长的绝对导入路径。
互联网目前似乎痴迷于完全自主的管道——让一个 agent 全天候编写代码,无需人类参与。
也许我过时了,但我对这种方法高度怀疑。
我可以列举很多 AI 误解的例子、必须审查的东西、以及自主实现失败的地方。
但有一个问题比所有其他问题都重要:
如果你把自己从流程中移除,你就会停止学习。
你不再收集反馈。
你不再审视 AI 理解了什么、误解了什么。
你看不到哪些指令有效,哪些造成了困惑,AI 在哪里快,在哪里吃力,或者在哪里添加工具可以消除一整类错误。
最重要的是,你丢失了改进自己开发框架所需的数据。
这是我最喜欢的例子。
有一段时间,我创造了自认为完美的 AI 开发流程。
我反复审查每一条指令。打磨每一个细节。我确信这个框架设计得异常出色。
然后我在生产环境中使用它。
不到两个月,这个流程经历了五代根本性变革——这还不算数十次小的改进。
因为真实世界的使用产生了反馈。
我观察到什么失败了,什么造成了不必要的劳动,AI 错误理解了哪些内容,以及什么可以更有效地自动化。
无论你的提示、指令集、agent 配置或 MCP 设置看起来有多好,在真实生产条件下观察之前,你都没有客观证据证明它有效。
这就是为什么我的工作流程让人类参与其中。
不仅仅是批准 AI 生成的代码。
而是从每一次迭代中学习,不断改进产生这些代码的系统。