首页 / 资讯中心 / 文章详情

原子化AI编程全栈实战:任务拆解与可验证交付复盘

原子化AI编程全栈实战:任务拆解与可验证交付复盘 ★ FEATURED ARTICLE
原子化AI编程这件事我最初是抱着试试看的心态入场的。真正动手做完一个完整的全栈项目之后我才发现它跟宣传里说的完全不是一回事——它没让写代码这件事消失而是把工作的重心从敲代码整个搬到了拆任务上。这篇文章就是我这套流程的完整拆解和复盘一个带权限体系的任务协作系统前端到后端到部署全部由我一个人推进AI 承担了大部分具体实现。我会把每个阶段怎么切、切多细、切完之后怎么验收、哪些地方 AI 一上手就翻车原原本本写出来。如果你也打算一个人扛下一个全栈项目或者已经在用 AI 写代码但总觉得生成得挺快、收拾起来很慢下面的内容应该能帮你少走几段弯路。1. 一个人做全栈为什么现在真的跑得通1.1 角色从写代码的人变成拆任务的人先说结论原子化 AI 编程能不能成八成取决于拆任务的水平两成才是模型本身的能力。我这个项目里模型换过两次整体产出质量的波动远没有我拆解方式变化带来的波动大。同一套模型我第一周按帮我写个登录功能这种粒度提问拿回来的东西基本没法直接用第三周我按实现一个接收手机号与验证码、校验有效期、返回访问令牌与刷新令牌的 POST 接口输入输出类型如下这种粒度提问一次通过率能到七成以上。差别在哪在于原子这两个字。原子任务不是小任务而是最小可独立验证的交付单元。它有明确的输入契约、明确的输出契约、明确的验收方式以及独立的回滚点。一个任务只要缺了其中任何一条它就不是原子的而是一颗裹着糖衣的雷。我第一周踩的最大的坑就是把一个页面当成一个任务扔过去结果拿回来的代码里字段名和数据库对不上、错误处理全是console.log、状态管理混着业务逻辑改起来比从零写还慢。所以我后来给自己定了一条铁律凡是不能用一句话说清楚怎么算做完了的任务一律不许开工。这句话听起来像废话但真执行起来你会发现项目初期有一半的需求都过不了这一关。1.2 原子化拆解的本质是把模糊需求压成可验证单元很多人理解的拆解是按层拆数据层、接口层、前端层。这个拆法没错但它只解决了在哪儿写没解决写成什么样。真正的原子化拆解得同时切三刀切法切什么切不好的后果纵向切按业务能力切登录、建任务、分配、流转一个功能横跨四五个文件改一处崩三处横向切按技术层切契约、实现、联调、样式层与层之间契约漂移联调时全靠猜时间切按可验证节点切能跑通、能测、能上线攒了一大堆半成品谁也不知道整体能不能跑我实际操作时是三刀叠着用先纵向切出业务能力每个能力内部再横向切成契约—实现—验证三个小格每个小格做完立刻跑一次验证。这样任何一个格子出问题影响范围都被锁死在那个格子里。举个具体的。项目里有个任务转派能力我把它切成这么几格数据层转派记录表结构 转派前后的状态机约束契约层转派接口的请求体、响应体、错误码枚举服务层转派逻辑校验当前持有人、校验目标人是否存在、写流水、更新主表、发通知接口层路由挂载、权限中间件、参数校验前端转派弹窗组件选人、填原因、提交、错误提示验证三条用例正常转派、转给自己、转给已离职的人每一格单独喂给 AI单独验收。你会发现第 3 格是唯一真正需要想的一格其他的都是模式化工作。这也就是原子化最值钱的地方——它把项目里真正需要人脑的部分压缩到了很少的几格里。1.3 哪些活交给 AI哪些必须自己扛这一点我必须说清楚因为它决定了你整个流程的上限。我的划分是这样的放心交给 AI 的样板代码、类型定义、CRUD 接口、表单校验、样式微调、单元测试骨架、错误信息文案、正则、SQL 建表语句、Dockerfile、Nginx 配置、日志格式化。需要我先想清楚再交给 AI 的状态机设计、权限模型、事务边界、缓存策略、分页与排序的默认行为、幂等键的选择。基本不交给 AI 的数据模型的第一版设计、跨模块的依赖关系、上线顺序、以及所有这个需求到底要不要做的判断。这个划分不是拍脑袋来的是踩出来的。我最惨的一次翻车是在权限模型上——我让 AI 设计一个支持多角色的权限系统它给我的东西看上去非常完整角色表、权限表、角色权限关联表、用户角色关联表一应俱全。但我拿去一接业务就发现它把数据范围权限和功能权限混在了一张表里导致某个项目的管理员只能管自己项目里的任务这种最常见的需求根本表达不出来。这类问题不是代码写错了是模型选择本身错了而模型选择是人的活。2. 立项期第一刀把全栈需求切出可交付的原子清单2.1 判断一个任务够不够原子的三条硬标准我后来用三条标准来卡能独立验证、能塞进一次上下文、能单独回滚。第一条能独立验证。意思是这个任务做完我能用一条命令、一个页面操作或者一组测试用例明确地判断它成没成。做不到这一条的任务说明它和别的任务耦合在一起了得继续切。第二条能塞进一次上下文。这条特别实用。我一开始没这个概念把整个数据模型文件加整个路由文件一起贴给 AI结果它生成的东西开始记混——把 A 表的字段往 B 表的查询里塞。后来我给自己定了个粗略的量化标准单次投喂的代码量控制在 800 行以内超过就拆。800 这个数不是玄学是我实测下来模型还能稳定记住前文细节的上限附近当然不同模型不一样你可以自己压测一下。第三条能单独回滚。这个任务做砸了我能不能只把这一块撤掉而不影响别的如果你的答案是撤了别的地方就跑不起来那说明边界没切干净。2.2 我的任务卡模板每个原子任务我都会写一张卡格式固定。这个模板本身也是慢慢磨出来的早期版本只有目标和验收两栏后来发现不够用才补到现在这样### 任务卡 T-014 **目标**实现任务转派接口 **范围**仅后端不含前端调用 **输入契约** POST /api/tasks/:id/transfer body: { targetUserId: string, reason?: string } **输出契约** 200: { success: true, data: { transferId: string } } 400/403/404: { success: false, code: string, message: string } **依赖**T-008任务主表、T-011用户表、T-013权限中间件 **不允许改动的文件**src/db/schema.prisma **验收方式** 1. 正常转派返回 200 且流水表新增一条 2. 转给自己返回 400 且 code TRANSFER_TO_SELF 3. 无权限用户调用返回 403 **回滚方式**删除路由挂载回滚一次迁移第三栏和第四栏是最关键的。输入契约输出契约这两栏逼着我在写代码前就把接口形状定死AI 拿到的就不是一段模糊描述而是一份规格书。不允许改动的文件这一栏同样重要——我吃过亏AI 为了让它自己的实现跑通擅自去改 schema 里的字段类型表面上测试过了实际把另一条链路打崩了。任务卡写多了之后会发现一个副产品卡片本身就是最好的文档。项目做完三十多张卡片按顺序排开整个系统是怎么长出来的一目了然。2.3 依赖排序别按页面顺序拆新手最容易犯的错是按页面拆先做登录页、再做列表页、再做详情页。这个顺序看起来符合直觉实际会把联调期无限拉长因为你每次都要临时造一堆假数据。我用的顺序是数据契约 → 认证与权限 → 核心领域逻辑 → 查询与聚合 → 界面 → 打磨。道理很简单前四层都是确定性产出越往下越容易被验证界面层则极度依赖前面定好的契约。如果先做界面你会在数据契约还没稳定的时候反复改前端改一次就是一轮返工。我第一版就是按页面顺序来的做到第三个页面的时候用户表加了个字段前面两个页面的请求层全得跟着改那两个小时的活纯属白干。还有一条同层内的任务优先做被依赖次数最多的那个。用户表和权限中间件被依赖的次数最多所以它们排在最前面。这跟做菜先备料是一个逻辑葱姜蒜一次切好后面每道菜都能直接用。3. 数据层的原子化先把契约钉死再谈实现3.1 表结构必须先手写第一版我在 1.3 里说过数据模型的第一版不交给 AI。这不是保守是我实测出来的效率选择。让 AI 从零设计表结构它给出的东西通常看起来合理但缺三样东西索引规划、软删除与审计字段的统一策略、以及状态字段的取值范围约束。这三样恰恰是后期最难改的。我的做法是自己先手写一版 schema哪怕写得很粗糙只要把实体和关系表达清楚就行。然后让 AI 做两件事一是帮我审查这版 schema 有没有明显问题比如缺外键约束、缺唯一索引二是基于这版 schema 生成迁移脚本和种子数据。这个人定骨架、AI 填肉的顺序带来的效率提升非常明显因为 AI 最擅长的恰恰是那些机械性的、有明确规则的活——写迁移脚本这种活手工写十分钟AI 写十秒钟而且基本不会漏字段。审查那一步我给 AI 的提示词大概长这样下面是一份 Prisma schema只做审查不要改文件也不要输出完整 schema。 逐条列出问题每条包含问题位置、问题类型索引/约束/类型/关系、 为什么是问题、建议怎么改。如果某一项没问题就不用提。 重点关注唯一性约束、外键级联行为、状态字段的默认值与取值范围、 时间戳字段的一致性。注意只做审查不要改文件这句话少了它 AI 大概率会直接把整个 schema 重写一遍给你然后你就得开始玩找不同。3.2 让 AI 产出迁移与种子数据的正确姿势迁移脚本这块AI 的准确率很高但有个前提一次只让它处理一张表。我有次图省事把五张表一起丢过去让它生成迁移结果它把四个字段的默认值搞混了两个not null加在了有历史数据的表上迁移一跑直接报错。后来我改成一张表一次准确率接近 100%。种子数据更值得说一说。种子数据的价值不在于有几条数据而在于它必须覆盖边界情况。我现在写种子数据的标准是每张表至少准备三类记录——正常数据、边界数据空字符串、超长字符串、临界时间、关联缺失数据外键指向的记录被软删除。这三类数据在联调期能帮你提前发现大量的空指针和越界问题。具体操作上我会让 AI 基于 schema 生成种子脚本但会在提示里明确要求覆盖这几类并且要求每条种子数据带注释说明它想覆盖什么场景。这个注释很重要三个月后你回头看没有注释的种子数据就是一堆看不懂的垃圾。3.3 数据层三个必翻车的地方第一软删除和时间戳的一致性。AI 生成查询时经常忘记过滤软删除标记。我的解决办法是在数据访问层封一层统一的查询构造器所有业务查询必须走这层禁止直接调 ORM 的原始查询方法。这层封装的代码也让 AI 写但我会在任务卡里明确要求所有查询默认附带 deletedAt IS NULL 条件且提供显式跳过的方法。第二级联删除的误伤。这个坑很隐蔽。AI 看到外键就喜欢给你加onDelete: Cascade但业务上很多时候你要的是限制删除或者置空。我的做法是schema 里默认全部写成Restrict只有明确讨论过的关系才改成Cascade。改一次要讨论一次虽然啰嗦但比线上删了一条主记录结果带走了三百条流水强。第三金额和计数用浮点。这个属于老生常谈但 AI 依然会犯。凡是钱、积分、配额这类需要精确计算的东西一律用整数存最小单位分、厘或者用定点小数类型。我在任务卡模板里加了一行固定条目涉及金额字段请使用整数存储最小货币单位从那以后再没出过问题。4. 接口层的原子推进一个接口一个闭环4.1 单接口闭环的四段式结构我把每个接口都拆成固定的四段四段写完这个接口才算完入参校验类型、必填、长度、格式、业务规则校验业务逻辑查数据、算结果、写数据事务边界在这里错误出口每个可能的失败点对应一个稳定错误码响应组装统一响应结构不泄漏内部字段这四段分开喂给 AI 效果最好。我试过一口气让它写个完整接口拿回来的代码往往把校验和业务逻辑揉在一起错误处理就是随手抛个throw new Error(出错了)。分开之后每一段的产出质量都明显上了一个台阶。错误码这一块我特别想强调。AI 生成的错误处理有个通病语言太自由。同一个任务不存在它可能在三个地方分别写成任务未找到、该任务不存在、找不到指定任务。前端拿到这些文案根本没法做判断。我的解法是先定一份错误码枚举把它作为契约的一部分写进任务卡里export const ErrorCode { TASK_NOT_FOUND: TASK_NOT_FOUND, TASK_NOT_TRANSFERABLE: TASK_NOT_TRANSFERABLE, TRANSFER_TO_SELF: TRANSFER_TO_SELF, TARGET_USER_INACTIVE: TARGET_USER_INACTIVE, } as const;枚举一旦定死AI 就不会乱编了因为它在上下文里能看到只能从这里选。4.2 上下文投喂的取舍这是整个流程里最需要手感的部分。给少了AI 开始凭空想象字段给多了它开始记混。我摸索出来的一套投喂顺序是这样的投喂内容必要性说明任务卡全文必须规格书不能省相关表结构片段必须只贴这个接口用到的表别贴整个 schema相邻接口的实现强烈建议让它照着已有风格写风格一致性靠这个错误码枚举必须防止乱造错误信息工具函数签名建议只给签名不给实现省 token 也防跑偏完整的项目目录树不必除非涉及新建文件相邻接口的实现这一条是风格一致性的关键。AI 有很强的模仿倾向你给它看一段已有的、写得规矩的实现它后续产出会明显靠拢那个风格你不给它每次都能给你整出新花样。我一般会固定拿前一个做完的接口当样板贴过去。还有个细节贴代码的时候把无关的注释和日志删掉。这不是为了省 token是因为模型会被注释干扰。我有次贴了一段带// TODO: 这里逻辑待确认的代码过去结果 AI 生成的新代码里也照抄了一句类似的 TODO而且它自己还真按待确认的方式实现了一遍。这种失真好气又好笑。4.3 权限校验统一收口权限这事我踩过一次大坑值得单说。项目中期我发现有个接口忘了加权限校验——不是 AI 忘的是我自己写任务卡时漏了那一栏。这暴露了一个结构性问题只要权限校验是每个接口各写一遍就一定会有漏网的。后来我改成统一收口所有需要登录的路由必须挂在一个统一的路由组下这个组自带认证中间件需要特定权限的接口额外声明所需权限标识由中间件统一判定。业务代码里不再出现任何权限判断逻辑。改完之后任务卡里关于权限的那一栏从记得加权限校验变成了声明所需权限标识从一道需要人记住的工序变成了一次声明。这种把易错的人工步骤转成结构性约束的改造我觉得是原子化流程里最值得投入的部分——它一次投入长期生效。5. 前端原子拼装组件契约比页面重要5.1 先组件后页面别反过来前端的拆法和后端不太一样。后端的一个接口天然就是一个原子前端不一样一个页面往往要由七八个组件拼出来。我的做法是先切出组件清单再给每个组件定契约最后才是页面组合。契约包括三样props 的形状、组件自己管什么状态、组件向外抛什么事件。这三样定完一个组件就可以独立开发、独立预览、独立测试了根本不需要等页面。这个做法有个实打实的好处组件可以并行做。我今天让 AI 生成表格组件明天生成筛选栏两个组件之间没有依赖谁也不等谁。等到页面组合的时候拼起来就行。如果反过来先做页面你会发现在页面里写组件AI 生成的东西一定是这个页面专用的复用不了。5.2 请求层抽出来别让组件直接 fetch这一条我要说得重一点。AI 生成前端代码时最喜欢在组件里直接写请求// 这种写法在 AI 生成的代码里出现频率极高 useEffect(() { fetch(/api/tasks?page page) .then(r r.json()) .then(setData); }, [page]);这段代码有三个问题没有错误处理、没有 loading 状态、请求逻辑和组件绑死。单个组件看没什么等你做到第十个组件你会发现每个组件的错误提示各不相同改一个统一的超时策略要改十个文件。我的解法是先手写一个请求层大概三十行包含统一 baseURL、统一鉴权头、统一错误转换、超时控制然后把它的用法作为不得违反的约定写进每一个前端任务卡里。AI 看到这个约定生成出来的就是const { data, loading, error } useRequest(() api.task.list({ page }));差别有多大前者我要收拾十分钟后者基本不用收拾。5.3 样式一致性的保底手段样式是 AI 最容易自由发挥的地方。同一个项目里它可能给你整出三种按钮、四种间距、五种圆角。这事不是靠提示词能解决的靠的是约束。我用了两层约束。第一层是设计变量颜色、间距、圆角、字号、阴影全部定义成变量写进任务卡的固定条目里告诉 AI只能从这些变量里取值不许写硬编码数值。第二层是已有的组件库把项目里已经写好的三四个基础组件的源码固定作为上下文的一部分贴过去让它照着写。这两层一加样式漂移的问题基本解决了。偶尔还是有漏网的我在验收的时候会全局搜一遍硬编码的颜色值搜到就改。6. 联调期踩坑实录AI 代码的几种典型失真6.1 幻觉字段看着完全正确跑起来全错这个坑我遇到不下五次每一次都要花时间排查。现象是这样的前端页面能渲染控制台不报错但数据永远是空的。打开网络面板一看请求发出去了接口也返回 200就是data是空数组。根因往往是这样AI 生成前端类型定义时字段名用的是assigneeId而它生成后端时用的是assignee_id。两边各自看起来都很合理甚至各自的单测都能过因为都是自己内部一致但一接就断。排查链路我后来固定成四步打开网络面板看响应体的实际字段名对比前端类型定义里的字段名搜索后端查询语句里select或returning的字段列表找到那个不一致的字段改一处我一般改前端因为后端契约已经写在任务卡里了这个问题的根本解法不是排查是预防。我后来的做法是把数据库字段命名规范写死在任务卡模板的固定条目里并且要求所有接口的响应字段必须显式列出不许用select *。显式列出之后字段漂移的问题少了九成。6.2 并发与幂等AI 基本不会主动考虑这个坑最危险因为它不会在测试环境暴露。我做的任务协作系统里有个领取任务的功能逻辑很简单检查任务状态是不是待领取是的话改成已领取并绑定当前用户。AI 生成出来的代码逻辑完全正确测试环境跑一百遍也没问题。但我知道这有并发问题——两个用户同时点领取两个请求都通过了状态是待领取的检查然后都执行了更新后一个覆盖前一个结果是两个人都以为自己领到了。修法很简单把检查 更新合成一条带条件的原子更新UPDATE tasks SET status ASSIGNED, assignee_id $1, assigned_at now() WHERE id $2 AND status PENDING; -- 检查影响行数为 0 说明被别人抢先了但关键在于AI 不会主动想到这一点。它写的代码在单线程语义下是完全正确的。所以这类问题必须由人在任务卡里显式提出来。我后来在任务卡模板里加了一个固定栏目叫并发约束凡是涉及状态变更的接口都必须填填不了就说明我还没想清楚。同样的还有幂等。所有创建类接口我都要求带幂等键尤其是涉及外部回调的场景。这个也是 AI 不会主动加的得写进规格里。6.3 错误被静默吞掉AI 生成的代码里错误处理有两种极端要么到处throw要么静默吞掉。后者更可怕因为前者你至少能看到堆栈。最典型的是try/catch里写个空 catch或者只打一行日志然后继续往下走。这个在数据写入场景下会造成数据不一致——主表更新成功了从表更新失败因为失败被吞了事务也没回滚最后你得到一份半截的数据。我的处理方式是在任务卡里固定一条所有 catch 块必须要么重新抛出、要么转换为统一错误码向上返回禁止空 catch 或仅打日志。另外涉及多表写入的接口我会在验收标准里明确要求事务包裹任一步失败整体回滚并且在验收时专门构造一次中途失败来验证。6.4 上线前一周我才发现的那个问题说个具体的过程因为它很好地体现了原子化的价值。项目做到后期我加了个任务列表按截止时间排序的需求。这个需求很小我随手让 AI 改了一下查询加了个orderBy测了一下页面排序是对的就收工了。过了两天我又加了个按优先级筛选同样的流程同样的测试方式。上线前一周做全量回归时我发现列表分页在筛选 排序同时生效的时候会出现重复数据——第一页的最后一条和第二页的第一条是同一条。排查花了我大概一个小时链路是这样的先确认是不是前端去重的问题不是数据本身重复再看 SQL 的实际执行计划发现orderBy用的字段不唯一多个任务的截止时间相同数据库在两次分页查询之间返回的顺序不稳定。修法是加一个唯一字段作为排序的第二关键字。这个问题的本质是分页必须有稳定的排序保证。这是一个老知识点但我在拆任务的时候把它漏了因为每个任务单独看都做对了。这也说明原子化不是万能药——它能保证每个格子里的事做对但格子与格子之间的相互作用仍然需要人在集成阶段整体过一遍。7. 交付前的原子校验与上线闭环7.1 我的冒烟清单上线前的验证我不看测试覆盖率那个数字我看一份手写的冒烟清单。清单是按用户能走完的完整路径组织的不是按模块组织的。大概长这样路径关键检查点结果新用户注册到首次登录注册成功、收到验证、登录后看到空状态页通过创建任务并指派创建成功、列表出现、被指派人收到通知通过任务状态流转全链路待办→进行中→待验收→完成每步权限正确通过越权访问用 A 账号访问 B 的任务详情返回 403通过分页边界空列表、单条、整页、跨页排序稳定性修复后通过异常路径断网提交、重复提交、超长输入通过这份清单的价值在于它模拟的是真实用户行为而不是代码覆盖率。我在这一栏里抓出过好几个单测全过但用户一用就崩的问题比如空列表状态下页面白屏、重复点击提交创建了两条记录。7.2 部署最小闭环部署这块反而是 AI 帮了大忙的地方。我的做法是让它生成多阶段构建的容器配置和反向代理配置然后自己逐行看一遍关键参数。有几个参数我会特别检查构建产物目录是否正确挂载静态资源的缓存策略带 hash 的文件长缓存入口文件不缓存反向代理是否透传了真实客户端信息健康检查路径是否和实际接口对上这些参数 AI 大概率写对但只要有一个写错表现就是页面能打开但接口全 502这种级别的问题。花五分钟看一遍比上线后花半小时排查划算。还有一条经验第一次部署不要追求全自动。我第一个版本的部署是手工敲命令的跑通之后才写成脚本。直接上自动化流水线出问题时你分不清是配置错了还是流程本身有问题。7.3 文档任务卡就是最好的文档前面提过任务卡本身可以当文档用这里补充一下我实际怎么用。项目交付后我要写一份说明给后面接手的人也可能是三个月后的自己。我发现不需要另写一份架构文档把三十多张任务卡按依赖顺序排开再补一份模块地图就够了。模块地图大概就是一张表列出每个模块对应哪几张任务卡、模块之间的调用关系、以及每个模块的边界在哪。反倒是那些踩过的坑值得单独记。我在项目目录里维护了一个PITFALLS.md每条写三段现象、根因、怎么避免。这个文件是整个项目里我自己回看频率最高的东西。8. 复盘原子化的收益边界在哪做了这么一轮下来我对原子化 AI 编程的评价是它极大地压缩了实现的成本但完全没有压缩设计的成本反而把设计的成本提前了、集中了。以前我是边写边想写到哪算哪靠重构慢慢收敛。现在不行我必须在上手之前把契约想清楚因为 AI 不会帮我想它只会严格执行我给的规格而且执行得比我快得多。这意味着项目前期的思考密度比过去高很多但中后期的返工量确实明显下降。我粗略估了一下这个项目里我花在想上的时间和花在写上的时间比例大概是 4:6而在过去同样规模的项目里这个比例大概是 2:8。原子化也不是没有代价。最明显的一个是流程开销写任务卡本身要花时间一张卡五分钟三十张卡就是两个半小时。如果你的项目只有三五个接口这么搞反而不划算。我的经验阈值是接口数量超过十五个、或者项目周期超过两周原子化才开始有正收益低于这个量级直接跟 AI 对话式地写反而更快。第二个代价是全局视野容易丢失。拆得越细越容易陷入每个格子都对、整体不通畅的状态我在 6.4 那个分页排序的问题就是典型。这个目前我没有特别好的自动化解法只能靠定期的整体回顾——我一般每做完两三个模块就把完整主流程手动走一遍专门找那种单个看都对、串起来别扭的地方。最后一个体会也是最想分享的一条把易错的人工步骤转成结构性约束比什么提示词技巧都管用。我在这个项目里做的几次效率跃升靠的都不是换了更好的提示词而是把某件事从每次都要记得做变成了不做就跑不起来。权限校验统一收口是这样请求层封装是这样错误码枚举是这样数据库字段命名规范也是这样。AI 的执行力没有问题它的问题在于不会主动替你想那些应该做但没人说的事而结构性约束恰好就是把这些事变成必须做的事。
阅读完成 · 觉得有帮助?
咨询建站