1. 从“氛围编程”说起:一个被忽视的效率杀手
1.1 “氛围编程”是怎么流行起来的
不知道从什么时候开始,办公室里开始流行一种“凭感觉写代码”的开发方式。需求文档只写了个大概,交互稿还停留在线框图阶段,评审会开了个寂寞,大家散会之后互相对一下眼神,觉得“差不多就是这样了”。然后前端开始搭页面,后端开始写接口,等到联调那天,字段名对不上、状态机流转绕晕人、异常分支谁也没考虑,一天时间全耗在“这不是我的问题”的扯皮上。
我最早听到“氛围编程”这个词,是在一次跨团队复盘会上。有人说我们现在的项目开发就是“氛围编程”——大家不是靠规范、靠契约、靠验收标准推进的,而是靠会议氛围、聊天记录、临时口头对齐来推进的。当时觉得这句话有点调侃,但细想一下,真实得让人难受。
所谓氛围编程,本质上就是开发决策没有沉淀成可查阅、可执行、可验证的规范文本,而是散落在聊天记录和模糊记忆里。它表现为:代码写得挺快,但没人说得清楚某个字段为什么叫这个名字;接口一次调用能通,但换个场景就崩;产品说“这里逻辑很简单”,开发听了之后“感觉懂了”,做完之后发现根本不是同一件事。尤其是团队稍微大一点、跨端跨部门合作多一点的时候,这种隐患会被放大得非常明显。
1.2 氛围编程到底付出了什么代价
讲一个我们真实踩过的坑。之前做订单模块重构,产品在评审会上用两句话描述了退款规则:“用户申请退款,如果还没发货就直接退,如果发货了就走售后流程。”大家觉得听明白了,开发直接开写。结果后端把“发货”理解成仓库出库单创建,前端把“发货”理解成物流单号回填,测试把“发货”理解成订单状态变为已发货。三个角色,三种理解,上线前测出来八个逻辑漏洞,改了整整一周。
这就是典型的氛围编程代价。表面上看,团队省掉了写文档的时间,实际上把时间加倍花在了沟通、返工、扯皮和背锅上。而且这种代价不在当期暴露,它会在项目中期爆发,并在人员变动时变成灾难。老员工一走,新员工只能靠翻代码猜业务意图,猜错了就埋雷。
更隐蔽的一个代价是,氛围编程会让“认真写规范”这件事情显得很另类。团队文化一旦形成,谁要是提出“先写个技术方案再动手”,反而会被说效率低。这种氛围对新人尤其不友好,他们不敢问“为什么”,只能靠模仿和老员工的只言片语来摸索,最终整个团队的代码风格越来越散乱,技术债越堆越高。
所以我和团队后来做了一个很明确的方向调整:把“规范”重新请回开发流程的核心位置,用SDD(Specification-Driven Development,规范驱动开发)来取代之前那个靠氛围推进的局面。这篇文章就把我们这段时间的实践思路、模板和踩坑经验完整梳理一遍,适合那些正在被需求理解不一致、跨端联调困难、AI生成代码不可控等问题困扰的团队和开发者参考。
2. SDD究竟是什么,它和TDD、文档驱动有什么不一样
2.1 SDD的核心定义和工作流程
SDD,全称Specification-Driven Development,核心思想非常朴素:先写清楚“做什么、做到什么程度算好”,再开始写代码。它要求开发者在动手之前,先把需求翻译成一份足够精确的规格说明,这份说明包含功能行为、输入输出、边界条件、异常处理、验收标准等,让开发和测试都照着同一份文本来执行。
SDD不是拍脑袋发明的新概念,它是综合了传统软件工程中的需求分析、接口契约设计、测试用例前置等实践,重新整理出来的一套流程。它和敏捷开发并不冲突,反而正好填补了敏捷中“用户故事粒度太粗、验收条件不清晰”的短板。
我们团队现在的标准流程是这样的:需求评审通过之后,由技术负责人和核心开发一起编写SDD文档,文档评审通过之后,前端、后端、测试各自按文档开工;开发过程中如果发现文档有错漏,先改文档再改代码;联调阶段直接以文档里的字段定义和状态流转为准,有争议查文档,而不是翻聊天记录。这套流程跑顺之后,联调效率至少提升了一倍,产品验收的一次通过率也明显高了不少。
2.2 SDD与TDD、传统文档驱动的区别
很多人第一次接触SDD会问:这和TDD(测试驱动开发)有什么区别?和传统意义上的“先写设计文档再开发”又有什么区别?我简单梳理一下。
TDD的核心是“测试先行”,它关心的是“代码行为应该如何被验证”。开发者先写一个会失败的测试,再写最少的代码让它通过。TDD更偏向微观层面的编码实践,解决的是实现细节的正确性问题。但它本身不解决“需求理解不一致”的问题——测试用例写错了,代码再怎样通过测试也是白搭。
传统文档驱动则容易走极端,厚厚一本需求规格说明书,写完就已经过时了,代码和文档完全脱节,文档变成了一种仪式感的产物。很多团队都有这种经验,文档写了,但没人看,或者看了也没用,因为文档里的内容太抽象,开发还是不知道一个接口具体返回什么字段。
SDD恰好站在两者的中间地带。它比TDD更大的粒度,先定义整体行为契约,再用测试用例来固化其中的关键场景;它比传统文档更克制、更聚焦,只写那些“会影响开发实现和结果验证”的内容,不写项目背景、不写战略意义、不写废话。简单来说,SDD是一份开发与测试共同遵守的“精确施工图”,它的存在就是为了消灭“我觉得”“你以为”“他好像”这类模糊表达。
2.3 SDD的适用边界
SDD不是银弹,它有自己的适用范围。我的经验是,它对中大型需求、跨端协作需求、复杂状态流转需求效果非常明显。比如订单系统、支付系统、权限体系、优惠计算这一类逻辑分支多、出错代价高的模块,SDD能带来质的提升。
反过来,对于非常小的需求,比如改个按钮文案、调一下列表排序规则,走完整的SDD流程确实有点重。我们团队的做法是分级对待:S级和A级需求必须输出完整SDD文档;B级需求输出精简版,只写验收标准和关键改动点;C级需求直接在任务管理工具里描述清楚即可,不需要单独成档。
这么说吧,SDD解决的最大问题不是“编程”本身,而是“沟通”。它把多人协作中最大的一块隐性成本——信息在人与人之间传递时的损耗——显性化,然后用一份文档把它固定住。这就是它和氛围编程最根本的区别。
3. 实操落地:一份真正能用的SDD规范文档该怎么写
3.1 规范文档的核心要素与结构
很多团队写技术方案容易走两种极端:要么写成作文,大篇幅讲背景和意义,开发想看的具体内容一笔带过;要么写成流水账,罗列一堆接口名称,但边界条件全都没有。一份合格的SDD文档,我建议至少包含七个模块:需求背景、术语定义、功能拆解、接口契约、状态流转、异常处理、验收标准。
其中需求背景只需要一小段,目的是让后来看文档的人理解“这个功能为什么存在”。术语定义容易被忽略,但非常重要,像前文提到的“发货”这种词,必须在术语表里给出唯一定义。功能拆解要按用户可感知的维度切分,而不是按后端数据表的维度切分。接口契约要精确到字段级别,包含字段名、类型、是否必填、取值范围、示例值。状态流转必须画清楚状态机和触发条件,哪怕用文字穷举每条路径也比含糊带过强。异常处理要写清楚“不正常时系统该干什么”。验收标准则是整个文档的灵魂,后面单独讲。
我还要加一条关键词:所有描述必须可以被验证。比如“查询性能要好”这句话就不合格,因为不可验证;但“列表接口在100万条数据量下P95延迟小于800毫秒”就是合格的。写SDD的时候,每写一句话都问自己,这句话测试能不能据此写用例?如果连测试都写不出来,代码更不可能写对。
3.2 字段级接口契约和状态流转的写法
接口契约是SDD里最需要较真的部分。拿用户地址管理来举例,一份合格的接口文档里,起码要这样描述“省市区字段”:省份名称用provinceName,类型string,长度不超过32字符,必填;省份编码用provinceCode,类型string,符合GB/T 2260标准,选填,但当前端拿到该字段时必须优先展示编码对应的标准名称。这里还隐含一个规矩:同一个字段在全系统各端保持一致命名,严禁出现后端叫provinceName、前端叫province的情况。
我特意加了一条“枚举值变更必须走评审”的约定。很多联调事故都发生在枚举值上,比如订单状态后端返回的是数字0、1、2,前端以为对应待支付、已支付、已取消,结果后端的0其实代表已创建。SDD文档里要把这类映射关系直接列表写清楚,最好在接口契约里直接给出JSON示例,开发照着抄都不会错。
状态流转我建议用“前置条件 + 触发动作 + 后置条件”的模式来穷举。例如:订单状态从“待支付”流转到“已支付”,前置条件是订单属于当前用户且订单未超时且支付结果回调成功,触发动作是支付网关回调,后置条件是写支付流水、发送支付成功消息、如果存在库存预占则转为正式扣减。写清楚每个流转路径之后,开发只需要对着文档翻译成代码逻辑就行,不需要再自己脑补业务规则。
3.3 验收标准怎么写才不算“空话”
验收标准是我最看重的一个模块,也是绝大多数团队最不重视的模块。很多文档写到最后写一句“功能正常实现,符合需求”,这等于没写。我要求团队里的验收标准必须满足三个条件:可执行、可判定、无歧义。
一个比较实用的写法是采用“当…时,系统应该…”的句式,并且尽量数字化。比如:“当用户未登录访问订单列表时,系统应返回401错误码,前端跳转登录页,不允许出现空白页或无限loading。”再比如:“当退款金额超过原订单金额的10%时,系统应拦截该操作并返回错误码REFUND_AMOUNT_EXCEED,提示文案为‘退款金额异常’。”这种描述方式,测试可以直接转成用例,开发可以直接写成判断条件,产品验收时也有明确的勾选标准。
如果验收标准里涉及性能和安全,也要量化。并发量、响应时间、数据一致性级别都需要白纸黑字写清楚。例如:“订单创建接口在500并发下P95响应时间不超过500毫秒,且不允许出现重复订单号。”这句话写完,后端自然知道要加分布式ID生成器和唯一索引,不用等压测出了问题再补救。
4. SDD与AI辅助开发结合后的全新价值
4.1 AI编程时代为什么更需要SDD
现在AI辅助编程已经成为很多团队日常开发的标配,我也在用。但用了一段时间之后我发现一个问题:AI生成代码确实快,可它生成的是“大概率正确的代码”,不是“保证符合你业务约束的代码”。如果你给它一个大而化之的指令——“写一个订单查询接口”,它确实能写出来,但字段命名、状态判断、异常处理大概率和你团队的规范不一致。
这就是AI时代的氛围编程陷阱。过去氛围编程靠人和人之间传染,现在AI把这种不精确放大了,因为它对上下文里的模糊指令非常忠实。你给一个模糊的需求,它立刻返回一堆“看起来没问题”的代码,但这些代码往往经不起细节推敲。
SDD在这里的价值是,它给AI提供了一个精确的、结构化的输入。把写好的SDD文档直接喂给AI,让它基于规范去生成代码或做代码走查,输出的质量会截然不同。因为SDD包含了字段定义、状态流转、异常处理、验收标准这些AI最需要但平时最难自己获取的信息。
4.2 一套可行的“AI + SDD”工作流
我们现在的协作方式是这样的:人工负责写SDD文档,把业务规则和边界条件全部确定下来;AI负责在SDD的约束下快速生成代码初稿;人工负责评审AI生成的代码是否遵守了SDD,以及SDD本身是否还有漏洞。
实际操作时,我会给AI提供三个文件:SDD文档、团队编码规范、相关旧代码作为风格参考。然后提一个相对固定的需求模板,比如:“请参照SDD文档第3章接口契约实现订单列表查询接口,要求返回结构严格保持字段命名一致,分页参数使用pageNo和pageSize,异常场景按第6章处理逻辑输出错误码,不要自行新增字段和枚举值。”这样AI生成的代码基本可以直接进入代码评审环节,而不是推倒重来。
实测下来,这套工作流对我们团队的效率提升非常明显。尤其是那些模板化程度高的CRUD模块,AI结合SDD生成的代码能达到八九成可用度。人需要花时间的地方只剩业务规则特别复杂的核心模块,而这类模块本来也不适合全权交给AI。
4.3 给AI“喂”规范时的几个细节
给AI喂SDD文档的时候,我建议先把文档里的关键约束提取成“约束清单”,再和完整文档一起交给AI。因为完整文档可能很长,AI在上下文窗口里容易被信息稀释,对堆在后面的约束关注度下降。我把这个方法叫“先给结论再给上下文”,先让AI知道“你有这些红线不能碰”,再让它去阅读细节。
举个实际例子,有一次我让AI按SDD实现一个优惠券分摊功能。文档里写清楚了金额分摊时使用“最小单位分、向下取整、最后一个订单项补齐差额”的规则,但AI第一次生成的代码用的是四舍五入,导致整个订单的总优惠金额对不上。我把规则提取成约束清单,加上“严禁使用浮点数计算金额,一律使用整数分”这条之后,重新生成才符合要求。这个细节看似简单,但很多人和AI协作时习惯丢一个大文档过去,效果反而不稳定。
我们也在尝试让AI反过来审查SDD文档的完整性。把文档喂给它,让它扮演测试工程师,提出“这个规范里有哪些场景没考虑到”。很多时候确实能挖出一些盲区,比如登录态过期时接口返回什么、数据并发修改时怎么处理这类容易被遗漏的边界条件。这个用法很值得推广,本质上是在规范编写阶段就引入了一台“查漏补缺机”。
5. 推行SDD过程中一定会踩的坑
5.1 团队阻力与“文档无用论”
推行SDD最大的阻力不是技术问题,是观念问题。团队里一定会有人说:“写这些文档有什么用,代码才是唯一的真理。”这种声音通常来自两类人:一类是技术很强但没吃过协作亏的老手,一类是刚入行还分不清“业务理解”和“编码实现”的新人。前者的逻辑是代码能跑就行,后者的逻辑是跟着感觉走就行。
我的处理方式是,不搞一刀切,先选一个正在踩坑的模块做试点。当时我们选的是支付回调模块,刚出过一次线上事故,所有人都在气头上,这时候推SDD阻力最小。我们用两天时间把规范文档写出来,拉上前后端测试一起评审,改完再开发,上线之后效果立竿见影,回滚的次数直接清零。有了这次成功案例,再横向复制到其他模块,大家就没那么抵触了。
还有一个很现实的问题:谁写文档?如果所有文档都压在开发头上,开发会觉得这是额外负担。我们的做法是技术负责人和核心开发一起写初稿,再让测试补验收标准,产品负责提供业务规则和术语定义。各写自己最擅长的部分,过程反而很快。
5.2 文档腐败与代码偏离
推行一段时间后,你会碰到“文档腐败”的问题。文档刚写出来的时候是准的,时间一长,业务上改了逻辑,开发手动改了代码,但没人回去同步文档。慢慢地文档就变成了摆设,甚至文档写的和代码实现的完全是两个不同的东西,而这恰恰给后来的AI协作埋下大坑——AI读到的规范是过时的,生成出来的代码自然也是错的。
针对这个问题,我们把“文档更新”写进了开发的完成定义里:一个需求只有代码上线、文档更新、用例归档同步完成,才算真正结束。同时在代码评审的时候,如果发现实现和文档不一致,评审人有义务当场提出修改意见,要么改代码要么改文档,不允许带着不一致进入主干分支。
还有一个技巧,在SDD文档头部加一个“文档变更记录表”,每次修改必须追加一行,注明修改人、修改时间、修改原因。这个小动作成本很低,但能让每个人在动文档之前多想一想。我们团队自从加了这个表之后,随手改文档不通知别人的情况明显减少了。
5.3 快速上手的三个循序渐进建议
如果你也想在团队里推行SDD,我建议按三步走,不要一开始就追求“所有需求都写完整SDD”的完美状态。
第一步,先建模板。从过往做得最痛苦的一个项目里总结出必要模块,做一份团队自己的SDD模板,不要照抄网上的,因为每个团队的痛点和上下文不同。模板里要有争议性字段的处理约定,比如“时间统一存时间戳、展示统一转格式化”“金额统一用分为单位”;这些约定会是SDD落地的重要基础。
第二步,选一个中等规模的试点项目完整跑一遍流程。所谓完整,是指从写文档、评审文档、开发、测试、联调、上线到复盘,所有人都严格按规范来,目标是让团队形成对SDD价值的真实体感。这一步最重要的是做复盘,把过程中发现的问题记录下来,修订到模板和流程里。
第三步,把SDD和任务管理系统打通。把文档链接挂到每一个对应的任务卡片上,让文档成为任务的“必填附件”。同时在代码仓库里建一个独立的docs目录,按模块分文件夹存档,避免文档散落在Wiki、网盘、本地电脑里,找都找不到。走完这三步,SDD基本就内化成了团队的习惯动作,不是靠个别人盯着的“额外作业”了。
对我来说,SDD最大的意义不是多写几份文档,而是改变了团队思考和沟通的方式。它逼着我们在动手之前先想清楚,狠刹了“感觉到位就开干”的风气。如果你已经受够了联调时的鸡同鸭讲,受够了AI生成的代码总在不该出问题的地方翻车,真的可以试着从一个小模块开始,把规范驱动开发捡起来。这套方法不挑语言、不挑框架、不挑团队大小,唯一需要的只是有人愿意先迈出写第一份规范的那一步。