首页>后端>正文

从需求到架构:AI辅助编写design.md实战

Feng 34 阅读 后端

软件工程说到底是用代码表达需求。设计没想清楚,代码写得再快也是返工。proposal.md 把“做什么”讲明白之后,下一步必须回答“怎么做”——这就是 design.md 的职责。

需求文档能告诉 AI 功能应该表现成什么样,却回答不了技术层面的选择题。以登录模块为例,proposal.md 可以写得很具体:账号密码登录、持续操作会话不过期、30 分钟无操作注销、支持记住密码。但以下问题它管不了:

从需求文档到架构设计:AI辅助编写design.md的工程方

认证逻辑是否独立成模块?前后端交互的数据结构长什么样?会话续约用 Redisson 看门狗还是别的机制?撞库攻击怎么防?续约失败要不要走数据库降级?

这些不澄清,AI 就会按训练数据里的常见套路组织代码。极端情况下整个登录模块被塞进一个类,认证代码散落到各个接口方法里,原有架构被冲得七零八落。功能也许能跑,但后续排查和迭代的成本会成倍上升。

从需求文档到架构设计:AI辅助编写design.md的工程方

两份文档的分工边界

proposal.md 划定需求边界,design.md 回答代码如何组织、模块如何协作。两者都面向人和 AI,但信息密度和思维跨度完全不同。从 proposal 到 design,是把业务语言翻译成技术方案的关键一跃。

康威定律说组织沟通结构会映射到系统结构上。AI 不知道代码之外的团队分工和协作约束,这些必须由人主动定义并写进上下文。《人月神话》强调概念完整性——单个模块粗糙可以迭代,整体交互杂乱则系统没有改进的基础。

从需求文档到架构设计:AI辅助编写design.md的工程方

举个具体例子。统一响应格式就是概念一致性的体现:

@Data
public class Result<T> {
 private boolean success;
 private T data;
 private int errorCode;
 private String errorMsg;
}

如果 A 模块返回 Result,B 模块只返回布尔值,调用方就得分别适配。design.md 的作用就是在 AI 动手之前,让它清楚每个模块在系统中的位置和职责。

从需求文档到架构设计:AI辅助编写design.md的工程方

架构从业务结构中长出来

回到登录模块,需求可以拆成三块:校验用户输入、对工作台操作鉴权、定期续约会话并在退出时清理令牌。三部分职责不同、处理逻辑不同,沿着数据流梳理自然得到分层结构:

表现层/接入层负责输入校验;业务逻辑层/安全层负责鉴权;会话管理层/基础设施层负责续约和令牌清理。

从需求文档到架构设计:AI辅助编写design.md的工程方

从需求文档到架构设计:AI辅助编写design.md的工程方

这个分层不是先挑架构再往里塞业务,而是从问题本身的结构中抽象出来的。要不要拆微服务?proposal.md 已经说明系统仅供个人使用,这个体量下微服务只会增加复杂度。架构选择以刚好够用为准。

从需求文档到架构设计:AI辅助编写design.md的工程方

模块定义环节经常被低估。架构图画了,职责边界却模糊。登录功能服务于个人工作台,认证和鉴权应独立到专门模块,菜单鉴权通过独立切面处理,避免与业务逻辑耦合。这对应单一职责原则:每个模块只有一个引发变更的理由。

划定模块时尤其要说清“不负责什么”。缺少边界,AI 可能直接改现有方法来适配新业务,而不是按开闭原则封装扩展。模块划分的重点不仅是“拆”,更是“界”。

从需求文档到架构设计:AI辅助编写design.md的工程方

ADR:把决策和理由一起留下来

技术选型总要面对选择题:通信选 HTTP 还是 RPC?消息队列用 Redis 还是 RocketMQ?很多团队的选择偏保守——“一直用 Redis 就继续用”。等到业务痛点逼着考虑 RocketMQ 时,没人记得当初为什么选 Redis,也不敢轻易动。

