用AI写业务代码后,必须要坚持自己做的几件事情——过程控制

0 阅读16分钟

前言

AI 已经可以非常快速地帮我们实现业务代码了。

这是非常明确的,并且经过反复验证过的结论。

但越是这样,越会逐渐演变成失控,于是想到哪写到哪,总想让 AI 一口气全部写完。

业务代码和 demo 最大的区别在于:业务从来不是孤立存在的。

它背后有一连串的业务流程、历史包袱、数据状态、权限边界、团队规范、上线风险和长期维护的成本。

所以,用 AI 写业务代码,重点从来不是怎么让 AI 多写,而是如何让 AI 在受控的范围内写对 (此处加粗) 。

下面整理了一些实践经验,既来自我自己的真实经历,也来自和许多朋友深入交流后的思考与总结。希望这些内容能对大家有所帮助。

538.gif

一、先理业务流程,再让 AI 写代码

我发现不少人用 AI 写业务代码时,总是很笼统的把需求丢给 AI,比如下面这样:

帮我实现一个订单退款功能。
帮我写一个审批流。
帮我加一个导入导出模块。

结果 AI 做出来的东西往往不尽如人意,继而演变成生气上火,乃至和 AI 对喷。

因为 AI 生成代码时,往往会默认很多业务假设。

比如退款流程里,它可能默认订单只存在“已支付”和“已退款”两种状态,它并不了解真实的业务流程和场景。

这些假设一旦和真实业务不一致,代码生成的越多,后面改起来越痛苦,哪怕中途 review 起来也会非常痛苦。

更好的方式是:先自己把业务流程理一遍

在写代码之前,至少要搞清楚:

  • 这个功能涉及哪些角色?
  • 每个角色能做什么?
  • 主流程是什么?
  • 异常流程有哪些?
  • 数据状态如何流转?
  • 哪些地方需要权限校验?
  • 哪些地方需要事务?
  • 哪些地方可能失败?
  • 失败后如何补偿?
  • 哪些逻辑是历史兼容?
  • 哪些规则未来可能变化?

这一步,其实也可以让 AI 来协助,但不能完全交给 AI。

可以提前让 AI 帮我们整理成资料,但真实性的判断必须由我们亲自负责。

下面几个章节,我们就列举需要哪些资料来辅助开发。

二、先输出流程图、时序图

在让 AI 写代码之前,最好先让它输出结构化的材料,比如:

  • Mermaid 流程图
  • Mermaid 时序图
  • 状态流转图
  • 数据结构草案
  • 接口设计草案
  • 异常路径说明
  • 落地步骤拆分

例如可以先让 AI 输出一个业务流程图:

flowchart TD
    A[用户发起退款申请] --> B{订单是否可退款}
    B -- 否 --> C[返回不可退款原因]
    B -- 是 --> D[创建退款申请]
    D --> E[冻结相关金额或资源]
    E --> F[调用支付渠道退款]
    F --> G{渠道退款是否成功}
    G -- 成功 --> H[更新订单和退款状态]
    G -- 失败 --> I[记录失败原因并进入重试或人工处理]
    H --> J[通知用户]

再输出一个时序图:

sequenceDiagram
    participant User as 用户
    participant API as 业务服务
    participant DB as 数据库
    participant Pay as 支付渠道
    participant MQ as 消息队列

    User->>API: 提交退款申请
    API->>DB: 校验订单状态
    DB-->>API: 返回订单信息
    API->>DB: 创建退款单
    API->>Pay: 发起退款
    Pay-->>API: 返回退款结果
    API->>DB: 更新退款状态
    API->>MQ: 发送退款通知事件
    API-->>User: 返回处理结果

不要小看这些 mermaid ,首先它们能强迫你先思考清楚:

这个功能到底怎么跑? 数据状态怎么变化? 哪些节点可能失败? 失败后系统应该怎么办?

同时这些文本化的 mermaid ,也能够让 AI 更轻松的了解业务流程,岂非一石二鸟。

如果流程图都搞不清楚,直接让 AI 写代码,那铁定是就是无序与混乱的熵增。

那 mermaid 整理完了,下一步要做什么。

三、务必先出方案,再做落地

流程图解决的是“看清楚业务”,而方案解决的是“想明白怎么做”。

前者帮我们降低沟通成本 (沟通成本 = token) ,后者决定后续落地是否稳定、可控。

正确顺序应该是:

flowchart TD  
A[需求理解] --> B[业务流程梳理]  
B --> C[方案设计]  
C --> D[风险点确认]  
D --> E[任务拆分]  
E --> F[小步编码]  
F --> G[测试验证]  
G --> H[Code Review]  
H --> I[上线与回滚方案]

