在软件工程的全过程中,有两个阶段最为关键:产品设计与架构设计。前者由产品经理主导,关注"如何以产品特性系统化地满足用户需求";后者由架构师主导,关注"业务系统如何系统化地进行分解与交付"。
无论是产品文档还是架构文档,它们都是设计文档的一种,本质都是团队内及团队间协同的共识载体。上一篇 [69讲] 从协同角度谈了共识的重要性,本节我们聚焦:如何将共识精确地表达为设计文档。
交叉参考:[45讲 - 架构:怎么做详细设计?] 已介绍模块级设计文档的写法,本节补充系统概要设计与产品设计文档的异同。[32讲 - 架构:系统的概要设计] 讨论了系统分解的逻辑。
所有设计文档的内容组织逻辑是相通的,无论产品设计还是架构设计,都遵循以下四段式结构:
| 要素 | 写法要求 | 核心关注点 |
|---|---|---|
| 现状 | 不要长篇累牍。陈述与改变相关的重要事实,强调存在性和重要性 | 我们在哪里 |
| 需求 | 不需要长篇累牍。痛点够痛大家都知道,这是对痛点和改进方向的共识确认 | 问题是什么 |
| 需求满足方式 | 详写。包括交付物规格和使用界面(接口) | 做成什么样 |
| 实现原理 | 数据结构 + 算法。用 UML 时序图或伪代码表达 | 怎么做到 |
产品经理与架构师是一体两面,对人的能力要求相似,但关注维度不同:
| 维度 | 产品设计 | 架构设计 |
|---|---|---|
| 关键词 | 用户需求、技术赋能、商业成功 | 用户需求、技术实现、业务迭代 |
| 交付物规格 | 产品原型 | 网络 API 协议 / 包导出的公开类或函数 |
| 实现原理 | UserStory 设计——业务流怎么完成 | UserStory 的程序逻辑实现 |
| 数据结构 | 业务对象模型 | 内存/外存数据结构、数据库表结构 |
| 算法 | 业务流程 | UML 时序图、伪代码 |
这是设计文档"实现原理"部分的指导思想:
许式伟的关键判断:设计是软件工程中的头等大事,我们应该在设计中"多浪费点时间",这样的"浪费"最终会得到十倍甚至百倍以上的回报。如果两套方案的比较优势不显著,写两套独立的设计文档是应该被鼓励的。
在描述交付物规格时,有两个核心关注点:
对于系统的概要设计:
以 MVC 为例:
| 维度 | 系统概要设计 | 模块详细设计 |
|---|---|---|
| 关注焦点 | 不同模块的配合关系 | 模块内部实现 |
| 数据结构 | 不需要交代 | 必须先交代清楚 |
| UserStory | 讲清楚模块间协作流程 | 讲清楚内部业务流程 |
| 伪代码 | 必须——表达模块间交互语义 | 必须——表达业务逻辑 |
伪代码的表达方式及语义需要在团队内形成默契。这种伪代码的语义表达必须是精确的:
post /v1/foo/bar json {...} / ret json {...})许式伟以画图程序为例,从"最小化核心系统 + 多个彼此正交的周边系统"的视角表达模块关系:
这种表达方式的关键:周边系统彼此正交,通过核心系统间接交互。但要注意,模块关系图仍然粗糙——为了共识的精确,必须将各模块核心的使用界面(接口)表达出来。
很多人喜欢画"上传文件的业务流程图"这类图——数据从客户端到 API 网关,到业务服务器,到存储。这类图:
正确的做法:对模块调用接口进行分类(DOM API / Event / Plugin),通过接口类型表达模块间的依赖关系。
正如 [69讲] 所强调的,概要设计阶段最好的状态不是只有设计文档,而是同时有代码产出——系统初始框架 + 核心模块 mock 实现。这能极大降低"各模块做好了但拼不起来"的风险。
设计文档的四要素:现状、需求、需求满足方式(交付物规格)、实现原理(数据结构 + 算法)。其中需求满足方式和实现原理必须详写。
规格高于实现:交付物规格(接口)是团队间最重要的契约,必须精确无歧义。架构图只是辅助,接口定义才是正式契约。
多方案对比应被鼓励:设计是头等大事,在设计中"浪费"时间回报百倍。当方案比较优势不显著时,写两篇独立的设计文档。
模块关系的三种接口类型:DOM API、Event、Plugin。通过接口分类表达模块间依赖关系,比画流程图更有抽象力。
伪代码必须精确:团队内形成默契的伪代码表达方式,语义必须无歧义。
概要设计与详细设计的差异:概要设计关注模块配合,无需交代数据结构;详细设计关注内部实现,必须先交代数据结构。
下一篇 [71讲 - 如何阅读别人的代码] 将探讨设计文档的反向过程——如何从代码中还原架构设计。