我前阵子接了个活儿,想让 AI 帮我写一个“客户信息管理模块”。我当时觉得这需求够清楚了吧,五个字,一句话,丢给 AI 就能出代码。结果它给我生成了一堆看起来运行正常、实际上完全没法用的东西:电话字段允许输入“abc”,邮箱格式全靠自由发挥,地址随便填什么都能过,删除客户没有任何确认逻辑。那一刻我意识到,问题不在 AI 身上,在我身上——我给它的“一句话需求”,本质上等于没给需求。
后来我把那套“客户信息管理系统”的模糊想法,花了一个下午拆成了一份字段级 Spec,也就是把每个字段叫什么、类型是什么、能填多长、可不可以为空、默认值怎么定、校验规则是什么,逐条写清楚,再拿这份 Spec 去喂 AI。结果生成的代码几乎一遍跑通,连测试用例都自己补得七七八八。
这篇文章就是想说清楚一件事:AI 写代码的时代,吃香的已经不是会写多少行代码的人,而是能把需求写得让 AI 不用猜的人。我会讲讲为什么一句话需求指挥不动 AI,怎么把模糊想法逐步拆成字段级 Spec,再附一份可以直接抄的实战案例和排查清单。适合刚接触 AI 编程、总觉得“AI 写的东西不靠谱”的开发者,也适合想在团队里推行 AI 辅助开发的负责人。
1. 为什么一句话需求指挥不动 AI 写代码?
1.1 AI 其实是“概率模仿者”,不是“需求翻译官”
很多朋友有一个误解,觉得 AI 是万能的翻译,你给它一句人话,它就能理解成计算机程序。实际用过大模型写代码的人应该都有体会,它更像一个训练量极大的“模式匹配器”:你给它一句话,它会根据训练数据里出现频率最高的模式,给你接一个“最像那么回事”的答案。
我举个例子你就明白了。你去一家餐厅,跟服务员说“来个好吃的”,服务员大概率给你推荐大众点评排名第一的招牌菜。这不是因为它读懂了你的口味,而是因为在它见到的所有点单样本里,排名靠前的菜最容易让人满意。你如果补一句“辣的、酸汤肥牛、别放香菜”,它端上来的菜就完全不一样了。
AI 写代码也是这个道理。你说“帮我写个注册功能”,它就用训练语料里出现频率最高的那种注册模块来生成:用户名加密码,存数据库,查重一下,返回 success。它不知道你的系统还有手机号注册、邀请码、短信验证、登录风控、第三方绑定,因为你没说。不是说 AI 蠢,而是它只能用文本模式去“猜”概率最高的上下文。你给的信息越少,它就越倾向于生成一个泛化到谁都能用的“平均答案”,而这个答案往往不是你的业务要的东西。
这里还要多说一句,频繁翻车的另一个原因是“代码”本身的复杂性大概率超过一句自然语言能承载的信息量。一句话需求压缩了太多业务上下文,而代码要求的是精确、无歧义、可执行。两者之间存在巨大的信息鸿沟,AI 再聪明也没法凭空跨越,除非你自己去把鸿沟填上。
1.2 一句话需求到底丢掉了什么
我复盘了自己踩过的坑,发现一句话需求至少会丢掉下面这些关键信息:字段约束、边界条件、业务规则、异常路径。你一说“注册功能”,脑子里可能已经默认了“用户名 3~20 个字符、密码 8 位以上包含字母数字、手机号走短信验证、重复用户名要提示”。但这些默认值,AI 完全不知道。它只能按最常见的设计来,而最常见的注册功能大多只覆盖用户名和密码。
再举一个更典型的例子。“写一个订单系统”,这句话能引起的歧义多到离谱:订单需不需要拆单?支付有几种方式?订单状态怎么流转?取消订单有没有时限?超时未支付要不要自动关闭?库存是下单锁还是支付才扣?这些规则哪怕漏掉一条,AI 生成的代码都算不上“可用”,只能算“能跑”。
一句话需求还有一个非常隐性的破坏力,就是它会让你误以为“需求已经说完了”,从而跳过审视需求的环节。等到 AI 生成一堆代码,你才发现这里不对那里不对,再一条条改,成本反而比一开始把规则写清楚更高。传统开发里有个词叫“需求变更成本”,AI 编程时代这个成本没有消失,只是从“改代码”变成了“改描述”和“重新生成”,如果反复重新生成,时间一样哗哗地流走。
所以我的判断是:AI 不是不需要需求分析,而是比以前更需要需求分析,只不过交付物要从“一份给产品经理看的需求文档”,变成“一份给 AI 当施工图的字段级 Spec”。这个转移背后,才是真正想清楚业务逻辑的过程。
1.3 字段级 Spec 为什么能成为突破口
字段级 Spec,说白了就是把需求细化到“每个字段怎么定义、每条规则怎么表达”的文档。它像是给 AI 的一张“施工图纸”。你跟装修师傅说“把房子弄好看”,师傅只能按他理解的审美发挥;你给他一张标好了插座高度、柜子颜色、地板材质的图纸,他才能还你一个你想要的屋子。代码也是一样。
有人可能会问,我直接跟 AI 说清楚不就行了吗,为什么非要写下来成 Spec?这里面有一个 AI 输出的本质问题:上下文越长,AI 对每条信息的遵守度反而会下降,尤其是纯聊天的长篇对话,越到后面越容易遗忘早期约定。你如果口头零零散散告诉 AI 十条规则,它生成第三十个字段的时候可能已经忘了第一条。但如果你把规则集中成一份结构化 Spec,放进提示词的开头,AI 在生成每个字段、每个函数时都能“看到”这份权威定义,输出的一致性会显著提高。
另外,字段级 Spec 也是你和 AI 之间的“唯一事实来源”。遇到 AI 生成得不对,你不用翻聊天记录去对质,直接把 Spec 里对应字段的条目甩给它说“这里违反了第 3 条”。这比“你上次说的那个,再改改”靠谱得多。后面我会专门讲怎么用这套办法跟 AI 来回拉扯,效率能翻一倍。
2. 把口头想法拆成字段级 Spec 的四步法
2.1 第一步:拆业务对象,让实体浮出水面
拿到一个模糊需求,第一步不是急着写字段,而是先把业务对象拆出来。业务对象就是你系统里要管理的“东西”,比如商品、订单、用户、客户、项目、任务。一个对象配一张表,一个对象就是一个核心实体。
怎么拆?我最常用的办法是拉一个“名词清单”。把你刚才那句话里的所有名词都抓出来,比如“做一个客户信息管理系统”,抓出来的名词就是“客户”和“信息”。再往下细化,“客户”可以拆出基础资料、联系方式、交易记录、售后记录等子对象;“系统”还要考虑登录账号、角色权限这些外围对象。拆的时候宁多勿少,因为这决定了你给 AI 的边界——你不写客户交易记录,AI 就默认不生成,你后来说“怎么没有下单历史”,那只能怪自己没写进去。
拆完实体之后,还要理一下实体之间的关系。客户和订单是一对多,订单和商品是多对多,这些关系决定了 AI 该怎么建表、怎么设计外键。有些人可能会说“我直接用 AI 设计表结构就行了”,我也试过,结果它设计出来的表字段倒是挺全,但表之间的关联、索引、唯一约束经常想当然,比如同一个手机号允许注册了两次。实体和关系这一步,我建议人先想清楚,AI 可以辅助,但不要完全撒手。你给它一个稳定的骨架,它才好填肉。
2.2 第二步:逐字段落盘,写出让 AI 不猜的定义
实体确定后,就进入最关键的一步:把每个实体的属性拆成字段,再为每个字段写清楚定义。什么算“定义清楚”?我给自己定的标准很简单:一个字段要让完全没参与过这个项目的人(或者 AI),看了就懂、不用猜。
我常用的字段定义模板是这样的:字段名、中文含义、数据类型、长度、是否必填、默认值、是否唯一、校验规则、业务说明。举个例子,“客户手机号”这个字段,如果你只写“phone: string”,AI 大概率会存成 varchar(255),不校验,允许为空。但如果你写成:
phone: string, 长度 11 位, 必填, 唯一, 符合大陆手机号正则AI 生成的代码就会对应给它加一堆校验、唯一索引和错误提示。差别就是这么大。我是从实际项目里真切感受到的:同样的 AI,同样的模型,Spec 写得越细,生成代码的质量差距就越明显。
这一步比较耗时,但它有个隐藏好处:写字段的时候你会被迫思考业务逻辑。比如“客户状态”这个字段,写默认值的时候你会想:新客户是什么状态?禁用客户怎么表示?这就是在做需求分析。很多人觉得写字段级 Spec 浪费时间的根本原因,是他们把需求分析当成了额外负担。实际上它就是你该做的事,只不过以前是拿嘴在会议上说,现在是拿键盘落到文档里。
2.3 第三步:补齐行为规则,把“大概”变成“必须”
字段定义完了,还差行为规则。我管它叫“动词层”,因为字段定义管的是“数据长什么样”,行为规则管的是“数据怎么被创建、修改、查询、删除”,这正是最容易让 AI 自由发挥的地方。
行为规则要覆盖四个问题:
- 谁能操作这个对象?(管理员可删除/客服只读)。
- 操作流程是什么?(注册先填手机号,再收验证码,最后设密码)。
- 状态怎么流转?(订单从待支付变已支付,已支付才能发货,退款只能发生在已支付之后)。
- 异常怎么办?(库存不足要提示什么话术?删除已停用客户是否允许?邮箱格式错了返回什么错误码?)
为什么必须写到“什么错误返回什么话术”这种粒度?因为 AI 生成的异常处理往往千篇一律,只会返回一个“error: invalid input”,这在真实场景里根本不够用。你告诉它“手机号格式错误时返回 code 40001,提示文案为‘手机号格式不正确,请重新填写’”,它就能给你完整的响应结构。你连错误码都给了它,它就不会自己随便编。
我还喜欢在行为规则里加一句硬约束:“未在 Spec 中声明的行为,一律不要默认实现。”这句话非常重要,等于给 AI 的自由发挥关上了门。你看很多 AI 生成的代码,给你多加一些自以为是的功能,表面上很贴心的样子,实际却是画蛇添足。有了这条约束,你至少能保证一辈子的“清纯”:出来的代码跟你的 Spec 一致,你的评审成本就低很多。
2.4 第四步:用验收清单反向检查 Spec
写完 Spec 别急着喂给 AI,建议先自己拿它做一轮“验收测试”。我会把 Spec 里的每条字段规则转成一个验收点,比如“手机号少于 11 位能不能保存成功?”“重复手机号报不报错?”然后把验收点列成清单。如果这个清单你自己说不出答案,说明 Spec 没写完,还得补。
这一步还有一个更实用的技巧:你先不看代码,只看 Spec,把这张验收清单丢给 AI,让 AI 根据 Spec 写出对应的测试用例。如果 AI 能写出像样的测试用例,说明 Spec 的自洽性没问题;如果 AI 卡住了,或者反复问你“这个字段的规则没提”,那就是 Spec 有漏洞。
我自己每次的新项目都会走一遍这个闭环:需求 → 实体 → 字段 → 行为规则 → 验收清单。走完之后心里特别踏实,因为我知道 AI 再怎么生成,也就是在替我把“已经想清楚的东西”翻译成代码,而不是替我“发明业务逻辑”。这个区别,决定了 AI 写代码是效率工具还是麻烦制造机。
3. 实战案例:一个客户信息管理的字段级 Spec 全流程
3.1 一句话需求及其翻车现场
为了让你更直观地看到差别,我拿最典型的“客户信息管理”来走一遍。先说原始需求,很多人会这样提:帮我写一个客户信息管理系统,能增删改查客户。
这个需求够普通吧?我把它直接丢给 AI 生成了一份代码,结果是:客户表里就四个字段,姓名、电话、邮箱、地址,“电话”字段用的是字符串随便存,邮箱格式没校验,删除客户直接物理删,没有任何二次确认。更夸张的是,它连客户唯一标识用的还是自增 id,传一次数据换一次环境 id 就乱了。你能说这代码不能跑吗?能跑,但没有一个字段经得起业务细节的审视。
我当时遇到这种情况,第一反应是“换一个更强的模型试试”,换完发现还是老样子,逼得我回头去补 Spec,才意识到我的问题从一开始就偏了:我把它当成 AI 的问题,其实是我需求做得不到位。
3.2 我的最终字段级 Spec(直接可抄)
下面这份 Spec 是我后来整理的“客户信息管理”简化版,字段粒度到了可以直接丢给 AI 生成后端接口的程度。我强烈建议你照抄结构,再替换成自己的业务字段。
# 客户信息管理模块字段级 Spec ## 实体:customer(客户) | 字段名 | 类型 | 长度 | 必填 | 默认值 | 唯一 | 校验规则与业务说明 | |---|---|---|---|---|---|---| | customer_id | bigint | - | 是 | 自增 | 是 | 主键,系统生成 | | customer_no | string | 32 | 是 | 自动生成 | 是 | 格式:CUS + 年月日 + 4位随机数,如 CUS202501160013 | | name | string | 50 | 是 | 无 | 否 | 客户名称,首尾空格自动去除,不允许为空串 | | phone | string | 11 | 是 | 无 | 是 | 必填,正则校验:1开头第二位3-9,共11位数字 | | email | string | 100 | 否 | 无 | 否 | 如果填写,必须符合 email 格式正则 | | address | string | 200 | 否 | 无 | 否 | 详细地址,最长200字符 | | status | string | 20 | 是 | enabled | 否 | 枚举:enabled(启用)/ disabled(禁用) | | remark | string | 500 | 否 | 无 | 否 | 备注,内嵌 500 字符 | | created_at | datetime | - | 是 | 当前时间 | 否 | 创建时间,系统自动生成,不可修改 | | updated_at | datetime | - | 是 | 当前时间 | 否 | 最后更新时间,每次更新自动刷新 | ## 行为规则 1. 新增客户:front desk、API 均可新增;新增时如果 phone 已存在,返回错误码 40001,文案"该手机号已注册,请更换手机号"。 2. 查询客户:支持按 name 模糊查询、按 customer_no 精确查询;列表分页默认每页 20 条,最大 100 条。 3. 更新客户:仅更新非空字段;name、phone 修改时重新做唯一性校验;remark 可更新为 null 语义的空字符串。 4. 删除客户:不允许物理删除,启用逻辑删除(添加 deleted 字段,默认 false);已删除客户在普通列表不展示。 5. 特殊约定:所有接口入参和出参均为 JSON,时间格式 ISO8601(如 2025-01-16T10:30:00Z)。 6. 未在本 Spec 中声明的功能,一律不要默认实现。你仔细对比一下,这份 Spec 和“能增删改查客户”这句话的信息密度差多远。字段多达 10 个,光校验规则就有电话正则、邮箱格式、唯一索引、状态枚举、逻辑删除,还规定好了错误码。AI 拿到这份图纸,根本不需要“猜”,因为它已经没有任何发挥空间了。
3.3 喂给 AI 的提示词(附模板与要点)
Spec 写好之后,怎么喂给 AI 也有讲究。很多人直接把整份文档复制到对话框里,说“按这个生成”,这样能用,但还不够稳。我用的提示词模板长这样:
你是一名资深后端工程师。请严格按照下面的字段级 Spec 实现客户信息管理模块的 RESTful API。 【硬性要求】 1. 不得新增、删除或修改 Spec 中字段的定义。 2. 所有接口字段名与 Spec 保持一致,不准自行改名。 3. 校验规则必须按 Spec 逐条实现,缺失校验视为错误。 4. 错误码和提示文案按 Spec 定义,不得自定义。 5. 生成代码时,请同步生成对应的数据库建表语句。 6. 最后给出所有接口的单元测试代码,覆盖 Spec 中每条验收点。 【字段级 Spec】 (在这里粘贴上面那份 Spec)这里面有几个点值得展开讲。第一,我强调“不得新增、删除或修改 Spec 的字段定义”,这句话是在给 AI 划边界,否则它会很积极地把“deleted_at”这种字段自己加上,或者把 customer_no 改成 customerId,代码风格完全不可控。第二,让它“同步生成建表语句”和“单元测试代码”,这等于把 Spec 里的每一条规则都变成可验证的东西,你再拿测试结果跟 Spec 对,哪里不一致一目了然。第三,提示词里不要写“请把代码写得好一点”这种废话,AI 对“好”的定义和你不一样,“好”是不可度量词,在 Spec 里根本不存在。
如果你是第一次这么干,可以先用一个小模块练手,感受一下“约束严格”和“自由发挥”之间的差距,真的非常明显。我见过很多同事第一次用这套方法,直接愣住:“我怎么之前没这么干过。”
3.4 AI 生成后的逐项核对方法
AI 生成完代码,不要直接上线,一定要做一次“Spec 核对”。怎么核?我把方法归纳成三个动作。
第一个动作是“字段对照”。打开数据库表结构,跟 Spec 里的字段表逐一比对,多一个字段、少一个字段、类型不一致,全部打回。这一步用肉眼是体力活,但真的很重要,因为生成代码经常会出现“email 字段搞成 varchar(255),结果校验正则也写错”这种小事,堆起来就是灾难。
第二个动作是“用例抽查”。让 AI 生成的单测跑一遍,然后手动再补几个边界用例:电话填 10 位报不报错?电话填 12 位报不报错?邮箱填“123@”行不行?remark 填 501 个字符会不会崩?拿这些边界值去戳 API,本质就是在给你的 Spec 做压力测试,顺手还能验证 Spec 本身的合理性。我曾经在抽查中发现“删除客户后还能按 customer_no 查到它”,因为我把逻辑删除字段放在了实体里却忘了在查询规则里声明“默认过滤 deleted=true”,这个坑就是边界测试帮我捞出来的。
第三个动作是“差异回写”。核对过程中如果发现 Spec 有漏洞,先改 Spec,再让 AI 按新 Spec 重跑,而不是直接在生成的代码上改。我一直秉持一个原则:文档和代码必须一致,不一致时以文档为准,让代码反过来适配文档。这样你手里的 Spec 永远是最新的,长期来看维护成本极低。如果你直接改代码,Spec 很快就过时了,后续再用 AI 改需求就又回到“AI 猜你想改哪里”的老路。
4. 常见翻车现场与排查技巧
4.1 三种典型翻车模式与快速诊断
跟 AI 配合写代码久了,翻车场景来来回回就那么几类,我把最常见的三类整理成了速查表,方便你定位问题:
| 翻车现象 | 大概率原因 | 快速排查方法 | 解决动作 |
|---|---|---|---|
| AI 生成的字段/表结构跟想象不一样 | Spec 没有覆盖字段粒度,或覆盖了但提示词没强调“不得修改” | 对照 Spec 逐字段检查建表语句 | 补全字段定义,重新生成,绝不手改代码 |
| 校验规则缺失,非法数据入库 | 校验规则只写在自然语言里,没收敛到字段表 | 打开数据库,尝试插入非法值;查代码里有无对应正则 | 把校验规则写成 Spec 的字段行,让 AI 按行实现 |
| 代码能跑但多了很多没要求的东西 | 提示词里没有“未声明功能不要实现” | 对比 Spec 功能列表,检查是否有额外路由/表/字段 | 在提示词中追加硬约束,把自由发挥项删掉重跑 |
我早期犯的最多的就是第一类:总以为自己说得够清楚了,其实 AI 眼里的“清楚”跟我脑子里的“清楚”完全是两码事。后来我养成了个习惯,写完 Spec 先默读一遍,看到底有没有“合理就行”“合适就好”“按常规来”这类词,只要看到这种主观模糊词,一律改成可度量的描述。好比你把“地址要校验”改成“地址长度 5~200,不允许只有空格的纯空白字符串”,AI 就不会在“校验地址”四个字上一头雾水。
第二类翻车,说白了还是因为你偷懒。你说了“email 可选”,但你没说“可选的意思是不填也能过,填了就必须符合格式”,两种话的代码实现完全不同。AI 一看“可选”,很容易理解成“不管填没填,格式都不校验”,于是你的邮箱字段成了什么都能装的大筐。所以要记住,凡是“可选”字段,必须同时声明“为空时的行为”和“非空时的校验”,缺一个,AI 就漏一个。
4.2 值得长期保持的几条实用习惯
除了上面这些排查方法,我自己沉淀了几条习惯,算是长期跟 AI 配合下来的“肌肉记忆”。
第一,把 Spec 当成项目的一部分,而不是一次性工具。我会在项目文档里建一个docs/specs.md,把所有模块的字段级 Spec 都放进去。以后每次让 AI 改需求,先更新这条 Spec,再把它丢给 AI,省得每次都在聊天窗口里翻旧账。有些 IDE 类 AI 工具支持读取项目文档,你把 Spec 放进去,它会自动参考,生成代码的自觉性非常高。
第二,用优先级标记控制 AI 的实现顺序。Spec 里每一条规则我都习惯加一个优先级标签,比如 [P0] 表示不做会出大事,[P1] 表示应该做,[P2] 表示有更好。AI 生成代码的时候,它会先实现 P0,再考虑 P1,最后才管 P2。这招在长模块开发里特别管用,以前我让它“先搭个框架”,结果它把所有功能都平铺开来一次性实现,没有主次,现在有了优先级标签,它反而知道什么该放主路径。
第三,遇到 Spec 歧义,回到现实业务里找答案,而不是问 AI。有一次我让 AI 生成退款功能,它向我确认“退款要不要走原路退回”,我说“你按普通方案处理”,等代码出来才发现业务方要的是“仅原路退回”。这种问题根源不是技术,而是需求本身没定。AI 能帮你处理“怎么做”,但“做什么”必须由人来拍板。所以我在很多事情上都特别警惕:尽量不让 AI 替我做业务决策,我有空就先把业务规则研究透,再写进 Spec。业务上拿不准的时候,最快的路径是去问真实用户,而不是问 AI。
4.3 工具链建议:用哪个环节承载 Spec 最合适
最后一个经常被问的问题:字段级 Spec 应该存在哪?对话式 AI、IDE 插件、代码仓库文档,到底放哪里最合适?我的答案是:存在代码仓库的文档目录里,又或者放在你的知识库里,喂 AI 的时候随时能整份复制。
对话式 AI 用的是“粘贴式”,适合一次性需求,把 Spec 贴进去,生成完就结束,优点是灵活,缺点是对话一长,AI 容易把早期规则忘掉。IDE 类 AI 工具用的是“参考式”,它会自己读取项目里的 Spec 文件,生成时自动遵守,优点是可以持续生效,缺点是如果文件太多太乱,它也分不清哪个 Spec 对应哪个模块,所以文件命名一定要清晰,比如customer_spec.md、order_spec.md这样。
很多人纠结“我是不是一定要用最贵的模型”。以我的观察,模型的差距远小于 Spec 质量造成的差距。你给开源模型一份无懈可击的字段级 Spec,它一样能生成能用的代码;你给顶级模型一句模糊需求,它再聪明也只能给你一个概率最高的“标准答案”。先把 Spec 写明白,再谈模型选型。工具链上我有两条建议:一,代码生成用带项目上下文感知的工具,它能把 Spec 和现有代码结构联系起来;二,需求澄清阶段用对话式 AI 当陪聊,帮我想出我遗漏的问题,但最终的字段定义一定要自己读一遍、过一遍脑子再定稿。
写在最后的一点点个人体会
我踩过很多坑以后,最大的体会是:AI 写代码这事,真正值钱的工作不是“写”,而是“描述”。描述得越精确,AI 的发挥空间越小,代码质量反而越高。现在我基本不在 AI 生成之后再逐行 review 代码了,因为我知道只要 Spec 没有漏洞,它生成的东西就能放心用。
有一个小技巧我几乎每个项目都会用:在生成代码之前,先把 Spec 当作一条条“验收口令”发给 AI,让它按字段逐个念一遍并解释它打算怎么实现。这个过程非常神奇,你会在它一句句复述里发现你写的哪些规则不够严谨、哪些字段存在语义歧义。等它把整份 Spec 的意图“反讲”一遍,你心里就有底了:这份图纸已经严丝合缝。再往后,AI 生成的代码是不是一次过,已经没那么重要了,因为你知道,只要按 Spec 来检,任何偏差都能在两三句话之内被纠正回来。