主题
SDD 生产踩坑与最佳实践
一句话:SDD 生产里最常见的失败不是「AI 写错代码」,而是「规范和代码各走各的路」;下面六个坑按「现象 → 根因 → 处置」组织,最后给出可执行的检查项。
为什么需要
SDD 的流程不复杂,难的是让它持续运转。上线一段时间后,团队通常会同时遇到六个问题:规范过期、规范过细、跳过审核门、上下文漂移、测试后补、工具碎片化。它们的共同点是——都不是模型能力问题,而是流程和所有权问题。
核心机制
坑 1:规范过期
现象与根因:规范描述的是旧架构,新成员按规范理解系统却和代码对不上;根因是规范被当成一次性文档,没有 Owner,也没有检查机制。
处置:
- 规范进 git,和代码同 PR;只改代码不改规范时给出警告。
- 每个
specs/子目录指定 Owner;修改别人的规范要在 PR 里通知。 - CI 定期检查规范中引用的路径、类名、接口是否仍存在(脚本见「最小上手」)。
- 用增量记录变更(ADDED / MODIFIED / REMOVED),避免每次重写全文。
坑 2:规范过细
现象与根因:规范写到 API 参数默认值、数据库字段类型,需求微调就要同步改多处;根因是把规范当成实现文档。
处置:分三层放信息。
| 层级 | 内容 | 变化频率 | 放哪里 |
|---|---|---|---|
| 目标层 | 功能目的、用户价值 | 低 | 规范 |
| 行为层 | 可观察的系统行为、异常 | 中 | 规范(Given/When/Then) |
| 实现层 | 类名、表结构、参数默认值 | 高 | design.md |
判断方法:这行内容改了,用户可感知的行为变不变?不变就不该写在主规范里。
坑 3:跳过审核门
现象与根因:觉得需求简单就直接让 AI 实现,生成后才发现漏了团队约定;根因是人觉得显而易见的上下文(历史、约定、行业规则),AI 并不知道。
处置:
- 轻量门(5 分钟内):一句话规范 + AI 复述理解 + 人确认,然后实现。
- 完整门:规范 → 设计 → 任务,每步确认后进入下一步。
- 紧急通道:允许先修,但 PR 里写明问题、方案、影响范围、验证方式,并指定 24 小时内补全规范的责任人。
坑 4:上下文漂移
现象与根因:前几个任务执行良好,后面开始忽略早期约束。长会话中早期信息的权重会被稀释,具体阈值随模型、任务长度和上下文组织方式变化,不要背固定轮数。
处置:
- 子 Agent 隔离:每个任务用独立 Agent,只带规范 + 当前任务,适合任务多、依赖少的场景。
- 规范重注入:每完成几个任务重读一次规范全文,适合中等复杂度。
- 人工检查点:每完成一个模块对照规范检查,最可靠。
- 拆分任务:把大任务拆成有明确验收的小任务。
- 有文件依赖的任务要串行;并行只适合互不重叠的改动。
坑 5:测试后补
现象与根因:先让 AI 写代码,再根据代码写测试;测试全绿,上线后却发现逻辑和需求不符。因为测试是照着实现写的,验证的是「代码做了什么」,不是「需求要求什么」。
处置:核心路径强制 TDD,非核心路径合入前补齐。
text
需求:密码至少 8 位,且包含大小写字母和数字。
错误做法:先写实现(只校验长度),再照着实现写测试 → 全绿但需求没满足。
正确做法:先写三条失败测试:长度 < 8 拒绝;只有小写字母拒绝;大小写 + 数字且长度 ≥ 8 通过。
实现必须让三条都通过。分层强制:支付、认证、数据变更必须先有失败测试;普通 CRUD 允许先实现,但测试必须在合入前补齐。不要追求 100% TDD,也不要接受 0%。
坑 6:工具碎片化
现象与根因:前端用 Cursor,后端用 Claude Code,运维用 Copilot;规范放在各自工具的配置目录里,互相找不到,也没人敢改。根因是没有统一的规范存放位置和权威来源。
处置:
- 规范统一放
specs/,进 git;各工具配置只做指针。 - 用
AGENTS.md放通用约束(支持的工具会自动读,不支持的由规则文件引用)。 - 冲突时以仓库规范为准,工具配置让位。
- 指定一个 Owner 维护跨工具约定,避免六份规则各自演化。
最小上手
两个检查,先跑 warning 模式。pre-commit 在代码变更没有伴随规范变更时提醒:
sh
#!/bin/sh
# .githooks/pre-commit —— 只警告,不阻塞;启用:
# git config core.hooksPath .githooks && chmod +x .githooks/pre-commit
changed=$(git diff --cached --name-only)
code=$(printf '%s\n' "$changed" | grep -E '^(src|app)/' || true)
spec=$(printf '%s\n' "$changed" | grep -E '^specs/' || true)
if [ -n "$code" ] && [ -z "$spec" ]; then
echo "WARN: source changed without specs/ change; check if spec is affected" >&2
fi
exit 0CI 检查规范引用的源码路径是否还存在:
sh
grep -rhoE '`(src|app)/[^`]+`' specs 2>/dev/null | tr -d '`' | sort -u |
while read -r p; do
[ -e "$p" ] || echo "stale spec path: $p"
done这是启发式检查,只能发现「路径消失」,发现不了「行为变了」。跑通的标志:删掉一个被规范引用的文件,CI 能报出 stale spec path。
生产动作
- 推广分三步:选 1–2 个返工最多的模块试点 → 把有效做法固化成模板 → 用试点数据说服其他模块。记录返工次数、Review 问题数、线上事故数;样本小就说明样本小。
- Hook 先 warning 后 blocking。 先让误报可见并记录,团队适应后再收紧;直接阻塞容易被绕过,也会引起反弹。
- 核心路径强制 TDD,非核心路径合入前补齐。 这是质量底线和交付速度的折中。
- 规范 Owner + 月度回顾。 清理过期规范,合并已完成的增量,检查规范数量是否增长过快。
- 工具升级后重跑检查。 配置项改名、命令变更会让旧的 hook 静默失效。
生产检查项
上线前:
- [ ] 每个改动模块有规范 Owner,规范与代码在同一 PR。
- [ ] 验收标准至少一条可执行(命令或测试),没有「运行良好」这类表述。
- [ ] 主规范里没有类名、表结构、参数默认值;这些在
design.md。
运行中:
- [ ] CI 每周检查规范引用的路径是否存在,报告有人处理。
- [ ] pre-commit 在 warning 模式运行,误报有记录、有收敛计划。
- [ ] 长任务使用子 Agent 或定期重读规范,产物可追溯到任务。
故障时:
- [ ] 线上 bug 能定位到对应规范条目;定位不到就补规范。
- [ ] 紧急修复走了轻量门,并在 24 小时内补全正式规范。
边界
- 生命周期很短的项目:规范还没写完项目就结束了,投入产出倒挂。
- 需求完全不确定的探索:先做原型探路,方向确定后再补规范。
- 微型项目:一个人、几千行、单一功能,流程开销可能大于编码开销。
- SDD 保证「做对事情」,不保证「做对的事情」。 产品方向错了,规范只会让你更快地做错。
- Hook 是提醒,不是强制。 有
--no-verify、有直接改文件、有 CI 之外的路径;真正的门在测试和 Review。
讲清楚
SDD 生产里最难的不是写第一份规范,而是让规范活下去。六个坑可以归成三类:规范过期和过细,是维护方式问题;跳过审核门和上下文漂移,是流程执行问题;测试后补和工具碎片化,是质量门和所有权问题。处置的核心是三条:规范放仓库、和代码同 PR;规范只写用户可感知的行为,实现细节放设计文档;核心路径先写失败测试,用测试而不是口头声明来定义完成。推广时从最痛的模块试点,先 warning 后 blocking,用试点数据说话。最后要承认边界:SDD 能保证按约定做对,不能保证约定本身正确。
初稿 2026-07 | 最后修订 2026-10 | 事实核查:移除无出处的时长/比例数字与不可移植脚本;并入原 08 篇的推广路径、分层 TDD、边界认知