3348 字
17 分钟
从 Prompt 到 Spec / SDD:AI Coding 中的需求约束

从 Prompt 到 Spec / SDD:AI Coding 中的需求约束#

本文是 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,不追求长,而是追求边界清楚。通常会先写这些内容:

  1. 背景:为什么要做这个功能,当前问题是什么。
  2. 目标:本次任务最终要完成什么。
  3. 非目标:本次明确不做什么,哪些事情即使相关也先不处理。
  4. 输入与输出:数据从哪里来,结果应该是什么。
  5. 成功标准:什么情况下可以认为任务完成。
  6. 约束条件:技术栈、兼容性、性能、样式、接口范围。
  7. 验证方式:如何检查实现是否正确。

这里面我最重视的是“非目标”。

很多 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 等。

我目前不会急着把它们写成工具清单,也不会在没有充分调研的情况下判断哪一个更适合长期使用。它们具体怎么设计、适合什么场景,还需要后续结合真实项目交叉验证。

相比工具本身的热度,我更关心它们是否真的能解决几个问题:

  1. 能否把需求稳定沉淀在仓库中;
  2. 能否区分需求、设计和任务;
  3. 能否被不同 Coding Agent 复用;
  4. 能否和 docs、AGENTS.md、测试流程结合;
  5. 是否适合个人项目,而不是只适合团队或大型工程。

还有一个现实问题:不论是规范文档还是 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 更重要。