尤其是复杂业务,应该先让 AI 输出一版方案,而不是直接生成实现。

一个比较好的提示词是:

先不要写代码。

请先基于下面的需求,输出一份技术方案,包括:
1. 业务流程
2. 涉及的模块
3. 数据结构变化
4. 接口设计
5. 状态流转
6. 异常场景
7. 权限和安全点
8. 测试用例
9. 分阶段落地步骤
10. 可能的风险和回滚方案

确认方案后,再开始分步骤实现。

细心的朋友应该发现了,其实这里面就包含了业务流程的梳理。

这样做的好处是,AI 会先进入设计模式或者计划模式,而不是直接进入Coding。

在 AI 的加持下,再也不必担心代码写的慢了,所以更应该沉下心来把业务理清楚,哪怕起步慢一点,至少是在正确的方向上冲刺。

说句私心话,AI 时代下,写代码本身已经完全不重要了。

程序员真正该抓住的,是业务理解、问题拆解和方案设计能力,这才是我们要掌握的核心能力。

具备这些能力后,AI 才能真正成为我们的放大器,而不是等着让 AI 来取代我们。

四、不要一股脑让 AI 写太多

AI 一次性写太多代码,虽然确实看起来很爽,实际上还是具备很高的风险。

我们自己平时写一点 MVP 可以这么玩,让 AI 一次性跑个几个小时甚至几天,但在公司里写业务代码,还是要慎重。

常见问题包括:

  • 改动范围过大,难以 review
  • 混入不必要的重构
  • 生成了不存在的 API
  • 破坏了原有抽象边界
  • 引入重复逻辑
  • 测试跟不上
  • 出问题时不知道是哪一段导致的
  • 回滚成本高

更好的做法是:小步快跑,分阶段生成

比如下面这种提示词,千万不要再说了:

帮我完整实现订单退款功能。

这种提示词看似直接,实际上范围太大。AI 很容易在不了解业务约束、现有代码结构、历史方案和边界条件的情况下,直接开始自由发挥。

更好的方式,是先指定参考资料,再结合现有代码,让 AI 按步骤推进:

第一步:请参考《订单退款需求文档》和《退款流程设计方案》,结合现有订单相关代码,只分析当前订单状态和退款状态的设计,告诉我需要重点查看和可能修改哪些文件,不要写代码。
第二步:请参考《订单退款需求文档》和《退款单数据结构设计方案》,结合现有数据表结构和迁移脚本风格,只新增退款单相关的数据结构和迁移脚本,不要改业务逻辑。
第三步:请参考《订单退款接口设计文档》和前面确认过的退款单数据结构,结合现有订单接口代码风格,只实现退款申请接口,先不要接入支付渠道。
第四步:请参考现有订单模块的单元测试写法,以及《订单退款测试用例文档》,结合刚刚新增的退款申请接口,补充对应的单元测试。
第五步:请参考《支付渠道接入文档》和《退款失败重试方案》,结合现有支付模块代码,只接入退款相关的支付渠道逻辑,并处理失败重试场景。
第六步:请参考《订单退款需求文档》《退款流程设计方案》和本次所有代码变更,结合现有订单、支付、退款相关代码,检查整个 diff,指出潜在风险、遗漏场景和需要人工重点 Review 的地方。

粒度越小、边界越清晰、参考越丰富,质量控制越轻松。

不要追求一次性生成完整功能,要追求每一步都可理解、可验证、可回滚。

五、必须通过 Git 控制过程

Git 一定是必选项。

也许未来会有更好的控制载体,但目前的情况下, Git 一定是最好的选择,没有之一。

AI 生成代码的速度实在是太快了,如果没有版本控制,很容易出现下面这些问题:

  • 忘了改过哪些文件
  • 不知道某个 bug 是什么时候引入的
  • 想回滚但回不干净
  • 多个方案混在一起
  • 临时实验污染主分支

然后又要抓耳挠腮,又要和 AI 对喷。

所以,在让 AI 大量修改代码之前,一定要保证 git status 是干净的。

每完成一个独立步骤,就做一次原子化提交。

所谓原子化提交,就是一个 commit 只做一件事。

比如下面这种:

feat(refund): add refund order model
feat(refund): implement refund apply API
test(refund): add refund application tests
fix(refund): handle duplicate refund request
refactor(order): extract refundable status check

如果觉得英文比较难理解,也可以用中文的 commit 信息,形式都不重要,能看懂最重要。

千万不要把这些东西混成一个巨大的提交:

feat: update refund logic

这种提交对 review、排查和回滚都不友好。