这就是决策蒸发:方案留下了,理由丢了。解决办法是把难以改变的关键决策写成架构决策记录(ADR)。决策越难改,支撑它的理由越值得记。

从需求文档到架构设计:AI辅助编写design.md的工程方

一份 ADR 保持简练,包含五个要素:标题一句话概括决策;背景说明面临什么问题;选项列出考虑过的方案及优缺点;决策写明最终选择;理由说明关键取舍。超过一页纸就该检查是否陷入了不必要的细节。

以登录方案为例,ADR 会记录:当前只有单管理员、“已登录/未登录”两种状态,完整 Spring Security 的权限模型带不来相称收益;JJWT 负责标准令牌签发与验签,项目只维护登录流程和接口拦截;MVC 拦截器沿用现有 Controller、Service 和前端请求链路,依赖少、代码范围小。

从需求文档到架构设计:AI辅助编写design.md的工程方

这里有个版本层面的坑需要提前说明。JJWT 0.11.x 与 0.12.x 的 API 差异较大:0.12.x 中 Jwts.parser() 改为 Jwts.parser().verifyWith(key).build(),signWith(key, SignatureAlgorithm.HS256) 的旧签名已废弃,需改用 signWith(key, Jwts.SIG.HS256)。若项目基于 Spring Boot 3.x(要求 Java 17+),依赖坐标也应从 jjwt 拆分为 jjwt-api、jjwt-impl、jjwt-jackson 三个。版本不匹配时典型报错是 NoSuchMethodError 或 ClassNotFoundException,根因就是编译期与运行期 JJWT 版本不一致。在 design.md 中固定版本号并注明 API 风格,能避免 AI 生成过时代码。

ADR 还要写清失效条件:出现多用户或角色权限、需要立即撤销会话、接入 SSO 或 MFA、发生令牌安全事件时,应重新评估并迁移到 Spring Security 或服务端 Session 方案。JWT 无状态认证的边界就在这里——它适合单用户、低撤销需求的场景,不适合需要即时踢人下线或细粒度权限控制的系统。

从需求文档到架构设计:AI辅助编写design.md的工程方

接口契约先于实现

架构划了边界,ADR 记了选型,接下来要约定模块之间怎么通信。接口契约说明我提供什么数据、你返回什么结果,内部实现不向调用方展开。AI 生成代码时,明确的输入输出约定能显著减少实现偏差。

先定义数据载体。LoginRequest 包含 username 和 password,前者非空,后者只允许进入认证服务。UserAccount 包含 userId、username、passwordHash(BCrypt 摘要,仅服务端校验)、nickname、avatar、createdAt。TokenClaims 包含 subject、username、issuedAt、expiresAt。LoginResult 仅在校验成功后返回 token、userId、username 及可选的展示字段。

从需求文档到架构设计:AI辅助编写design.md的工程方

有些场景还要说明数据之间的对应关系。同一用户既能用账号名也能用手机号登录,就用一份配置让 AI 看清归属:

# 同一组配置表示账号名、手机号和昵称属于同一个用户。
# 用户输入账号名或手机号登录后,系统都能找到 user_id 为 1 的账户。
# 以下均为示例数据。
accounts:
 - user_id: 1
 username: "demo-admin"
 phone: "13800000000"
 nickname: "博主"

数据模型定了,再定义模块对外暴露的函数、入参和返回值。design.md 只给对接者需要的信息,隐藏无关内部细节——信息隐藏让调用方按契约对接,降低噪音减少阅读负担,同时避免暴露过多实现细节。

从需求文档到架构设计:AI辅助编写design.md的工程方

非功能性约束不能等上线才想

看建筑,外行先关注外观布局,建筑师还要考虑消防通道、抗震等级、防水隔热。软件同理。proposal.md 的验收标准通常描述用户能感知的功能,但系统要上生产,非功能性需求(NFR)必须提前设计。

