一个人接下全栈项目,最难受的时刻从来不是写不出那几行业务代码,而是脑子里同时装着前端状态管理、后端鉴权、数据库迁移、部署脚本这一整套东西,切一次上下文就掉一层血。这两年我把 AI 编程助手真正用顺手以后,最大的收获不是"写得更快",而是我摸索出了一套叫原子化 AI 编程的工作方式:把一个人要扛的全栈项目,拆成 AI 在一次会话里能完整吃下、并且能用一条命令验证对错的最小任务块,然后一块一块啃。这篇就是我拿一个内部用的"知识条目管理与检索系统"做试验田的完整拆解复盘——前端 React + TypeScript,后端 FastAPI,PostgreSQL,Docker 部署,从零到能跑通的整个过程。它解决的核心问题是:在没有团队分工的前提下,如何让 AI 输出稳定可控、不跑偏、不偷偷重构你的架构。适合正在一个人做产品、做毕设、做副业的开发者看,也适合已经用过 AI 补全但总觉得"它写的东西我不敢合进去"的朋友。
1. 为什么我把"全栈"拆成了"原子任务"
1.1 一个人的全栈项目,瓶颈根本不在打字速度
我先说个可能反直觉的结论:单人全栈项目里,AI 能把编码时间压掉一大半,但项目总工期往往只缩短三成左右。差的那部分去哪了?全耗在上下文切换和返工上。
举个我自己踩过的场景:上午我在写后端的分页查询,脑子里是 SQL 和索引;下午切到前端列表页,要立刻换成组件状态和请求缓存的思维;晚上补部署脚本,又是另一套心智模型。每一次切换,我都要重新把上一块的隐式约定捡回来——字段到底叫created_at还是createdAt?空值返回null还是空字符串?这些约定在我脑子里是模糊的,对 AI 来说就更是黑箱。
所以我的思路变了:不再问"AI 能不能帮我写这个功能",而是问"这个功能能不能被切成若干个自包含的小块,每块的输入输出都写死,AI 只负责填中间的实现"。这就是原子化拆解。它的本质不是提高 AI 的能力上限,而是降低 AI 犯错的表面积——任务越小、边界越清晰,它越没有机会自作主张。这一段听不懂没关系,往下看完那张对照表你就明白了。
1.2 什么才算"够原子":我用的四条硬标准
"原子"这个词很容易说得很玄。我给自己定了四条可以量化的标准,只要有一条不满足,就说明这块还得继续切:
- 改动文件数 ≤ 3 个。超过 3 个文件,AI 很容易只改到一半,或者改了一个忘了另一个,最后一编译到处报错。
- 单次 diff ≤ 200 行。这个数字不是拍脑袋——超过 200 行,我人工 review 时的注意力就开始下降,漏看的概率明显上升,反而是"看起来省了时间实际埋了雷"。
- 有且只有一条可执行的验收命令。比如
pytest tests/test_pagination.py -q或者npm run typecheck。验收必须是机器判定的,不能是"我看着还行"。 - 不引入新的抽象层。除了专门负责"定义接口"的那类任务,其他任务一律不允许 AI 自己新建 service 层、工厂类、基类。这是血泪教训,后面第 5 节会细讲。
这四条里我最看重第三条。有明确验收命令的任务,AI 一旦写错会立刻暴露;没有验收命令的任务,错误会安静地躺在代码里,等你三天后联调时集中爆发。你可以把这条理解成"给 AI 装一个即时报错的红绿灯",没有红绿灯的路口,出事故只是时间问题。
1.3 拆解粒度对照表:把"大功能"翻译成"原子块"
空说标准没意义,我把这个项目里几个真实功能的拆法列出来做对照。注意右边那列的关键特征:每个原子任务都能独立验收,且验收失败时错误范围是可定位的。
| 原始功能描述 | 我的原子拆法 | 验收命令 | 为什么这么切 |
|---|---|---|---|
| 条目列表分页查询 | 先写查询参数校验的 schema | pytest tests/test_schema.py | schema 是契约源头,先固化它后面两个任务才有参照 |
| 同上 | 再写 SQL 与仓储函数 | pytest tests/test_repo_pagination.py | 只碰数据层,不涉及 HTTP |
| 同上 | 最后接路由与序列化 | curl冒烟脚本 | 前两块都绿了,路由层出错的概率极低 |
| 前端条目列表页 | 先定请求函数与类型 | tsc --noEmit | 类型通不过就没必要写组件 |
| 同上 | 再写纯展示组件(假数据) | 人工看 + storybook 截图 | 展示逻辑和网络逻辑解耦 |
| 同上 | 最后接真实请求与 loading 态 | 冒烟脚本点一遍 | 前面两块已稳定,接入失败容易定位 |
| 用户鉴权 | 先定 token 结构与签发函数 | 单测覆盖过期/篡改用例 | 安全相关必须逐条用例验证 |
| 同上 | 再写中间件与依赖注入 | pytest tests/test_auth_mw.py | 中间件行为是"全量生效",必须隔离测试 |
这张表看起来啰嗦,但它省下的返工时间非常可观。我做过粗略对比:同一个列表功能,如果一次性丢给 AI 说"帮我写个带分页的条目列表接口和页面",它经常会把分页参数命名成page/size,前端却用page/pageSize,然后我花半小时对账。切碎以后,前端那个任务卡里我直接把后端契约文件贴进去,这类对账成本几乎归零。
注意:不要为了"原子"而原子。如果一个任务切完只剩两三行代码,那说明切过头了,管理任务卡本身的成本会超过收益。我的经验是单个原子任务对应 20~150 行产出最舒服。
2. 架构与上下文准备:骨架必须人定,AI 只做填充
2.1 架构决策别交给 AI,这是最容易翻车的地方
我在第一个试验项目里犯过一个典型错误:一开始就问 AI"帮我设计一个知识管理系统的后端架构"。它给我输出了一套看起来非常专业的分层:domain 层、application 层、infrastructure 层、interface 层,还配了依赖注入容器和事件总线。一个总共就六七张表的内部工具,被架成了这个样子。
后果是什么?我后面每加一个功能,都要在四五个文件之间跳来跳去,而且 AI 每次生成新代码时对我这套分层的理解都不完全一致,时而把校验放在 domain,时而放在 interface。架构一旦被 AI 生成,你就失去了判断"这段代码该放哪"的锚点。
现在我固定这样干:项目开工前,我自己用半小时画一个极简的目录结构,写清楚三层——路由层只做参数解析和响应封装,服务层放业务规则,仓储层只碰数据库。然后在项目说明文件里明确写一句:"禁止新增目录层级,新增文件必须归入以下现有目录之一。"就这一句话,AI 的"过度设计冲动"就被压下去大半。架构是路标,不是装饰。一个人做项目,路标越少越不容易迷路。
2.2 三层上下文文件:让 AI 每次只看见该看的
上下文管理是原子化 AI 编程里最被低估的一环。很多人抱怨"AI 记不住前面的约定",其实问题在于你把整个项目的所有信息一股脑塞给它了。我的做法是把项目信息拆成三层文件,按任务需要投喂:
第一层是项目总纲,大致百来行,内容固定不变:技术栈版本、目录结构、命名规范(字段用 snake_case 还是 camelCase、日期统一 ISO8601、错误响应格式)。这个文件我会在每次新会话开始时贴一次,让它成为所有任务的地基。
第二层是契约文件,包括数据库表结构的迁移脚本、后端的分页与错误响应类型定义、前端生成的 API 类型文件。这一层只在与"跨端交互"相关的任务里投喂,因为它信息密度高但和纯内部逻辑无关。
第三层是任务卡,一次只贴一张,写完就丢。这一层严禁把上一个任务的讨论过程带进来——这是最关键的纪律。
我实测下来,三层分离之后 AI 输出的一致性提升非常明显。以前它经常在第三个任务里突然把created_at写成createdAt,现在几乎不犯,因为每次会话开头那几十行总纲里明明白白写着命名规范。
2.3 会话节奏:什么时候该果断重开
有个细节值得单独讲:什么时候该结束当前会话、重开一个新的。我踩过的坑是一直在同一个会话里连续做十几个任务,到后面 AI 的回答质量肉眼可见地下降——它开始重复前面已经废弃的方案,或者把两小时前我否掉的写法又提出来。
我后来总结了一个信号清单,出现任意一条就立刻重开:
- 它开始引用我早前说过"不要这么做"的方案;
- 它输出的代码里出现了一种我在项目里从没用过的风格;
- 我发现自己要用三段话去解释"我们现在到哪一步了"。
第三条最准。当你需要花力气给它做进度同步时,这个会话的性价比已经为负了。重开的成本只是贴一次总纲文件,几十秒的事,比在混乱上下文里纠错便宜太多。这条经验听起来很朴素,但它是我这套方法里省时间最多的单条习惯。
3. 任务卡与提示词:把"我想要"翻译成"可验收"
3.1 任务卡模板:六个字段,缺一不可
我不再直接对 AI 说需求,而是先写一张任务卡。模板固定六个字段,格式很土但极其好用:
| 字段 | 作用 | 填写要求 |
|---|---|---|
| 目标 | 一句话说清这块要交付什么 | 动词开头,不超过 30 字 |
| 范围 | 允许改动的文件清单 | 精确到路径,写死上限 |
| 契约 | 依赖的类型、字段、接口签名 | 直接贴代码,不要转述 |
| 约束 | 禁止事项与技术限制 | 至少写 2 条"禁止" |
| 验收 | 一条可执行命令 | 必须能机器判定 |
| 输出 | 期望的交付形态 | 是完整文件还是 diff 片段 |
"约束"这一栏是我后来加的,加完效果立竿见影。人类开发者天然有"顺手优化一下"的习惯,AI 把这个习惯放大了十倍。写上"禁止修改已有测试文件""禁止新增第三方依赖""禁止改动契约字段命名"这三条之后,它的越界行为少了非常多。
3.2 契约先行:先定类型,再谈实现
如果只能保留一条原子化 AI 编程的经验,我选这条:跨端任务永远先定契约,再写实现。契约的形式可以是后端一个类型定义文件,也可以是 OpenAPI 描述,甚至就是任务卡里贴的一段 TypeScript 类型。重点不是形式,而是"先有唯一的权威定义"。
原因很实在:AI 在两个独立会话里生成前端和后端代码时,它并不知道另一边写了什么。你不给它一个共同参照物,它就只能靠概率去猜字段名,猜对的概率大概七成,剩下三成靠你事后对账。而字段名对不上的报错往往出现在最深的地方——比如列表页正常,翻到第二页才 500,排查成本成倍上升。
我现在的固定顺序是:先用一个原子任务把契约文件写出来并提交,之后所有涉及这块的前后端任务,任务卡的"契约"栏直接把该文件内容贴上。这一步多花十分钟,后面能省掉一整个下午。
3.3 一次完整的提示记录:从任务卡到产出
光说不练没意思,贴一段我实际用的提示(脱敏后的模拟项目内容),你可以直接照着改:
【项目总纲】略(约 90 行,含技术栈、目录结构、命名规范) 【任务目标】 实现条目列表的仓储层分页查询函数,不涉及 HTTP 路由。 【允许改动】 app/repositories/entry_repo.py tests/test_repo_pagination.py(新建) 【契约】 Entry 表字段: id UUID, title str(200), content text, status str(20) 取值范围 draft/published/archived, created_at timestamptz, updated_at timestamptz 函数签名固定为: def list_entries(conn, *, status: str | None, q: str | None, page: int, size: int) -> tuple[list[Row], int] 【约束】 - 禁止新增任何第三方依赖,只能用项目已有的 psycopg 与 pytest - 禁止在 SQL 里字符串拼接用户输入,一律用参数绑定 - 禁止修改任何已存在的测试文件 - size 上限 100,超限抛 ValueError,不要在仓储层做静默裁剪 【验收】 pytest tests/test_repo_pagination.py -q 全绿 【输出】 两个文件的完整内容,不要输出 diff。这段提示里最关键的不是格式多漂亮,而是"契约"给了函数签名,"约束"堵了三个常见的坑(拼 SQL、偷偷裁参数、乱加依赖),"验收"给了机器判定。实测下来,这种提示的首次通过率比随口描述高出一大截,我在这个项目里统计过一批任务,首次跑通的比例大概在八成上下,没通过的多数是边界用例没写全,而不是方向错了。
3.4 别让 AI 自己写测试,让它在测试约束下写实现
这里有个反常识的做法:测试用例我要求它先写,但我会先审一遍再让它写实现。原因在于,如果让它同时写测试和实现,它非常容易写出"刚好能过"的测试——断言写得很松,或者干脆把边界用例漏掉。
具体操作是拆成两个原子任务:第一个任务只写测试,约束是"必须包含空结果、越界页、非法 status、模糊搜索无匹配四种用例";第二个任务写实现,约束是"禁止修改测试文件"。这样测试就成了真正的护栏,因为实现方无权改护栏。这个"分两步走"的小技巧,是我这套方法里性价比最高的操作之一,值得你直接用起来。
4. 验证闭环:让 AI 自己证明它没写错
4.1 三道闸门:类型、单测、lint
原子任务跑完,我不会立刻看代码,而是先让它自己跑一遍三道闸门。这个顺序有讲究:
- 第一道是类型检查(
tsc --noEmit或mypy)。类型错误是最便宜的错误,机器一秒就能告诉你,而且往往能反映出契约理解偏差。 - 第二道是单元测试。这里我要强调,单测必须是上一个任务里已经审过的那些,不能是本次新生成的新测试。
- 第三道是 lint 与格式化。放最后是因为它几乎不会失败,但一旦失败说明 AI 引入了不符合项目风格的写法,这往往是一个信号——它在别的地方也可能"自由发挥"了。
三道闸门全绿之后我再进入人工 review。这个顺序的好处很直接:人工时间只花在机器判不了的地方,比如命名是否贴合业务语义、错误处理是否符合产品预期、有没有隐藏的 N+1 查询。
4.2 冒烟脚本:一分钟验证整条链路
纯单测有个天然缺陷:它验证的是孤立单元,验证不了"整条链路能不能跑通"。所以我给这个项目写了一个 60 行左右的冒烟脚本,用bash串起来,每次合并几个原子任务就跑一次:
#!/usr/bin/env bash set -e echo "== 1. 迁移是否幂等 ==" python -m app.cli migrate && python -m app.cli migrate echo "== 2. 造数据 ==" python -m app.cli seed --count 50 echo "== 3. 登录拿 token ==" TOKEN=$(curl -s -X POST localhost:8000/api/auth/login \ -H 'Content-Type: application/json' \ -d '{"username":"demo","password":"demo123"}' | jq -r .access_token) test -n "$TOKEN" echo "== 4. 分页与状态过滤 ==" curl -sf -H "Authorization: Bearer $TOKEN" \ "localhost:8000/api/entries?status=published&page=2&size=10" | jq '.items | length' echo "== 5. 非法参数必须 4xx ==" code=$(curl -s -o /dev/null -w '%{http_code}' \ -H "Authorization: Bearer $TOKEN" "localhost:8000/api/entries?size=999") test "$code" = "422" echo "SMOKE OK"这个脚本里第二步"迁移跑两遍"是我特意加的,因为 AI 写的迁移脚本幂等性经常有问题——第一次跑通,第二次报字段已存在。这种 bug 单测很难覆盖,但在真实部署流程里是致命的。
4.3 人工 review 该看什么、不该看什么
很多人用 AI 编程效率上不去,问题就出在 review 环节:要么全看,累死;要么不看,出事。我现在的 review 有明确的优先级,只重点看四类地方:
第一是错误处理分支。AI 特别爱写except Exception: pass,或者把错误包装成一个通用消息丢掉原始上下文。这类代码单测经常能过,但线上出问题时你一点线索都没有。
第二是权限与数据边界。比如列表接口有没有漏掉"只能查自己可见的条目"这个过滤条件。这类逻辑 AI 写起来很容易漏,因为它不在函数签名里明示。
第三是并发与事务。写操作是否在事务内、更新时是否用了乐观锁或行锁。我见过 AI 生成的更新函数先查再改,两个请求同时进来就丢更新。
第四是新增的依赖与抽象。翻一眼依赖清单和目录结构,有没有多出来不该有的东西。
至于代码风格、变量名长短、注释多少,我一律不看——格式化工具和 lint 已经管了,人再管就是浪费。
5. 踩坑实录与排查速查表
5.1 高频故障速查表
下面这张表是我在这个项目里真实攒下来的,按出现频率从高到低排。左边是现象,右边是根因和我实际用的处置办法。
| 现象 | 根因 | 处置办法 |
|---|---|---|
| 装的包根本不存在,装完报找不到 | 模型幻觉出一个"看起来很合理"的包名 | 所有新依赖必须我手动确认版本后再让它写 import;install 命令永远由我执行 |
| 一个任务改到第三个文件就开始漏改 | 改动面超出原子粒度 | 立刻回滚,重新切任务卡,把范围写死 |
| 前后端字段名对不上 | 没有共享契约 | 契约先行,任务卡里贴类型文件 |
| 测试全绿但线上 500 | 单测覆盖不到链路,或异常被吞 | 补冒烟脚本;review 时专门盯except/pass |
| 迁移脚本跑第二次就报错 | 缺幂等判断 | 迁移里统一加存在性检查;冒烟脚本跑两遍 |
| AI 又把旧方案提出来 | 会话上下文过长 | 重开会话,只贴总纲 |
| 代码突然多出一层封装 | 过度设计冲动 | 总纲里写明"禁止新增目录层级",并在约束栏重申 |
| 分页总数和列表对不上 | 计数查询和列表查询条件不一致 | 要求两者由同一处条件构造器生成,写一条用例校验 |
| 前端请求偶尔重复发出 | 未处理组件重渲染触发 | review 时检查请求触发点,抽成统一的请求钩子 |
5.2 幻觉依赖:这是最需要警惕的一类坑
单独拎出来说,因为它的破坏力被严重低估。模型在生成 import 时,会倾向于使用"听起来该存在"的包名,甚至能编出看起来完整的 API 文档。我遇到过一次,它给我引入了一个用于校验的库,装的时候才发现根本没有这个包名。
从那次以后我立了一条死规矩:任何新依赖的安装命令由我本人执行,不采纳 AI 给出的 install 命令行。它可以说"这里需要一个日期解析库",我自己去查、自己选版本、自己装,然后回来告诉它"已安装某库 vX.Y,API 见下"。这条规矩看着繁琐,但它把所有"环境层面的不确定性"从 AI 手里拿回来了。环境是最不该交给 AI 的部分,因为它错了你排查起来完全没有线索,报错信息全在依赖树里。
5.3 几个土办法,专治"看起来能跑"的代码
还有几个不成体系但很好用的小手段,我一直在用:
让它先复述任务。任务卡贴完,先要求它用三句话复述目标和约束,我确认无误再让它动手。这一步能拦下相当一部分理解偏差,成本只有几秒。
要求它输出"改动清单"。动手前先列将要修改的文件和每个文件改什么,我扫一眼就能发现有没有越界。这比事后 diff 审查便宜得多。
卡住时让它给三个方案而不是一个方案。当我发现它第一次实现方向就不对时,直接说"给我三个不同思路,各说优劣,先不要写代码"。这能有效打断它在错误方向上的惯性。
关键路径要求它写"反例注释"。比如在鉴权中间件里,要求它注明"token 过期时返回 401 而非 403"这类反直觉约定,防止后来自己或它自己改错。
注意:这些手段都不是为了"管住 AI",而是为了把"判断权"留在人这边。原子化拆解的核心不是让 AI 更聪明,而是让人的判断介入得更早、更便宜。
6. 复盘账本:哪些环节真的快了,哪些只是错觉
6.1 一份粗糙但真实的时间分布
我不太喜欢讲"效率提升多少倍"这种话,因为变量太多。但我确实记了流水账,下面这个模拟项目的数字供你参考(项目规模:六张表、十二个接口、五个页面、一套部署脚本):
| 环节 | 传统手写(估) | 原子化 AI 流程 | 我的主观感受 |
|---|---|---|---|
| 架构与目录设计 | 0.5 天 | 0.5 天 | 没变,这块必须人做 |
| 契约与表结构 | 1 天 | 0.5 天 | 明显快,AI 擅长照着规范铺陈 |
| 后端接口实现 | 3 天 | 1 天 | 快得最多,重复模式多 |
| 前端页面 | 2.5 天 | 1.5 天 | 快,但样式调试仍要人工盯 |
| 测试编写 | 1.5 天 | 0.5 天 | 快,但用例设计仍需人审 |
| 部署与联调排错 | 1.5 天 | 1 天 | 略快,AI 对报错信息解读有帮助 |
| 返工与对账 | 0.5 天 | 0.3 天 | 变少了,主要靠契约先行 |
数字是估的,但趋势和我实际体感一致:越是"有固定形状"的活儿,AI 收益越大;越是"边界模糊、需要取舍"的活儿,收益越小。所以别指望它在架构选型或者产品判断上帮你,那部分省不下来。
6.2 我改掉的三个坏习惯
复盘下来,真正让项目变顺的不是某个工具,而是我改掉的几个习惯。
第一个是**"一次说清所有需求"的冲动**。以前我总觉得描述得越全面 AI 做得越好,结果它被无关信息干扰,反而在次要地方乱发挥。现在我宁可分五次说,每次只说一件事。
第二个是**"先跑起来再说"的侥幸**。AI 生成的代码有个特点:它经常"看起来能跑"。我现在的原则是任何产出必须先把三道闸门跑绿,再谈下一步,绝不带着红灯往前走。红灯欠账是会复利的。
第三个是**"让它顺手帮我重构"的懒惰**。我有一次让它"顺便把重复的校验逻辑抽出来",结果它抽了一个只在这两处用到的抽象,还改了两个我没让它碰的文件。从那以后我明确了一条:重构是独立任务,必须单独开一张任务卡,单独验收。
6.3 这套方法不适合什么场景
最后说点反向的,免得你被带偏。原子化 AI 编程在"业务规则清晰、模式重复度高"的项目里效果拔群,比如 CRUD 后台、数据看板、脚本工具。但在下面几类场景里,我的体感是收益有限甚至为负:
一是算法与性能敏感的核心逻辑。它写的实现往往正确但不够好,你还得自己重写,来回折腾不如一开始就自己写。
二是强交互与视觉细节密集的前端。动效、响应式断点、像素级对齐这类活儿,描述成本极高,AI 靠猜,你得反复纠正,反而更慢。
三是早期探索阶段的产品。需求每天都在变,你拆出来的原子任务第二天就作废,任务卡的管理成本白花。
这套东西真正的价值,我觉得是它把"一个人的全栈项目"变成了"一个人管一条流水线"——你不再是那个什么都要亲手写的人,而是那个定标准、切任务、验结果的人。角色变了,节奏就稳了,晚上也能睡得着。