而且这些代码的提交,可以让 AI 在开发业务的过程中自己去执行。
我们实践下来的经验发现,AI 做的 commit,无论是信息的精准度还是代码的颗粒度,比人类都要来的准确可靠。

当然,让 AI 自己提交代码,并不意味着可以完全放任它随意提交。恰恰相反,越是让 AI 参与开发,越要把每一步改动控制在足够小、足够清晰、足够可验证的范围内。

每一个 commit 最好都对应一个明确的小任务:要么是新增数据结构,要么是补充接口逻辑,要么是增加测试用例,要么是修复某个边界问题。不要让多个目标混在同一个提交里。

因为你需要随时能回答:

这一步 AI 到底改了什么? 这个改动能不能单独验证? 如果线上出问题,能不能只回滚这一小块?

六、worktree 是个好东西

git worktree 非常适合 AI 编码场景。

它可以让你在同一个仓库下,同时 checkout 多个工作目录。

这样你可以让不同方案彼此隔离,而不是都堆在一个目录里,而且可以同时开发多个互不影响的需求,不需要被分支束缚。

例如:

git worktree add ../project-refund -b feat/refund
git worktree add ../project-coupon -b feat/coupon

你可以在一个 worktree 里实现方案 A,在另一个 worktree 里尝试方案 B。

这样做有几个好处:

  1. 更安全: AI 想怎么改都在独立目录里,不会污染当前工作区。

  2. 方便对比方案: 可以让两个worktree同时做一个需求,两个方案可以分别跑测试、看 diff、做 benchmark。

  3. 方便丢弃失败尝试 承接第二点,如果某个方案很糟糕,可以直接删除 worktree 和分支。

  4. 减少上下文混乱 不同任务、不同分支、不同改动互不干扰。

  5. 适合多 Agent 并行探索 如果你使用多个 AI coding agent,worktree 可以天然隔离它们的修改范围。

常见流程可以是:

git status

git worktree add ../project-feature-x -b feat/feature-x-ai

cd ../project-feature-x

# 让 AI 在这里修改代码
# 跑测试
# review diff
# 满意后再合并回主开发分支

现在Codex、Claude Code等工具都已经内置支持了worktree,可以抓紧使用起来。

只有用过才知道有多爽。

七、AGENTS.md 和 CLAUDE.md 非常重要

这也是 AI 编程里面让人非常苦恼的上下文问题。

常见的解决方案有(此处不讨论RAG):

AGENTS.md
CLAUDE.md

它们的作用是告诉 AI:

  • 项目是什么
  • 技术栈是什么
  • 目录结构如何
  • 如何启动项目
  • 如何运行测试
  • 代码风格是什么
  • 哪些文件不能随便改
  • 提交规范是什么
  • 常见业务概念是什么
  • 常见坑有哪些
  • 修改代码前需要先做什么
  • 完成后需要检查什么

一个好的 AGENTS.mdCLAUDE.md,就像是给 AI 的员工手册和绩效指南。

例如可以包含:

# Project Guidelines

## Tech Stack
- Backend: Node.js + NestJS
- Database: PostgreSQL
- ORM: Prisma
- Test: Vitest

## Commands
- Install: pnpm install
- Dev: pnpm dev
- Test: pnpm test
- Lint: pnpm lint
- Typecheck: pnpm typecheck

## Rules
- Do not modify database schema without migration.
- Do not change public API response format without updating docs and tests.
- Always add tests for business logic changes.
- Keep commits atomic.
- Prefer existing utilities over creating new helpers.
- Do not introduce new dependencies without explanation.

## Workflow
1. Understand the requirement first.
2. Propose a plan before editing files.
3. Make small changes.
4. Run relevant tests.
5. Summarize changed files and risks.

这些信息不能太复杂,追求言简意赅。

至少得让 AI 知道项目的基本边界和平时开发的注意事项。

但是这些规则文件也不是无脑追加的。

八、规则文件要持续维护,但不能无限膨胀

AGENTS.mdCLAUDE.md 不是一次性写完就结束了。

在使用 AI 的过程中,你会不断发现:

  • AI 经常误改某些文件
  • AI 经常忘记跑某个测试
  • AI 经常使用错误的包管理器
  • AI 经常新建重复工具函数
  • AI 经常忽略某个业务边界
  • AI 经常把错误码写错
  • AI 经常破坏接口返回格式

这些都应该沉淀进规则文件。

但这样就会带来另一个问题:规则文件很容易越写越长,最后变成一堆没人读、AI 也抓不住重点的杂乱笔记。

因为提示词过载会稀释上下文权重,导致 AI 难以识别真正高优先级的约束。

