SDD/TDD非银弹:复杂需求中流程反成负担
SDD/TDD 并非万能:面对复杂需求时,流程也可能成为阻碍
在正式展开 SDD 教程前,有必要先阐明一个前提。
如果只渲染 SDD 的优势、TDD 的稳健和 AI 的强大,容易让人误以为这些工具能确保项目一帆风顺。
但近期实战中踩过的坑,真实体感恰恰相反:
SDD、TDD、AI,归根结底只是工具。
每项工具都有适用的边界——哪怕再高效的方案,用错场景也会变成累赘。在合适的场景中,它们能显著提升稳定性和协作效率;一旦场景不匹配,它们带来的额外工作量甚至可能超过手工编码。
本文并非唱反调,更不是劝退。后续仍会继续输出 SDD 相关教程。但在开始之前,需要把这个核心前提讲透:
一、重新界定 SDD/TDD 的适用边界
最初接触 SDD、TDD 和 AI 编程时,很容易被一套叙事逻辑吸引:先写 Spec 能理清需求;先写测试能保证质量;交给 AI 能提速;Agent 自动拆任务能让复杂项目可控。
这些说法本身没错。
但它们隐含一个前提:需求从一开始就是清晰、稳定、可完整定义的。
然而现实中大量复杂需求并非如此。有些需求初始就模糊,有些业务规则在开发中才发现不合理,有些边界在接入真实数据后才暴露,还有许多历史逻辑没有文档,只能靠啃代码和调试慢慢摸索。
在此前提下,如果一上来就要求写完整的 Spec、测试和任务拆解,往往适得其反:Spec 改一次,实现跟着改一次;测试改一次,任务清单又得改一次;AI 基于已过期的上下文生成不匹配的代码。
最终感受是:流程走得很完整,但人累得够呛,效率反而不如预期。
二、SDD 的价值不在“写大量文档”
对 SDD 的理解也在实践中发生了转变。
过去容易将 SDD 视为“文档优先”——先写清楚再动手。现在更愿意理解为“风险优先”——哪些地方容易出错,就先锁定哪些地方。
这两种理解差异巨大。如果把 SDD 当作“文档优先”,很容易写出大段自然语言描述,看似完整,但未必能真正降低风险。真正有价值的 Spec,应锁定这些内容:不可错的业务规则、不可随意变更的状态流转、对外暴露的 API 契约、金额/库存/权限/安全等高危规则,以及未来接手者最易误解的边界。
换言之,SDD 本质上不是为了彰显流程正式,而是为了降低关键路径上的错误成本。判断一份 Spec 是否有效的标准很简单:如果写完后,开发和维护中“猜测”的次数减少了,那就是有价值的;如果它只是让流程更繁复,那就是负担。
三、什么场景适合使用 SDD?
现在我会优先在以下场景使用 SDD——并且采用轻量级方式,而非一上来就长篇大论。
1. 规则确定且不容出错
支付、结算、库存、权限、状态机、审批流、合规规则——这些领域一旦出错代价极高。提前写清规则,成本远低于事后补救。
2. 对外契约需保持稳定
API 的输入输出、字段语义、错误码、事件格式。一旦被外部依赖,就不能随意修改。提前用 Spec 固化,可减少大量后续扯皮和返工。
3. 多人协作且生命周期较长
如果一个模块未来会有多人维护,或会存活几个月甚至更久,Spec 的价值将显著提升。它服务的不仅是当下开发,更包括未来的交接。
4. 规则易被误解
有些代码本身无法直观体现业务意图。例如:为什么此处不能 early return?为什么这个状态不允许回退?为什么不能吞掉该异常?为什么字段为空时不能用默认值?这些地方非常适合写点状 Spec——无需长篇,只要把“为什么不能乱改”讲清楚即可。
四、什么场景不适合重型 SDD?
并非所有需求都值得启用完整的 SDD 流程。以下场景我会非常节制。
1. 探索期需求
如果需求本身仍在试错阶段——比如新业务方向、交互方案、算法策略、运营活动——重型 Spec 极易成为负担。今天写下的设计,明天可能就被推翻。此时更适合:快速拆小、快速验证、跑通最小闭环、稳定后再回填关键规则。
2. 单人短周期任务
如果你独自完成,且生命周期很短——例如一次性脚本、临时工具、小范围修复——完整的 SDD 往往不划算。花在写 Spec 上的时间,可能已足够将功能写完并验证完毕。
3. UI 与体验型需求
多数 UI 需求不是靠 Spec 推导出来的,而是靠看、靠试、靠调。间距、动画、交互反馈、页面节奏——这类需求可以有设计规范和验收标准,但不一定适合编写重型 SDD。
4. 高频变化的业务
如果需求每天都在变,重型 Spec 会迅速过期。过期的文档比没有文档更危险——它会给人“这里已被定义过”的错觉。
五、TDD 同样如此:并非每个场景都必须先写测试
TDD 的价值同样巨大。它强迫你预先思考输入输出,让重构更安全,还能将关键规则转化为可执行的文档。
但它同样不是万能药。需求尚未稳定时,如果过早编写大量测试,后续就得频繁修改。此时测试不再是安全网,反而成为变化的阻力。
适合先写测试的场景:纯函数、规则引擎、金额计算、状态流转、权限判断、bug 复现、已稳定的核心逻辑。
不必强行先写测试的场景:UI 探索、原型验证、需求快速试错、一次性迁移脚本、临时运营逻辑。
当然,“不先写测试”不等于“不测试”。合理做法是:高风险逻辑必须测,低风险高变化的逻辑先验证闭环,稳定后再补测试。
六、更推荐的方式:核心硬化,外围敏捷
现在更倾向一种混合策略:将代码划分为“核心路径”和“非核心路径”,分别采取不同的管理强度。
核心路径:用 SDD/TDD 锁定
业务不变量、状态机、金额计算、权限规则、对外契约、数据一致性——这些地方可以更严谨:写轻量 Spec、写类型约束、写 Schema、写测试,让 AI 按规则生成代码,再由人工 review。
非核心路径:快速拆解,动态推进
页面展示、胶水代码、临时流程、简单数据转换、原型验证——这些地方不必一开始就写重型 Spec。可以先拆成小任务,人工判断方向,AI 辅助铺代码。等稳定后,再把真正重要的规则沉淀下来。
七、一份更实用的判断清单
以后遇到一个需求,我会先问自己五个问题。
1. 这件事错了,代价高吗?
如果会导致资损、数据不可逆、安全问题、合规问题,就值得上 SDD/TDD。如果只是 UI 展示不对、文案有误、可快速回滚,就不一定要堆流程。
2. 规则现在稳定吗?
如果规则已经明确,适合先写 Spec 和测试。如果规则仍在探索,先别急着文档化。
3. 未来会被多人维护吗?
如果是,写清楚很重要。如果只是一次性任务,文档成本要谨慎。
4. 这个规则能否变成可执行约束?
相比写大段自然语言,更推荐优先使用 TypeScript 类型、JSON Schema、OpenAPI、单元测试、状态机表、数据库约束。这些不仅供人阅读,也能被工具和 AI 消费。
5. 我现在是在解决问题,还是在维护流程?
这是最关键的问题。如果发现自己花大量时间同步 Spec、同步任务、同步测试,而业务本身没有进展,就该停下来重新评估。流程应当推动你前进,而不是拖着你走。
八、AI + SDD/TDD 的正确协作关系
AI、SDD、TDD 并非互相替代的关系,而是彼此配合的关系。
更合理的组合是:SDD 用于钉住关键规则,TDD 用于验证高风险逻辑,AI 用于执行、补齐、批量同步,人负责判断边界和取舍。
如果顺序颠倒,就容易出问题。例如先让 AI 根据模糊指令生成完整 Spec,再基于 Spec 生成完整测试,最后让 AI 写代码——看似自动化,实则风险极高,因为初始 Spec 可能本身就是错的。
更稳妥的方式是:人先锁定核心规则,AI 去执行和补齐,人再做 review 和判断。AI 能提升产出速度,但不能替代人对方向的判断。
九、给自己定下的实践原则
后续继续写 SDD 教程时,我会遵循这几条原则。
1. 不追求全量 SDD——只在关键规则、关键契约、关键风险点上使用。
2. 不把 Spec 写成作文——能用类型、Schema、测试表达的,就不只写自然语言。
3. 不在探索期过早重流程——先跑通,后沉淀。
4. 不为了 TDD 而 TDD——高风险逻辑优先测试,低风险高变化逻辑先保证反馈速度。
5. 不让 AI 替我做最终判断——AI 可以建议、生成、review,但最终边界由人负责。
十、结语:方法论的价值,在于用对地方
这篇文章作为 SDD 教程的前言,旨在先把一个底层观点讲清楚:不要因为外界鼓吹 AI 强大,就把所有任务都交给 AI;不要因为 SDD 看起来更工程化,就给所有需求套上完整流程;不要因为 TDD 代表质量,就在需求尚未稳定时编写一堆很快过期的测试。
真正成熟的做法,是承认每种方法都有成本,然后判断这份成本是否值得。
可以总结成这样的公式:
高效开发 = 核心路径的严谨锁死 + 非核心路径的轻量推进 + AI 的精准辅助 + 人的边界判断
后续的 SDD 教程,也将基于这个前提来写。不是教大家把流程做重,而是教大家:哪些地方必须写清楚,哪些地方可以轻一点,哪些地方该让 AI 帮忙,哪些地方人必须自行判断。
因为工程方法的目标,从来不是让流程看起来完整,而是让我们更稳定、更清醒、更高效地把事情做成。
