从 Prompt 到 Spec / SDD:AI Coding 中的需求约束
本文是 AI Coding 系列 的第二篇。
1. 为什么我不想只讨论 prompt 技巧
这篇文章不是 Prompt 技巧大全。
我当然不否认 prompt 的价值。很多时候,一句清楚的 prompt 就能让 AI 解释一个报错、生成一段局部代码,或者给出一个可以继续修改的思路。对小任务来说,这种方式轻、快、直接,确实有效。
但前言里我已经把 AI Coding 理解成一次工程工作流的重新组织。真正进入项目之后,问题很快就不只是“这句话怎么问模型效果更好”,而是需求边界怎么表达、设计约束怎么保留、修改范围怎么控制、结果怎么验证。
很多人包括我在内,第一次使用 AI Coding 时的反应都是直接写 prompt:帮我实现这个功能,帮我修这个 bug,帮我重构这段代码。这个入口没有问题,问题在于它很容易被误用成全部工作流。
Prompt 让 AI 开始工作,Spec / SDD 让 AI 在正确范围内工作。后者才是这篇文章真正想讨论的部分。
2. 一个能跑但很难维护的识别项目
我之前做过一个识别类项目。项目本身不需要特别复杂的业务逻辑,核心就是准备数据、训练模型、保存结果,再提供一段推理流程。
一开始我没有做太多设计,只是把需求直接输入给 AI,让它生成对应的训练和推理代码。最后项目确实可以运行,功能也基本实现了。从“能不能跑”的角度看,这次 AI Coding 是成功的。
但后续维护几乎不可行。
大量参数被硬编码在不同文件的不同位置。数据路径、模型参数、训练配置、推理阈值等内容都散落在代码角落里。项目看起来能跑,但任何一次调整都需要重新翻找多个文件。有时候只是想改一个阈值,也要先判断它到底写在推理脚本里、训练配置里,还是某个临时函数里。
后来我要求它重构代码,并明确加入了类似“将可配置参数集中整理到 YAML 文件中”“不要在项目代码中强制硬编码训练、推理和路径参数”这样的约束,代码结构才逐渐接近可维护的状态。
这件事让我重新看待 AI Coding 里的“会写代码”。问题不是 AI 不会实现功能,而是没有设计约束时,它往往会选择当前最容易生成的路径。当前最容易生成的路径,不一定是最适合长期维护的路径。
只描述“我要实现什么”是不够的。真实项目还需要说明“这个功能应该如何组织”“哪些参数应该配置化”“哪些逻辑不能写死”“哪些文件承担什么职责”。这些内容就是我开始重视 Spec / SDD 的直接原因。
3. 问题不在于 AI 不会写,而在于缺少边界
同一个需求可以有很多实现方式。
一个功能可以放在页面层,也可以下沉到组件层;状态可以保存在客户端,也可以从服务端派生;接口可以新增,也可以复用旧接口;一次修改可以集中在少数文件内完成,也可能扩散到多个模块。
开发者在做这些判断时,通常会带着项目经验:哪些地方历史包袱重,哪些接口不要轻易动,哪些参数不能写死,哪些改动会影响旧功能。很多判断并不会被写在 prompt 里,因为我们自己已经习惯了这些上下文。
AI 不天然拥有这些判断。如果没有明确边界,它很容易自行补全需求,把局部问题扩展成大规模重构,或者生成看起来合理但不符合项目风格的代码。它能完成任务,但完成方式未必可控。
这不是单纯的模型能力问题,而是工程约束没有被明确表达。AI 能力越强,生成和修改的速度越快,越需要提前定义边界。否则它不仅能更快写出代码,也能更快写出偏离项目约束的代码。
AI Coding 越强,越需要边界;自动化程度越高,越需要约束。
4. Prompt、Spec、SDD 分别解决什么问题
这里不做概念科普,只按我在 AI Coding 工作流里的理解来分。
Prompt 是一次性的任务指令。它适合启动任务、描述局部问题、分析报错、生成片段。它的优势是轻,但它很难长期保存项目决策。
Spec 是需求约束。它回答“要做什么、为什么做、做到什么程度、明确不做什么”。它的价值是把一句话需求变成稳定文本,减少 AI 自行脑补需求的空间。
SDD 是设计约束。它回答“怎么组织实现、涉及哪些模块、修改哪些文件、如何验证、后续如何维护”。它的价值是让 AI 的实现建立在项目结构之上,而不是建立在一次临时回答之上。
Prompt 让 AI 开始工作,Spec 约束方向,SDD 约束结构。
5. 我现在会在 Spec 里写清楚什么
我现在写 Spec,不追求长,而是追求边界清楚。通常会先写这些内容:
- 背景:为什么要做这个功能,当前问题是什么。
- 目标:本次任务最终要完成什么。
- 非目标:本次明确不做什么,哪些事情即使相关也先不处理。
- 输入与输出:数据从哪里来,结果应该是什么。
- 成功标准:什么情况下可以认为任务完成。
- 约束条件:技术栈、兼容性、性能、样式、接口范围。
- 验证方式:如何检查实现是否正确。
这里面我最重视的是“非目标”。
很多 AI Coding 的失控修改,不是模型不会做,而是它做得太多。一个需求如果只写“实现文章搜索”,AI 可能会同时改搜索 UI、改索引策略、改样式、加缓存,甚至顺手重构项目列表。也许其中有些改动是合理的,但它们未必属于本次任务。
Spec 不只是告诉 AI 要做什么,更重要的是告诉 AI 不要做什么。自由只存在于围栏之中,这句话放在 AI Coding 里很合适。
6. 我现在会在 SDD 里写清楚什么
Spec 解决的是方向,SDD 解决的是结构。对我来说,SDD 里最值得写清楚的是这些工程判断。
第一,功能放在哪里。它应该放在页面层、组件层、业务逻辑层,还是单独的工具模块。这个判断不写清楚,AI 很容易把逻辑塞进最顺手的文件里。
第二,状态由谁维护。它是客户端状态、服务端数据,还是从已有数据中派生出来的结果。状态位置一旦选错,后续代码会越来越难整理。
第三,接口优先复用还是新增。如果旧接口可以满足需求,我个人更倾向优先复用旧接口,而不是轻易新增一套接口。新增接口看起来清爽,但也会带来新的维护成本。
第四,参数是否允许硬编码。路径、阈值、模型参数、训练配置这类内容,通常应该进入配置文件,而不是散落在代码中。前面那个识别项目就是很直接的教训。
第五,哪些文件可以改,哪些文件不能改。修改范围必须明确,否则 AI 很容易把局部任务扩散成多文件重构。它不是故意添乱,它只是没有被告知边界在哪里。
第六,如何验证这次修改是正确的。不只是“能跑”,还要说明需要通过哪些测试、检查哪些输出、是否影响旧功能。
没有 SDD 的 AI Coding,很容易变成“当前能跑就行”的代码生成。这样的代码短期可能有效,但它未必尊重模块边界,也未必能被后续开发者稳定维护。
7. 工具只是辅助,先不要急着选型
围绕 Spec / SDD,社区已经出现了一些相关工具和实践,例如 OpenSpec、OPSX workflow、GitHub Spec Kit、Kiro Specs 等。
我目前不会急着把它们写成工具清单,也不会在没有充分调研的情况下判断哪一个更适合长期使用。它们具体怎么设计、适合什么场景,还需要后续结合真实项目交叉验证。
相比工具本身的热度,我更关心它们是否真的能解决几个问题:
- 能否把需求稳定沉淀在仓库中;
- 能否区分需求、设计和任务;
- 能否被不同 Coding Agent 复用;
- 能否和 docs、AGENTS.md、测试流程结合;
- 是否适合个人项目,而不是只适合团队或大型工程。
还有一个现实问题:不论是规范文档还是 skills 文档,只要被 AI 读取,就会消耗上下文窗口和 token 成本。这也是我认为文档不能无限堆,必须精简、分层、持续维护的原因。穷人用 token,主打一个精打细算。
工具不是重点,需求约束的结构才是重点。
8. 一个适合个人项目的轻量 Spec / SDD 模板
个人项目不一定需要很重的文档流程。小任务可以只写轻量 Spec,中等任务可以写 Spec + 简短 SDD,大型重构或多 Agent 任务则必须写清楚修改范围和验证方式。
模板的目的不是增加形式感,而是让 AI Coding 更可控。
轻量 Spec 可以这样写:
# Feature Spec: 功能名称
## Background为什么要做这个功能。
## Goal本次任务要完成什么。
## Non-Goals本次明确不做什么。
## User Flow用户或调用方如何使用这个功能。
## Requirements- 需求 1- 需求 2- 需求 3
## Constraints- 技术约束- 文件范围- 兼容性要求- 不允许修改的内容
## Success Criteria- 如何判断完成- 需要通过哪些测试- 需要人工检查什么
## Risks- 可能影响哪些旧逻辑- 哪些地方需要特别注意轻量 SDD 可以这样写:
# Software Design: 功能名称
## Related Files- 相关文件 1- 相关文件 2
## Current Behavior当前逻辑是什么。
## Proposed Design准备如何实现。
## Data Flow数据如何流动。
## API / Interface Changes接口是否变化。
## Modification Scope允许修改哪些文件,不允许修改哪些文件。
## Test Plan需要哪些测试或验证。
## Maintenance Notes后续维护需要注意什么。实际使用时,不需要每个字段都写很长。小任务可以每节只写一两句话,复杂任务再展开。我的做法通常更偷懒一点:先把模糊需求和模板一起丢给 AI,让它帮我整理成 Spec / SDD 草稿;我确认边界之后,再把整理后的文档喂给 Coding Agent 执行。原汤化原食,味真足。
9. Spec / SDD 和 TDD 的关系
Spec / SDD 解决的是生成前的不确定性。它们约束需求方向、设计结构和修改范围。
TDD、单元测试、端到端验证、回归检查解决的是生成后的不确定性。它们回答的是另一类问题:代码生成之后,怎么证明它没有偏离预期,没有破坏旧功能。
这两件事不能互相替代。Spec / SDD 写得再好,也不能代替测试;测试再完整,也不能自动告诉 AI 一开始应该往哪个方向设计。
Spec / SDD 让 AI 知道应该怎么做,TDD 让我们知道它做得对不对。
TDD 后面会单独展开,这里只先放一个位置。
10. 结尾:我真正想减少的是不确定性
Spec / SDD 不是为了让个人项目变得笨重,也不是为了给每个小改动都补一套完整文档。它的意义是减少 AI Coding 中的不确定性。
当 AI 能越来越多地参与代码修改时,开发者更需要把需求、边界、设计和验证方式说清楚。否则 AI 生成得越快,项目偏离约束的速度也可能越快。
Prompt 可以启动任务,但不能长期约束项目。Spec / SDD 的价值,是让 AI 在正确范围内工作。
我并不认为每一个小改动都需要完整的设计文档,但我越来越确信:当 AI Coding 开始进入真实项目时,稳定的需求约束会比一次漂亮的 prompt 更重要。