完整的业务流程在提测时容易覆盖,非功能问题却常常拖到生产才暴露。需要关注的方面包括:性能(响应时间、并发量、内存占用)、安全(认证、数据访问、密码加密、注入防护)、容错(超时重试、故障隔离、幂等)、可维护性(日志、监控、告警、健康检查、回滚)。

从需求文档到架构设计:AI辅助编写design.md的工程方

本次设计重点考虑优雅降级。会话续约依赖的 Redis 宕机时,先在有限时间内重试,再通过数据库降级维持工作台操作。proposal.md 写“持续操作时会话不会因超时被强制退出”,design.md 则要写清技术方案:Redisson 每隔 1 分钟重试续约,重试超过 3 次后降级为数据库定期续约。

这个降级策略需要可验证。测试环境可以用 docker stop redis 模拟故障,观察应用日志中是否出现重试计数递增,以及第 4 次失败后是否切换到数据库续约路径。判定标准有三条:Redis 停止后 3 分钟内会话仍有效;日志中降级标记出现且仅出现一次;Redis 恢复后自动切回 Redisson 续约。用 redis-cli monitor 可以确认恢复后的续约命令是否重新打到 Redis 上。

从需求文档到架构设计:AI辅助编写design.md的工程方

用 Codex 生成 design.md 的工作流

design.md 的内容明确后,不必全部手写。把 proposal.md 作为输入约束,通过 Claude Code 或 Codex 辅助设计。先在 CLAUDE.md 或 skill 中声明项目采用 SDD 流程,然后分四步走:

让 AI 阅读 proposal.md,提出模块划分、职责定义、数据模型、接口契约和非功能性约束的设计方案;人类工程师审核、决策并订正;AI 生成 design.md 并提交 Git,随功能迭代维护;基于 design.md 拆解任务。

从需求文档到架构设计:AI辅助编写design.md的工程方

从需求文档到架构设计:AI辅助编写design.md的工程方

有三条注意事项。第一,关键决策仍由人主导。AI 可以分析方案、指出盲点,但选型和取舍需要架构师判断。design.md 可能在开发中被修改甚至推翻,但设计过程迫使你认真思考系统结构,这些思考本身就有价值。

从需求文档到架构设计:AI辅助编写design.md的工程方

第二,确保 design.md 与 proposal.md 一致。proposal 规定不采用验证码登录,design 就不应引入 EasyCaptcha;proposal 规定登录尝试上限 3 次,design 必须用相同上限;proposal 规定超过 3 次后比对配置号码段,design 就不能改成短信验证码。

第三,设计变了文档要跟着变。拆解任务时发现遗漏或不合理,回头修改 design.md 并通过 Git 记录变更。

从需求文档到架构设计:AI辅助编写design.md的工程方

最终生成的 design.md 按以下顺序展开:标题说明对应的需求;ASCII 图展示架构和数据流向;按 Java 分包说明模块职责及新增修改范围;定义数据类型和接口契约;引用具体 ADR 文件;对照 proposal.md 说明非功能性约束的落实策略。

从需求文档到架构设计:AI辅助编写design.md的工程方

从需求文档到架构设计:AI辅助编写design.md的工程方

设计先于编码的四个必答题

架构设计不只是画图。收到需求后、开始编码前,必须先回答四个问题:模块如何划分?采用什么技术、为什么?模块之间如何通信?非功能性需求如何落实?

这四个答案对应 design.md 的四个主体部分:系统架构(按单一职责和关注点分离划分模块、定义职责、说明数据流向)、技术选型 ADR(记录选项、理由和取舍,作为 AI 实现的技术约束)、数据模型与接口契约(约定数据和交互规则)、非功能性约束(错误处理、安全性、可维护性的具体设计)。

AI 时代更需要主动做架构决策。否则 AI 会替你做选择;如果这些选择没有记录,后续就难以审查和回溯,风险会随系统演进而累积。所有 AI 辅助编程的问题,都可以通过增加一层规范来解决。

ID:7207

Feng
这位作者很神秘,还没有填写简介。