我很怕一种评审现场一位同事抱着一本80页的《详细设计说明书》进来目录翻到第三页就开始讲系统架构图底下开发听得毫无表情产品在打哈欠架构师皱着眉头翻数据库设计。等散会真正要动手写代码的人跑过来问我账单核销的状态机到底在哪个服务里更新我看着他手里的文档就知道这80页基本白写了。这种情形我看过太多次核心原因一句话就能讲清作者没把概要设计和详细设计的边界当回事把两份文档该管的事混进了一个文档。做软件项目绕不开这两份设计文档。无论你是项目经理、架构师、开发工程师还是正在为毕业设计或对外交付项目攒材料都需要先想清楚它们的区别。很多团队里的文档审查最后都变成“你写了什么”而不是“这个设计扛不扛得住需求变化”原因不是大家不认真而是从一开始就没想清楚手里的文档应该在哪个层面回答问题。这篇文章就把这件事摊开讲概要设计和详细设计到底差在哪两份文档的模板结构怎么拆以及写详细设计时真正好用的技能和工具。你按着这个思路回去改手里的设计文档评审时被问“这里为什么要这样设计”的概率会小很多。1. 先认清这两份文档在项目里的真实地位1.1 设计阶段坐标概要负责“定骨架”详细负责“填血肉”在软件项目里概要设计和详细设计都处在同一个时间窗口需求已经冻结代码还没动工。说得再直白一点需求分析告诉你“要建一座能容纳一万人的体育场”概要设计决定“体育场分成观众区、比赛区、疏散通道、后勤区主入口放在南侧交通流线怎么走”详细设计则具体到“这根承重柱用多少号混凝土、直径多大、钢筋怎么绑”。所以概要设计更关心系统的“组织方式”详细设计更关心“组成元素内部怎么工作”。我见过不少团队把概要设计当成“需求文档的加长版”整篇在复述用户故事反而模块交互、数据分布这些真正属于概要设计的内容被放到“待讨论”里最后冒出来一份不伦不类的文档。这是个很典型的问题文档没写错但站错了位置。站错位置的文档写得越厚对项目的误导越大。1.2 两套文档到底差在哪一张表说清楚拿登录认证功能举个例子你会更直观地感受到差别。在概要设计里你应该看到的是“认证服务负责用户身份校验与令牌签发用户通过接入层调用认证服务令牌存储在 Redis 中会话默认有效期 2 小时”。而在详细设计里你应该看到的是“LoginController.login(LoginRequest) 的入参出参是什么登录失败的次数超过 5 次如何处理Redis key 的命名规则是什么”。同一个功能两份文档回答完全不同的问题。很多人混着写就是因为把“围绕同一个功能”当成了“写同一套内容”。如果把握不住区别就对照这张表自查对比维度概要设计详细设计阅读视角系统级、模块间模块内、代码级核心产出架构图、模块划分、接口清单、数据关系、技术选型、非功能策略类设计、方法签名、表结构、流程图、状态机、异常处理、伪代码目标读者架构师、项目经理、开发骨干、运维、测试负责人模块开发工程师、测试工程师、未来维护者评审关注点模块边界是否清晰、需求是否全覆盖、技术选型是否合理、扩展性如何照着文档能否写代码、边界情况是否想全、是否可测试写得太粗的后果后期模块之间扯皮返工范围跨团队开发中反复补充设计决策进度失控写得太细的后果需求还没验证就固化细节后续反复改文档文档变成伪代码复述没人愿意读1.3 为什么现实项目里总是混着写第一种是“小项目一锅端”。团队小、工期紧设计阶段压缩成两天一份文档既讲架构又讲方法最后只能两头都不讨好。第二种是“文档名叫概要设计内容却是需求复制品”。整篇都是业务背景和功能列表评审时没人反对因为大家都觉得“反正也没信息量”。第三种是“模板混用”项目组拿了隔壁项目的详细设计模板填概要设计写着写着就变成了字段级描述。判断自己有没有混写方法也很简单拿三五页文档快速翻一遍如果每个功能都直接写成“接口怎么调”却看不到模块和模块之间的职责划分那这本更接近详细设计反过来如果通篇只讲业务场景代码人员看完还要问“我到底该建几张表”那它连概要的门都没摸着。2. 概要设计模板逐段拆解该写的粗颗粒度决策2.1 一份不翻车的概要设计目录长什么样一份标准的概要设计模板通常包含这些部分引言与背景术语定义总体架构系统功能与模块划分模块间接口与交互核心数据模型与数据存储设计非功能需求性能、安全、可靠性、可扩展性部署与运行环境风险分析与设计取舍按我的习惯“风险分析与设计取舍”经常被人删掉但它恰恰最值钱。概要设计阶段最大的价值就是提前暴露“我为了进度砍掉了什么、后续要怎么补”。比如“当前用户中心复用老系统不做单点登录改造下一期再迁”这个记录能避免后期有人拿一个新需求来问“你们当时怎么不考虑这个场景”。没有风险记录的概要设计像一份没有标注暗礁的航海图。2.2 架构图、模块边界和接口粒度怎么落笔总体架构图应该画到什么程度我的经验是能看出系统或服务的层次关系、调用方向和依赖方向即可技术细节标注到关键组件就可以比如“接入层 Nginx”“缓存 Redis”“消息队列 RabbitMQ”。不要在这里画类图更不要把某个接口的请求参数表贴进来。架构图是用来讲故事的不是用来给代码做索引的。模块划分部分每个模块要写三件事职责边界、依赖哪些模块、被谁依赖。能用一句话说清“这个模块管什么、不管什么”的团队后面写详细设计会顺很多。模块间接口这里只列接口名、方向、触发方式以及数据概要。“订单模块调用库存模块的扣减库存能力传入商品编码与数量预期扣减成功后返回剩余库存”就够了字段级契约留给详细设计。有人觉得这样太粗但概要设计本来就不该承担落地细节。2.3 概要阶段的数据与非功能需求决策级不写实现级概要阶段要不要设计数据库要但是设计的是实体和关系不是建表语句。我在概要设计里会用 ER 图把核心实体画出来标出关键属性和关系基数例如“用户 1 对多 订单订单 多对 1 门店”至于主键、索引、字段类型都属于详细设计。如果把 DDL 写到概要设计后面需求一变文档痛点会比代码重构还多。非功能需求这部分最容易被写成口号比如“系统应保证高并发、高可用”。没有数字的描述等于没有任何约束。我在概要设计里通常会写“登录接口在单机 4C8G 配置下支持 200 QPSP99 延迟小于 500ms核心链路依赖的 Redis 采用主从模式RTO 目标小于 5 分钟”。这些目标要在概要设计阶段定下来因为后续编码和压测都拿它当验收标准。概要设计输出的是“决策”不是“过程”这是很多人最容易踩的坑。3. 详细设计模板拆到字段与方法一次登录模块实例看明白3.1 详细设计文档的骨架把“能看懂”推向“能实现”详细设计模板的常见骨架一般是这么一组内容模块概述与设计范围功能流程设计正常流程、异常流程接口设计接口清单、方法签名、入参/出参/错误码数据结构设计表结构、字段说明、索引、数据量预估关键设计决策状态机、并发控制、缓存策略、幂等方案异常与边界处理安全与性能约束上下游协作点这个模板最关键的两处在“接口设计”和“关键设计决策”。很多人写详细设计只把 Controller 的方法签名抄一遍这只能叫“接口登记表”不能叫设计。真正要写的是“为什么这样设计”。方法名和参数类型是编码时顺手就能定的但“为什么并发扣减用 Redis 分布式锁而不用数据库悲观锁”这种内容才是详细设计里别人替代不了的东西。3.2 一个用户登录模块的详细设计长什么样我用“用户登录”这个小模块演示一段。模块概述先写清楚本模块属于认证服务提供账号密码登录能力对接接入层、用户服务、Redis 会话存储。然后接口设计如下POST /api/v1/login 请求参数 - username string必填1~50 字符允许字母数字下划线 - password string必填8~64 字符传输前使用 RSA 公钥加密 - captchaId string必填验证码 ID - captchaCode string必填4 位字符验证码 成功响应 { accessToken: ..., refreshToken: ..., expiresIn: 7200 } 主要错误码 10001 参数格式错误 10002 验证码错误或过期 10003 用户名或密码错误 10004 账号已锁定 10005 账号已被禁用流程设计部分要写清晰接收请求 → 校验验证码 → 校验参数 → 按 username 查用户表 → 解密密码并比对哈希 → 检查账号状态 → 失败次数超限则锁定 → 生成 token 写入 Redis → 写登录日志 → 返回响应。这里要把“失败次数超限”的条件写清楚15 分钟内连续错 5 次锁定 15 分钟锁定时间由 Redis 过期时间控制。数据结构部分给出两张表user 表包含 id、username、password_hash、status、failed_count、locked_until、last_login_atlogin_log 表包含 id、username、ip、user_agent、login_time、result。关键设计决策再补三行密码使用 bcrypt 存储Redis 里 token 的键是 auth:token:{userId}:{sessionId}过期时间 7200 秒。这样一份详细设计新来的开发照着就能写测试也知道造什么数据验证什么场景。3.3 详细设计和概要设计怎么承接细化而不是复制详细设计里要不要重复概要设计里的模块职责不要。你只需要在最开头写一句“本模块承接概要设计中的认证服务”然后把模块边界用一段话带过剩下的精力全部花在接口契约、数据模型和流程分支上。最常见的毛病是“概要里说一遍详细里又说一遍”两遍内容还一模一样等于平白多写一半废字。承接的正确做法是逐层放大概要里有“认证服务负责登录鉴权”详细里才有上面那份接口定义概要里有“会话信息存 Redis”详细里才有 key 命名和过期时间。读者拿两份文档可以一路追下去这才是它们之间的父子关系。4. 设计粒度边界什么时候算“够了”什么时候在过度设计4.1 控制粒度的五条经验规则控制粒度这件事理论讲再多都不如下面五条规则直接如果一段内容出现在概要设计里应该能回答“某个模块或服务该不该存在”而不是“这个函数怎么写”。如果一段内容出现在详细设计里应该让开发在写代码时不需要再问产品经理或架构师“这里遇到异常怎么办”“这个字段要不要加索引”。高复杂度、高风险、多分支的场景详细设计必须细到能用于估算工时我甚至会写出完整异常码清单。低风险的增删改查页面详细设计可以只写“接口 数据表”两层不写界面跳转不写重复的代码结构。判断粗细的终极标准看“改起来影响多大”影响范围跨模块就要在概要里说清楚影响范围只在函数内部不要写进文档。这五条够用。我后面还会讲一条辅助的土办法用来验收自己写好的文档。4.2 不同项目规模的粒度速查表不同规模的项目设计文档的篇幅和颗粒度差异很大。下面这个表是我的经验值不是硬性标准项目类型概要设计参考篇幅详细设计粒度内部小工具、个人项目5~10 页接口 核心流程关键算法伪代码外包、交付型项目15~30 页接口契约 表结构 关键路径时序图中大型单体系统30~50 页类级结构 接口 状态机 异常分支微服务、分布式系统40~80 页服务契约 领域模型 消息格式 分布式事务策略注意页数只是经验参考。项目要求“文档覆盖率”“评审签名”时页数会膨胀但设计含量并不一定随之增加。任何时候都不要为了凑页数复制代码评审专家一眼就能看出来。4.3 从评审里的提问反推文档缺了什么评审是检验粒度最好的镜子。如果评审时大家反复追问“状态在哪个环节变的”“并发情况下会不会超卖”说明详细设计里没有把状态机和并发控制写透。如果评审时大家反复追问“这个模块为什么存在”“为什么不用消息队列”说明概要设计里的模块职责划分和技术选型论证不够。反过来如果概要设计评审会上有人要求你把某个私有方法写出来你可以礼貌地拒绝那是详细设计的事如果详细设计评审会上有人问“这个字段默认值多少”而文档里查不到那是细度出了问题。我判断文档合格有一个很实用的技巧拿着文档找一个没参与设计开发的同事让他根据文档描述把功能实现思路完整讲一遍。讲得出来文档细节够了讲不出来哪里缺就补哪里。5. 写详细设计最实用的skill清单图、契约与工具5.1 先画后写时序图、流程图、状态图分别管什么详细设计里真正好用的 skill首先是画图。顺序是先画三张图再说别的。第一张是时序图用来表达一个请求跨了哪些模块、调了哪些服务、每一步的返回怎么回来第二张是状态图适合表达订单、任务、审批这类有生命周期对象的流转第三张是流程图适合表达有大量条件分支的业务逻辑。工具我常用 Draw.io、PlantUML 或者 ProcessOn选哪个不重要但一定要选择“能用文本或可导出文件做版本控制”的否则图一改旧版就丢了。很多人一上来就写大段文字写到一半发现模块调用关系说不清就是因为时序图没先画。注意图纸是给人快速建立共识用的数量要克制宁可一张图画三遍优化也不要一口气贴二十张图。5.2 接口契约要写到什么程度才算“共识”接口契约写作水平直接决定详细设计是否可执行。我的接口文档模板包含接口名与路径、协议与请求方式、入参字段及校验规则、出参结构、错误码列表、权限要求、幂等策略、超时与重试约定、安全要求、依赖的资源。拿“支付回调”举例光写“接收支付结果修改订单状态”等于没说。应该写入参有 orderNo、channelOrderNo、amount、paymentResult回调处理时以 channelOrderNo 判重金额不一致时记录可疑事件并返回失败接口需要在白名单 IP 范围内调用且验签处理完成要返回“SUCCESS”给支付渠道幂等键用 orderNo。到了这个程度开发和渠道对接人员才能各自开工而不是互相等。接口契约里的每个字段至少要能回答“谁传的、什么时候传、取不到怎么办”。5.3 把文档当代码管文本化工具与版本管理文档应该进版本库跟代码一起管理而不是散落在 Wiki 或网盘。我建议用 Markdown 写文档配合 Git 仓库管理每次评审意见合并到文档的过程就是一次 commit评审记录和版本 diff 都有据可查。文本化比 Word 更有优势的一点是它可以在需求或代码变更时被自动 diff 出来而 Word 很难做到逐行比较。另一种值得采用的做法是用 OpenAPI 描述对外接口再用它生成接口文档。这样详细设计里的接口契约可以保持同步还能直接拿来跑 mock 服务前端和后端不用等对方写完代码再联调。写详细设计“让文档活起来”比“写得很厚”有价值得多这是我认为最值得推荐的工作方式。5.4 团队模板怎么沉淀而不是摆设团队沉淀模板不是为了填鸭而是为了把每次评审的教训固化下来。模板的本质是“上一批人踩过的坑的地图”。我每做完一个项目会把评审意见里那些“没写清”的地方对应到模板里。例如这次发现“缓存失效策略没人在文档里说清”那就在模板的“关键设计决策”这一类里加一行“缓存key 规则、过期时间、失效处理”。模板一旦固定下来就不要频繁加章节否则两三年后模板会变得比项目文档还大没人愿意填。更好用的做法是团队共用一份设计文档模板再按项目类型在三个可选项里勾选单体服务、前端应用、微服务接口。沉淀的是“思考路径”不是“章节数量”。有人问“代码详细设计的 skill 有哪些好用”我的回答永远是先掌握控制视角的能力工具和模板反而是最容易学会的部分。6. 评审现场翻车记录文档失败是怎么发生的6.1 翻车一100页详细设计评审时没人想问一次评审同事负责的模块写了一份一百多页详细设计每张页面都有类图和方法签名。评审会上我们安静地翻了二十分钟没人提问不是没有疑问而是信息太多不知道从哪里问起。最后我问了一句这个模块和订单模块之间的接口消息是推还是拉他愣住了因为文档里根本没写模块间的协作方式。这就是粒度选错了方向。把力气花在“类图有多完整”却漏了模块之间的调用方式。那次之后我们立了个规矩详细设计先写接口契约和状态机类图只画关键部分谁都不许把工具自动生成的类图直接贴进来当设计。6.2 翻车二概要设计把字段钉死模块边界却错了另一个项目概要设计评审时架构师把所有下游系统的接口字段都定了。开发照着文档写完后才发现库存模块和订单模块的边界画反了本该由订单服务发消息通知库存服务结果被设计成库存服务定时轮询。因为概要设计里把字段细节钉得太死大家注意力都在“字段对不对”反而没人质疑“模块关系对不对”。这个案例说明概要设计阶段要克制写细的冲动。字段细节保护不了错误的模块边界相反因为太细评审只会看到叶子看不到树根。概要设计里的每一页都应该服务于一个目的让评审者看清这张系统的骨架图而不是提前陷入实现细节。6.3 翻车三没有设计决策记录三个开发写出三套逻辑最坑的还不是写太多而是不记录“为什么这样设计”。同一个模块分配给三个开发同事有人用乐观锁有人用悲观锁有人干脆不加锁测试说线上出现重复发放谁都不认为是自己的问题。因为详细设计里只写了“领取红包接口”没有写并发控制策略和设计依据。从那以后我要求详细设计里每个关键决策必须带一句“为什么”比如“采用 Redis 分布式锁因为需要跨服务锁不采用数据库锁因为会影响该表写入吞吐”。这一句话看似简单遇到线上问题能省掉一整天的扯皮。设计决策不落纸面等于把最值钱的经验随手扔掉了。最后说一条我的土办法每次写完设计文档先假想自己是三个月后被临时拉来维护这套代码的同事脑子里过三个问题这个模块是谁的职责它和谁协作消息走哪个通道状态什么时候变变了怎么办如果文档都能找到答案我才会把文档发出去评审。设计文档写得粗还是细没有绝对标尺但有一个目标从来不偏让别人在离开文档后不再需要猜设计意图。你在项目里一次一次试会慢慢找到自己团队最舒服的粒度。到那时候概要设计和详细设计这两份文件就不再是流程负担而是项目里真正值钱的资产。