所以维护规则文件要注意几个原则。

1. 只写高频、稳定、重要的规则

不要把所有细枝末节都写进去。

适合写进去的是:

经常发生的问题
一旦发生影响很大的问题
项目长期稳定的约定
新人和 AI 都容易踩的坑

不适合写进去的是:

一次性的临时需求
某个人的个人偏好
已经废弃的历史说明
过度细节的实现过程

判断标准很简单:

这条规则三个月后还重要吗? 新人看了会少踩坑吗? AI 遵守它能明显减少错误吗?

如果答案是否定的,就没必要写进去了。

2. 把规则写成可执行的指令,不要写散文

AI 更容易遵守明确指令,而不是长篇解释。

不推荐:

我们项目比较重视测试,所以大家在写代码的时候最好注意一下测试相关的问题。

推荐:

- Any business logic change must include or update tests.
- Before finishing, run `pnpm test` for affected packages.
- If tests are not run, explain why in the final summary.

规则要尽量具体:

  • 做什么
  • 不做什么
  • 什么时候做
  • 用什么命令做
  • 例外情况怎么处理

3. 分层组织,不要堆成一大坨

规则文件可以按优先级和主题拆分。

例如:

# AGENTS.md

## Non-negotiable Rules
## Project Commands
## Architecture
## Coding Style
## Testing
## Git Workflow
## Security
## Common Pitfalls

最重要的规则放前面。 因为 AI 的上下文有限,越靠前越容易被注意到。

可以使用这种结构:

## Non-negotiable Rules

- Never commit secrets.
- Never change public API response formats without tests.
- Never modify generated files manually.
- Always keep changes small and reviewable.

4. 用反例记录来常见问题

有些规则只写正向说明不够,最好是加上一两个反例。

例如:

## API Response

Use the existing response wrapper:

Good:
return success(data)

Bad:
return { code: 0, data, message: 'ok' }

AI 极其擅长模仿已有模式。给它明确的 Good / Bad 示例,比抽象描述更有效。

5. 定期清理过期内容

规则文件需要维护,不是只增不减。

建议每隔一段时间检查一次:

  • 哪些规则已经过期?
  • 哪些命令已经变了?
  • 哪些目录结构已经调整?
  • 哪些限制已经不再适用?
  • 哪些内容可以合并?
  • 哪些内容应该删除?

一个好的规则文件应该是高密度的,而不是空有长度。

我们的目标也不是要写成百科全书,而是让 AI 在最短时间内能够抓住最重要的约束。

6. 把经验沉淀成原则,避免流水账

不推荐这样写:

2025-01-03:AI 修改 refund.ts 时忘记处理 duplicate request。
2025-01-08:AI 又忘记处理 duplicate request。

推荐沉淀成规则:

## Idempotency

For payment, refund, order creation, and webhook handling:
- Always consider idempotency.
- Use existing idempotency keys where available.
- Add tests for duplicate requests.

经验一定是抽象成可复用的规则,否则规则文件会变成小学生的流水账,臃肿又没有营养。

九、让 AI 参与 review,而不是只参与 coding

AI 不应该只用来写代码,也应该用来检查代码。

在每一轮修改后,可以让 AI 做几类 review:

请 review 当前 diff,重点检查:
1. 是否偏离需求
2. 是否有多余改动
3. 是否破坏现有架构
4. 是否缺少异常处理
5. 是否存在权限问题
6. 是否有性能风险
7. 是否需要补充测试
8. 是否可以拆成更小的 commit

也可以让 AI 从业务角度检查:

请不要关注代码风格,只从业务正确性角度 review 当前实现。
重点看状态流转、异常场景、幂等、并发、权限和数据一致性。

还可以让 AI 从上线风险角度检查:

请从上线风险角度评估当前改动:
1. 可能影响哪些已有功能?
2. 哪些地方需要灰度?
3. 是否需要数据迁移?
4. 是否有回滚方案?
5. 应该重点观察哪些日志和指标?

写代码只是 AI 的一部分价值。

让 AI 帮助我们审查方案、审查 diff、补测试、找风险,也许会比写代码更有价值。

最后

AI 确实可以提高我们的产出速度,但不能代替我们承担责任(也许更应该把这话发给老板们看)。

业务代码上线后,真正承担后果的还是开发者和团队:

  • 数据错了
  • 订单状态乱了
  • 权限穿透了
  • 性能崩了
  • 线上事故了

这些问题, AI 可不会负责,哪怕我们喊破嗓子说是 AI 搞坏的,老板也只会觉得是我们不会用 AI ,最终还是我们来背锅。