首页 / 资讯中心 / 文章详情

规格驱动开发:用Markdown让AI写代码不再“抽卡”

规格驱动开发:用Markdown让AI写代码不再“抽卡” ★ FEATURED ARTICLE
1. 当AI写代码开始“抽卡”问题到底出在哪用AI写代码这件事很多人应该都有类似的体验同一个需求早上问它给你生成一版能跑的下午再问一遍它给你换了个架构、换了个命名风格甚至把之前明确说过的约束全忘了。你反复强调“不要用某个库”“错误处理要统一走中间件”“接口返回结构必须是这个格式”它当时答应得好好的下一轮对话又打回原形。这种状态我一般叫它“抽卡式编码”——能不能生成符合预期的代码全看运气。问题的根子不在于模型不够聪明而在于我们和AI之间的协作方式本身是无规格的。我们习惯用自然语言一段一段地描述需求模型每次都在一个模糊的上下文里做概率推断它没有一份稳定、可复用、可校验的“契约”去对齐。人跟人协作的时候靠的是需求文档、接口定义、编码规范这些东西但到了人跟AI协作绝大多数人把这些全丢了直接开聊。结果就是AI每次都在重新猜你想要什么。OpenSpec 这个项目要解决的就是这件事。它提出的核心概念叫规格驱动开发Spec-Driven Development简称 SDD思路很直接在让AI写代码之前先把“规格”用结构化的方式写清楚让规格成为人和AI共同遵守的唯一事实来源。而承载规格的载体选的是Markdown——一个几乎所有开发者都熟悉、几乎所有工具链都支持的格式。你不需要学一套新的DSL不需要装什么重型框架用Markdown把需求、约束、接口、验收标准写明白AI就能稳定地产出符合预期的代码。这篇文章适合几类人看一是已经在日常用AI辅助编码、但被“不稳定”折磨过的开发者二是团队里想推行AI编码规范、但不知道怎么落地的技术负责人三是对SDD这个概念好奇、想搞清楚它和传统需求文档区别的人。我会把OpenSpec背后的逻辑、规格该怎么写、实际落地时会踩哪些坑尽量讲透。2. 规格驱动开发到底“驱动”了什么2.1 从“对话式提示”到“契约式规格”的转变大多数人用AI编码的默认姿势是对话式的我描述一段它生成一段不满意我再补充说明它再改。这种方式在一次性、小颗粒的任务上没问题但一旦任务变复杂、跨越多个文件、需要多轮迭代上下文就开始漂移。模型记不住你三轮之前说过的约束你也记不住自己到底提过哪些要求。最后代码能跑但风格混乱、边界条件缺失、后续没法维护。SDD 的做法是把“对话”升级成“契约”。在动手写代码之前先产出一份规格文档里面明确写清楚这个模块要做什么、输入输出是什么、有哪些硬性约束、验收标准是什么、不允许出现什么。这份规格不是给人看的摆设而是同时给人和AI看的执行依据。AI基于规格生成代码人基于规格做review双方对齐的是同一份东西而不是各自脑子里的模糊印象。这个转变的关键在于规格是可持久化的。对话是流式的说完就散了规格是文件躺在仓库里随时可以查、可以改、可以版本化。下次再让AI改这个模块你把规格丢给它它就有了完整的上下文不需要你从头再讲一遍。2.2 为什么偏偏选Markdown作为规格载体选Markdown不是随便定的。规格载体要满足几个条件第一人和AI都能轻松读写第二能表达结构标题、列表、表格、代码块第三工具链支持好能diff、能渲染、能转换第四学习成本低团队里没人需要专门培训。Markdown 几乎完美命中这四点。它的语法足够表达规格需要的结构——用标题分层、用列表列约束、用表格对比接口、用代码块写示例。同时它又是纯文本git diff 看得清清楚楚code review 的时候改动一目了然。更关键的是AI对Markdown的理解非常成熟你给它一份Markdown规格它解析起来比解析一段散文式的自然语言准确得多。这里有个实操细节值得说Markdown的换行和表格在规格里特别重要。很多人写Markdown习惯一行写很长渲染出来没问题但在纯文本状态下读起来很痛苦AI解析时也容易把本该分开的两条约束连成一条。我的习惯是每条约束独立成行用列表项隔开宁可多换行也不要挤在一起。表格则用来表达“输入字段-类型-是否必填-说明”这类结构化信息比用文字描述清晰一个量级。2.3 SDD和传统需求文档的本质区别有人会问这不就是写需求文档吗有什么新鲜的区别在于受众和执行方式。传统需求文档是写给人看的写完就归档开发的时候大家凭记忆和理解去实现文档和代码之间没有强绑定。SDD的规格是写给AI执行的它必须精确到AI能直接据此生成代码的程度模糊的形容词“尽量快”“用户体验好”在规格里是无效的必须换成可判定的标准“响应时间P99小于200ms”“错误码统一为四位数字”。另一个区别是粒度。传统需求文档往往停留在功能层面SDD的规格要下沉到接口、数据结构、边界条件、错误处理。因为AI不会像人一样“领会精神”你写多细它就做多细你没写的它就自己发挥——而它发挥的方向往往不是你想要的。3. 一份能真正驱动AI的规格长什么样3.1 规格的最小结构目标、约束、接口、验收一份能用的规格我总结下来至少要有四块内容。目标说清楚这个模块解决什么问题一到两句话不要展开。约束列出所有硬性要求包括技术栈、命名规范、禁止使用的方案、性能底线。接口定义输入输出字段名、类型、必填性、取值范围都要写死。验收标准是可执行的检查项最好能对应到测试用例。这四块缺一不可。我见过有人只写目标就丢给AI结果生成的东西方向对但细节全错也见过只写约束不写验收代码看起来符合要求但根本没法验证。规格的价值就在于它把“我以为我说清楚了”变成“白纸黑字写清楚了”。3.2 用Markdown表格锁死接口定义接口定义是规格里最容易出问题的地方因为字段一多就容易漏。用Markdown表格来锁是最稳的。比如定义一个用户查询接口表格里列清楚字段名、类型、是否必填、说明、示例值。AI拿到这张表生成出来的结构体或类基本不会跑偏。字段名类型必填说明示例userIdstring是用户唯一标识长度32u_123456pageSizeint否每页条数默认20最大10020cursorstring否分页游标为空则从头开始c_abc这张表的好处是它同时约束了字段名、类型、默认值和边界。AI生成代码时会照着这个来review的时候你也照着这个查双方标准统一。比用一段文字描述“接收用户ID和分页参数”精确太多了。3.3 约束要写成“可判定”的句子约束最忌讳写成口号。“代码要优雅”“性能要好”“注意安全”这种话对AI来说等于没说。有效的约束必须是可判定的也就是能明确回答“做到了没有”。举个例子与其写“错误处理要统一”不如写“所有对外接口的错误返回必须使用统一的ErrorResponse结构包含code、message、traceId三个字段code为四位数字字符串”。与其写“注意并发安全”不如写“对共享map的读写必须加锁或使用sync.Map”。前者AI只能猜后者AI能直接执行。我一般会把约束分成两类技术约束用什么语言、什么库、什么模式和禁止约束不许用什么、不许出现什么。禁止约束特别有用因为AI有时候会自作主张引入一些你不想要的依赖或写法明确禁掉能省很多事。3.4 验收标准要能对应到测试验收标准写得好不好一个简单的判断方法能不能直接翻译成测试用例。如果一条验收标准你没法写出对应的断言那它就不够具体。比如“分页接口在cursor为空时返回第一页数据”这条可以直接写成测试传入空cursor断言返回的数据条数等于pageSize且第一条数据的ID等于全量数据的第一条。“错误码为四位数字”这条可以写成测试遍历所有错误返回断言code字段匹配正则^\d{4}$。规格里的验收标准越接近测试AI生成的代码质量越有保障因为它在生成时就知道自己要满足什么。4. 把规格接进日常编码流程的实操路径4.1 从存量项目切入还是新项目起步推行SDD我的建议是从新模块或新项目起步不要一上来就改造存量代码。存量项目的历史包袱重规格很难一次性补全强行推容易让人产生“这玩意儿没用”的抵触。新模块没有历史约束你可以完整地走一遍“写规格-生成代码-review-迭代”的流程跑通之后再考虑往存量上迁移。具体做法是挑一个边界清晰、依赖不多的新功能比如一个独立的查询接口或一个工具函数库。先花半小时把规格写出来然后让AI基于规格生成代码你对照规格做review。跑完一轮你会发现review的效率比之前高很多因为你有明确的对照物不用靠感觉判断代码对不对。4.2 规格的版本管理和迭代节奏规格不是写完就锁死的它会随着需求变化而迭代。关键是要把规格当成代码一样管理进git、走review、有版本记录。每次需求变更先改规格再让AI基于新规格改代码而不是直接口头告诉AI“把那个字段改一下”。迭代节奏上我习惯小步快跑。规格不要一次写太大一个模块的规格控制在能一次review完的体量。写完一版就让AI生成生成完发现问题就回头改规格改完再生成。这个循环跑几轮规格会越来越精确AI的输出也会越来越稳。最怕的是一次性写一份巨长的规格AI生成一大堆代码你review到崩溃最后也不知道问题出在规格还是代码。4.3 让AI参与规格的补全而不是替代一个很实用的技巧规格的初稿可以让人写但补全和查漏可以交给AI。你把初稿丢给AI让它指出哪些地方模糊、哪些约束不可判定、哪些接口字段缺说明。AI在这件事上比人细心它能发现你习以为常但实际没写清楚的地方。但要注意AI补全的内容必须经过你确认才能进规格。它有时候会自作主张加一些你没要求的约束或者把某个字段的类型改掉。规格的最终解释权必须在你手里AI只是帮你查漏的工具不是规格的作者。5. 那些没人告诉你但一定会踩的坑5.1 规格写太细反而限制AI发挥这是个反直觉的坑。规格的目的是对齐不是把AI变成只会照抄的机器。如果你把每一行代码该怎么写都规定死AI就失去了它最擅长的部分——在明确约束下给出合理的实现方案。我见过有人把规格写成伪代码结果AI生成的东西跟手写没区别完全没发挥出AI的效率优势。正确的度是约束写死实现放开。你规定接口长什么样、错误怎么处理、性能底线在哪但具体用什么算法、怎么组织内部函数让AI自己决定。这样既保证了输出符合预期又保留了AI的灵活性。5.2 Markdown格式细节导致的解析偏差Markdown看起来简单但格式细节对AI解析的影响比想象中大。几个高频问题换行——有些Markdown渲染器把单换行当软换行AI解析时可能把两行合并表格对齐——表格的竖线没对齐不影响渲染但AI读的时候可能错位代码块语言标注——不标语言AI可能猜错上下文。我的做法是规格文件里所有列表项之间空一行表格严格对齐代码块一律标注语言。这些细节看起来琐碎但能显著降低AI理解偏差的概率。另外如果你的规格要转成其他格式比如转Word给非技术同学看提前确认转换工具对Markdown语法的支持程度表格和嵌套列表是最容易出问题的。5.3 团队协作时规格的“最后一公里”SDD在个人用的时候很顺推到团队里就会遇到“最后一公里”问题有人不写规格直接让AI生成有人写了规格但跟代码对不上有人觉得写规格太慢不如直接聊。这些问题的根源不是工具是习惯。我的经验是先用工具强制再靠习惯固化。比如在CI里加一个检查改了代码但没改对应规格的PR打上待确认标签。或者在code review模板里加一项“规格是否同步更新”。强制一段时间后大家会慢慢发现写规格其实省了后面扯皮的时间习惯就养成了。最忌讳的是一上来就靠自觉大概率推不动。6. 规格驱动之后人和AI的分工边界在哪跑通SDD之后你会发现人和AI的分工其实变得更清晰了。人负责定义问题——写规格、定约束、划边界、做验收AI负责解决问题——在规格框架内生成实现、补全细节、处理边界。人不再需要逐行写代码但需要把“要什么”想得比以前更清楚。这个分工的转变对开发者其实是个挑战。以前你可以边写边想现在你得先想清楚再让AI写。很多人不适应觉得写规格比写代码还累。但换个角度想写规格的过程本身就是把需求想清楚的过程这部分工作以前也存在只是被藏在了反复改代码的循环里。把它提前、显式化长期看是省时间的。OpenSpec 这类工具的价值不在于它用了多高深的技术而在于它把“规格先行”这个朴素的工程原则用Markdown这种低门槛的载体接进了AI编码的工作流。它没有发明新概念只是把老道理用对了地方。至于要不要用、怎么用还是得结合自己团队的实际节奏来。我的建议是先用一个小模块试水跑通一轮再决定要不要铺开。试错的成本很低但一旦跑顺AI编码的稳定性会有肉眼可见的提升。
阅读完成 · 觉得有帮助?
咨询建站