从感觉编程到规范驱动开发:AI时代软件工程的质量保障实践
1. 从“感觉编程”到“规范驱动开发”的思维跃迁最近在团队里做Code Review经常看到一些让人哭笑不得的提交。一个简单的用户登录功能AI生成出来的代码洋洋洒洒几百行里面混着三四种不同的错误处理风格甚至还“贴心”地给我预埋了几个从没讨论过的“增强功能”——比如自动把用户登录地点的天气信息存进数据库。问起作者为什么这么写得到的回答往往是“我跟Copilot说‘做一个登录’它生成出来看着差不多跑起来也没报错我就提交了。” 我把这种工作模式称为“感觉编程”它正在成为新手工程师快速制造Bug的流水线。“感觉编程”的流程很有代表性你脑子里有个模糊的需求用自然语言描述给AI比如“给API加个分页”。AI吐出一段代码你运行一下没红字错误功能好像也管用就合入主线。这个过程快吗确实快半小时搞定一个需求。但可持续吗绝对是一场灾难。当项目规模稍微大一点这种模式会立刻暴露出三个致命问题首先生成的是无法审查的代码块动辄500行起步逻辑东一块西一块Reviewer根本无从下手其次AI会自作主张添加你从未要求的功能美其名曰“最佳实践”实则是技术债的种子最可怕的是当你试图让AI修正其中一个问题时它可能会基于错误的理解重写你半个代码库的架构。那么资深工程师是怎么利用AI的答案不是更复杂的提示词而是一种完全不同的工作范式规范驱动开发。我们不直接向AI提问而是先为AI编写一份它能够严格执行的“施工图纸”。这套方法的核心是将产品需求、技术设计和AI指令三层文档分离并串联起来把原本模糊的“感觉”转化为精确的、可验证的“规范”。接下来我就结合一个具体的JWT认证案例拆解这背后的完整工作流和实操细节。2. 规范驱动开发的核心三份不可或缺的文档规范驱动开发不是简单地“把需求写详细点”它是一套严谨的、将人类意图无损传递给AI的文档体系。这套体系由三份环环相扣的文档构成每一份都有其不可替代的使命。跳过任何一步都会让“感觉”重新渗入流程。2.1 产品需求文档定义“做什么”与“为什么”PRD是你的第一份也是最重要的锚点文档。它的目标读者首先是产品经理、设计师和工程师自己用于对齐业务目标。在AI开发语境下它更是AI理解业务上下文的基础。一份合格的PRD必须回答清楚两个问题我们到底要构建什么以及为什么它值得被构建以“为我们的内部管理后台添加用户登录”这个需求为例。一份“感觉式”的PRD可能只有一句话。而一份规范的PRD则需要展开核心用户与场景管理员需要登录后台管理系统查看运营数据。当前所有人共享一个超级密码存在安全风险且无法审计操作记录。功能需求清单用户使用邮箱和密码登录。登录成功后后续请求需携带凭证以访问受保护接口。支持“记住我”功能在浏览器关闭后一段时间内保持登录状态。提供安全的退出登录机制。非功能需求安全性密码需加盐哈希存储传输必须使用HTTPS。体验登录失败需给出明确但不过于详细的提示如“邮箱或密码错误”。可审计性关键操作需记录操作者ID。PRD里不涉及任何技术选型比如用JWT还是Session它只关乎业务逻辑和用户体验。这份文档是后续所有工作的源头也是评审AI产出是否偏离目标的根本依据。2.2 技术设计文档规划“如何做”的蓝图有了PRD我们知道了目的地。技术设计文档则是描绘通往目的地具体路径的工程蓝图。它的目标读者是研发团队核心是做出技术决策并阐述其合理性。这是将产品语言翻译为技术语言的关键一步。继续上面的登录案例TDD需要明确以下内容架构决策认证方案选择采用无状态的JWT认证而非有状态的Session。理由是后台管理系统为前后端分离架构且无需支持复杂的实时会话管理JWT更轻量且易于水平扩展。Token存储位置Access Token置于HTTP Authorization HeaderRefresh Token通过HttpOnly Cookie下发以平衡安全性与便捷性。数据模型设计User表需新增password_hash、last_login_at等字段。是否需要单独的RefreshToken表用于黑名单机制决定暂不需要初期通过较短的Access Token有效期来降低风险。API合约定义POST /api/auth/login请求体、成功响应含access_token, expires_in、错误响应401, 429。POST /api/auth/refresh如何利用Cookie中的refresh token获取新的access token。POST /api/auth/logout如何安全地使token失效。与其他系统的交互明确本次开发不会修改现有的用户权限系统新认证层需与现有权限中间件兼容。TDD的价值在于它迫使你在写第一行代码前思考清楚所有技术细节和边界情况。这份文档本身就是一次深度的设计评审。2.3 AI规范文档给AI的精确指令集这是直接面向AI的“可执行文件”。它基于前两份文档生成但不是简单的复制粘贴而是将需求和设计转化为AI能理解且能原子化执行的指令序列。AI规范文档的核心特点是精确、无歧义、可验证。一个糟糕的提示是“为我的Express.js API添加JWT认证。” 而一个规范的AI Spec应该是这样的## 任务实现基于JWT的用户认证模块 **上下文**基于已批准的PRD#123和TDD#456为Node.js/Express后台管理系统实现登录、刷新、登出功能。 ## 严格遵循的架构与技术栈 - 后端框架Express.js 4.x - 数据库ORMPrisma - JWT库jsonwebtoken - 密码哈希bcrypt - 环境变量管理dotenv ## 需要实现的具体端点与逻辑 ### 1. 登录端点 (POST /api/auth/login) **请求体**: json { email: string, password: string, rememberMe: boolean }处理逻辑:验证请求体格式。根据email从User表查找用户使用Prisma。使用bcrypt.compare验证密码哈希。若失败返回HTTP 401统一消息“Invalid email or password”。若成功 a. 生成JWT Access Tokenpayload包含userId和role密钥来自process.env.JWT_SECRET有效期rememberMe为真时7天为假时2小时。 b. 生成Refresh Token随机字符串存入数据库User.refreshToken字段单设备登录新token覆盖旧的有效期30天。 c. 在响应中返回{ “access_token”: “xxx”, “expires_in”: 7200 }。 d. 将Refresh Token通过HttpOnly、Secure、SameSiteStrict的Cookie设置到客户端路径为/api/auth/refresh。2. 刷新端点 (POST /api/auth/refresh)逻辑:从Cookie中读取refreshToken。在数据库中查找匹配此token且未过期的用户记录。验证通过后生成新的Access Token和Refresh Token规则同登录更新数据库中的Refresh Token并设置新的Cookie。返回新的Access Token。3. 登出端点 (POST /api/auth/logout)逻辑:从Cookie中读取refreshToken。清除数据库中相应用户的refreshToken字段设为NULL。清除客户端的Refresh Token Cookie设置过期时间为过去。返回204 No Content。必须创建的中间件authenticateJWT从Authorization: Bearer token头中提取token用jsonwebtoken.verify验证将解码后的用户信息至少含userId注入req.user。验证失败统一返回401。严格的代码风格与禁区错误处理使用异步中间件所有错误通过next(error)传递由顶层的错误处理中间件统一格式化返回。数据库操作必须使用Prisma Client并包含try...catch。安全绝对禁止在任何日志、响应中明文记录密码或完整的JWT。禁止修改不得修改现有的/api/admin/*路由权限检查逻辑只需确保新的authenticateJWT中间件能与其兼容。看到区别了吗后者几乎是一个可以直接粘贴执行的伪代码清单。它限定了技术栈、明确了每个步骤的输入输出、规定了错误处理方式甚至划定了AI绝对不能触碰的代码区域。这才是能让AI生成出可审查、可预测代码的关键。 ## 3. 五步工作流从需求到可合并代码的实操路径 有了三份文档的理论基础我们来梳理将其落地的标准化工作流。这个过程就像一条流水线每一步都为下一步奠定基础确保产出物的质量。 ### 3.1 第一步撰写与评审产品需求文档 不要独自闭门造车。即使是个人项目也试着从用户视角写下PRD。核心是举办一次简短的“三方对齐会”产品、设计、研发哪怕只有你一个人也扮演这三个角色过一遍。评审的重点是 * **需求是否完整**有没有遗漏的边缘情况比如“忘记密码”功能是否在本期范围 * **目标是否清晰**每个人对“做好登录”的理解是否一致 * **成功标准是否可衡量**是“功能上线”就行还是“登录成功率大于99.9%” 实操心得PRD评审通过后将其固定在项目Wiki或Issue中并锁定版本。后续任何需求变更都必须先更新PRD并重新评审从源头上控制范围蔓延。 ### 3.2 第二步细化技术设计文档 基于锁定的PRD开始进行技术决策。这一步的关键是**做选择题并给出理由**。例如 * 选择JWT而不是Session是因为我们的部署架构是无状态的。 * Refresh Token存数据库而不存Redis是因为初期复杂度低且数据一致性更容易保证。 * 密码加密选用bcrypt而不是argon2是因为当前Node.js版本对bcrypt支持更稳定。 将所有这些决策、接口定义、数据模型变更用清晰的图表和列表写在TDD中。然后发起一次技术评审。评审的重点是 * **方案是否可行**是否有难以实现的技术难点 * **方案是否最优**有没有更简单、更稳定的替代方案 * **方案是否安全**是否存在已知的安全漏洞如JWT密钥强度不足 * **对现有系统影响**本次修改是否会破坏现有功能 注意事项TDD评审通过后它就成了开发的“宪法”。AI编码、人工编码都必须遵循它。这能有效防止技术决策在实现阶段被随意篡改。 ### 3.3 第三步生成精准的AI规范文档 这是将人类智慧“编码”成AI指令的一步。我的经验是以TDD为骨架将每一个技术决策和API合约拆解成AI能执行的原子任务。一个高效的技巧是使用“**给定-当-那么**”的格式来描述逻辑 * **给定**一个有效的邮箱和密码 * **当**用户调用登录接口 * **那么**系统应验证密码生成双Token并正确设置Cookie和响应。 将TDD中所有类似的逻辑点都如此拆解就构成了AI Spec的主体。然后务必在文档末尾加上“**禁区与风格**”章节明确告诉AI哪些文件不能动、必须使用哪种错误处理模式、代码注释规范等。这份文档本身也应该作为代码仓库的一部分进行版本管理。 ### 3.4 第四步运行AI智能体并生成差异代码 现在才是打开你的Copilot Chat、Cursor AI或Claude Code的时候。不要直接给它看代码文件而是将**AI规范文档**作为最主要的上下文喂给它。你可以这样说“请根据以下Spec在现有的Express项目简要描述项目结构中实现所述功能。请严格遵循Spec中的所有要求包括技术栈、逻辑步骤和代码禁区。” 接下来AI会开始生成代码。规范驱动开发在这里的另一个优势显现了**你不需要它一次性生成全部功能**。你可以要求它按照Spec中的模块顺序逐个端点或逐个中间件地生成。例如先让它实现“登录端点”你审查并确认无误后再让它基于已实现的代码上下文继续完成“刷新端点”。 这样做的好处是每次生成的代码块都是小而精的便于聚焦审查也降低了AI因上下文过长而“失忆”或胡编乱造的风险。 ### 3.5 第五步审查代码差异而非海量代码 这是与传统“感觉编程”评审天差地别的一步。当AI基于精确的Spec完成编码后你作为评审者不需要再像大海捞针一样去审查几百行陌生代码的逻辑是否正确。你的审查重点转变为两个 1. **一致性审查**将AI生成的代码Diff与**AI规范文档**逐条比对。它是否严格遵循了每一条指令Token有效期对吗错误响应格式对吗Cookie属性设置了吗这个过程就像质检员对照图纸检查零件尺寸高效且准确。 2. **上下文集成审查**检查生成的代码与现有代码库的集成点。引入的中间件是否被正确挂载到路由上新的数据库字段迁移脚本是否生成是否有意外的全局变量污染或命名冲突 如果审查通过你就可以自信地合并代码。因为你知道所有的业务逻辑和技术决策早在PRD和TDD阶段就已经过深思熟虑和团队评审AI只是一个没有偏差的执行者。 ## 4. 深入案例JWT认证模块的规范驱动实现与避坑指南 让我们将上述理论完全套入到“为Express.js后台添加JWT认证”这个具体案例中看看每一步的实操细节和可能遇到的坑。 ### 4.1 需求与设计的转化从模糊到精确的挑战 最初的模糊需求是“加个登录”。通过PRD我们明确了要支持“记住我”。在TDD阶段这个需求转化为了一个具体的技术决策**Access Token的有效期根据rememberMe字段动态变化**。这是一个关键的细节如果不在设计阶段明确AI很可能会生成一个固定有效期的Token导致用户体验不符。 在编写AI Spec时这个决策需要被进一步拆解为可执行的逻辑 登录成功时判断请求体中的rememberMe布尔值。若为true则access_token有效期expires_in设为604800秒7天若为false或未提供则设为7200秒2小时。此逻辑必须同时应用于Token的JWT payload过期时间和响应的expires_in字段。 这就是“规范”的力量它把一个容易遗漏的产品细节变成了AI必须遵守的硬性规则。 ### 4.2 安全实现的魔鬼细节 安全无小事而AI对安全的理解是肤浅的。规范文档必须充当安全专家。 **密码存储**在Spec中必须明确写出“使用bcrypt.hash进行哈希盐轮数建议为12”。绝不能只写“哈希密码”否则AI可能选用不安全的MD5或SHA-1。 **JWT密钥**必须指令“从process.env.JWT_SECRET读取密钥该密钥必须是长度至少32位的随机字符串”。并补充说明“在项目根目录的.env.example文件中添加JWT_SECRET示例提醒开发者配置”。 **Refresh Token防篡改**指令中需明确“Refresh Token应是一个加密安全的随机字符串可使用crypto.randomBytes生成存储于数据库并用于查询验证”。这防止了客户端伪造Token。 **Cookie安全属性**这是重灾区。必须逐字写明“设置Cookie时属性必须包含httpOnly: true防止JS访问、secure: process.env.NODE_ENV ‘production‘生产环境HTTPS、sameSite: ‘strict‘防CSRF”。 注意我曾见过AI生成的代码漏掉了httpOnly导致Refresh Token暴露给前端JavaScript这是一个严重的高危漏洞。在Spec中显式强调每一条安全属性是杜绝此类问题的唯一方法。 ### 4.3 错误处理与日志记录的规范 混乱的错误处理是“感觉编程”代码的典型特征。在Spec中我们必须统一错误处理范式。 **指令示例** “所有异步操作如数据库查询、JWT验证必须使用try…catch包裹。在catch块中调用next(error)将错误传递给Express错误处理中间件。**禁止**在路由处理函数中直接使用res.status(500).json(...)。” “创建统一的错误响应格式。在项目的错误处理中间件中确保生产环境下返回{ “error”: “Internal Server Error” }开发环境下可包含message和stack。” 对于日志指令需明确边界“仅在登录成功/失败、Token刷新、登出时记录信息级别日志日志内容需脱敏如记录userId但不记录token。**严禁**在日志中记录密码、完整的JWT或任何个人身份信息。” ### 4.4 与现有系统的无缝集成 新模块不是孤岛。Spec必须定义清晰的集成合约。 **中间件挂载**“新建的authenticateJWT中间件需要在app.js或主路由文件中在所有需要认证的API路由之前全局挂载或按路由组挂载。” **用户对象扩展**“authenticateJWT中间件验证成功后应将解码出的payload至少包含userId赋值给req.user。确保现有权限检查中间件能兼容地从req.user读取用户信息。” **数据库迁移**“使用Prisma需生成并包含对应的迁移文件prisma migrate dev在User模型中添加refreshToken String?字段。” ## 5. 常见问题、排查技巧与效能提升实录 即便遵循了规范流程在实际操作中仍会遇到各种问题。以下是我在实践中总结的常见“坑点”及解决方案。 ### 5.1 AI生成的代码不遵循Spec怎么办 这是最常遇到的问题。根本原因通常有两个一是Spec本身存在二义性二是AI的上下文理解不足。 **排查与解决** 1. **检查Spec的精确性**立即回看AI违反的那条指令。是不是表述不够明确例如“验证密码”应改为“使用bcrypt.compare函数比对请求中的password和数据库中的password_hash字段”。 2. **提供更具体的上下文**如果项目结构复杂在给AI的指令开头简要描述相关文件的位置。例如“项目使用Express路由定义在src/routes/auth.js中用户模型在src/models/User.js。” 3. **分步执行即时反馈**不要让它一次性生成所有代码。让它先写登录接口的控制器函数。审查通过后再让它基于这个函数去写验证密码的工具函数。像教实习生一样一步步引导。 4. **使用“修正”指令**如果AI跑偏直接指出“你生成的代码在XX行没有按照Spec要求设置sameSite: ‘strict‘属性。请根据Spec第Y条修正。” AI会根据你的精确反馈进行调整。 ### 5.2 如何高效审查AI生成的代码 面对一份完整的Diff采用分层审查法 * **第一层架构符合性**。快速扫视生成的文件和代码结构是否与TDD中规划的模块划分一致如是否新建了/utils/auth.js存放JWT函数 * **第二层关键安全点**。直奔主题搜索“password”、“secret”、“token”、“cookie”等关键词逐项核对安全规范是否被严格执行。 * **第三层核心逻辑流**。针对每个端点沿着“请求输入 - 验证 - 业务处理 - 数据库操作 - 响应输出”的路径走读一遍看逻辑是否与Spec描述的完全一致。 * **第四层代码风格与集成**。检查错误处理是否统一、导入导出是否规范、是否无意中引入了未使用的依赖、是否修改了Spec中声明的“禁区”代码。 ### 5.3 规范驱动开发真的不会降低效率吗 初期看写三份文档比直接问AI要慢。但从整个项目生命周期看它的效率提升是巨大的。 **时间节省在哪儿** 1. **零返工**因需求不清或设计缺陷导致的代码推倒重来基本被杜绝。 2. **审查时间锐减**评审者从理解数百行杂乱代码变为对照Spec做“填空题”审查耗时可能减少80%。 3. **调试时间减少**由于边界情况已在Spec中定义运行时诡异Bug的数量显著下降。 4. **知识传承标准化**新成员加入时阅读PRD和TDD能快速理解系统全貌AI Spec则是最佳的实现参考手册。 一个实用的技巧是为你的项目创建PRD、TDD和AI Spec的**模板文件**。下次启动类似功能时你只需要在模板上修改而不是从头创作能极大提升文档阶段的效率。 ### 5.4 针对不同AI工具Copilot, Cursor, Claude的适配技巧 不同的AI工具有不同的特性和优势规范驱动开发是通用的但指令的微调可以让你事半功倍。 * **GitHub Copilot Chat**它深度集成在IDE中对项目上下文感知最强。在提问时可以多使用“在当前文件中”、“参考userService.js的模式”这样的短语让它更好地利用现有代码风格。 * **Cursor**其“Composer”模式非常适合分步实现复杂Spec。你可以将AI Spec的不同章节如“登录端点”、“刷新端点”分别粘贴到不同的Composer任务中让它依次完成保持上下文清晰。 * **Claude Code**长上下文能力出色且对指令的理解非常精准。你可以将完整的PRD、TDD和AI Spec一次性喂给它并说“请基于以上所有文档生成实现代码”。它往往能给出集成度更高、更连贯的产出。 无论使用哪种工具核心原则不变**永远让AI基于文档工作而不是基于你即时的、模糊的“感觉”**。当你养成先写规范再编码的习惯后你会发现你与AI的协作从未如此顺畅、可靠。你不再是一个Bug的修复者而是一个复杂系统的清晰构建者。