跳到正文

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 0

CI 检查规范引用的源码路径是否还存在:

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、边界认知

把一门技术讲透到既懂也会用