可以说在我们的日常工作中,最让很多程序人员感到头痛的不是一日三变的需求,也不是永远也解不完的Bug,而是被要求写设计文档。
我曾经遇到很多程序员,写程序很敏捷,上千行的代码眉头都不眨一下,但几百字的设计文档却憋得脸红脖子粗,憋了好几天终于写完了,然后赶紧去写几行代码压压惊。这还不算,写出来的文档要么不知所云,要么思路跳跃缺东少西。
其实,写设计文档并不像写散文、小说那样需要考虑如何遣词造句,也不需要修辞华美、文采飞扬,反而需要用最通用的词汇,陈述最清晰的逻辑。
这年头到处都是套路,所以根据我的多年经验,这个编写设计文档也是有套路的。
设计文档,说白了就是对系统设计过程的一次复盘。所以一般我们写设计文档的顺序就和做设计时的思路顺序是基本一致的,大都是先对整个系统有一个粗线条的勾勒,然后在初步细化,最后精工细笔的把整个系统描绘清晰。
那么一篇完善的系统设计文档从开始到结束都需要具备哪些内容呢?
我曾经遇到很多程序员,写程序很敏捷,上千行的代码眉头都不眨一下,但几百字的设计文档却憋得脸红脖子粗,憋了好几天终于写完了,然后赶紧去写几行代码压压惊。这还不算,写出来的文档要么不知所云,要么思路跳跃缺东少西。
其实,写设计文档并不像写散文、小说那样需要考虑如何遣词造句,也不需要修辞华美、文采飞扬,反而需要用最通用的词汇,陈述最清晰的逻辑。
这年头到处都是套路,所以根据我的多年经验,这个编写设计文档也是有套路的。
设计文档,说白了就是对系统设计过程的一次复盘。所以一般我们写设计文档的顺序就和做设计时的思路顺序是基本一致的,大都是先对整个系统有一个粗线条的勾勒,然后在初步细化,最后精工细笔的把整个系统描绘清晰。
那么一篇完善的系统设计文档从开始到结束都需要具备哪些内容呢?
1. 需求回顾
任何设计一定是源于某种需求的,这个需求可以有很多来源:
也许是来自产品经理的产品规划;
也许是来自市场经理的调研报告;
也许是与客户沟通的一若干邮件或者聊天记录;
也许是一次会议后的会议记录;
也许是某位领导某天拍脑袋想出来的一些思路;
总之,在一篇设计文档的开篇第一件事情就是要对整篇设计的需求来源做一个简单的回顾,把后续设计需要覆盖的需求列个一二三四五,如果需求还没搞清楚,那个人建议还是不要写这个设计文档,赶紧去沟通需求,否则真是在极大地浪费时间了。
在需求回顾这个部分中,如果此前自己或者别人恰巧已经做过了需求分析或者预研工作,那此部分只要简单地对需求进行罗列,并将之前的分析结论附在后面即可,只要记得在文档的结尾处注明分析数据的来源和出处即可。
如果此前未做过相关工作,那麻烦一些,需要对每条需求从技术角度进行一个简单地阐述,不需要洋洋洒洒地下笔千言,只需要从专业角度简单地说明行或不行,并附上可能需要的数据论据做支撑,仅此而已。
任何设计一定是源于某种需求的,这个需求可以有很多来源:
也许是来自产品经理的产品规划;
也许是来自市场经理的调研报告;
也许是与客户沟通的一若干邮件或者聊天记录;
也许是一次会议后的会议记录;
也许是某位领导某天拍脑袋想出来的一些思路;
总之,在一篇设计文档的开篇第一件事情就是要对整篇设计的需求来源做一个简单的回顾,把后续设计需要覆盖的需求列个一二三四五,如果需求还没搞清楚,那个人建议还是不要写这个设计文档,赶紧去沟通需求,否则真是在极大地浪费时间了。
在需求回顾这个部分中,如果此前自己或者别人恰巧已经做过了需求分析或者预研工作,那此部分只要简单地对需求进行罗列,并将之前的分析结论附在后面即可,只要记得在文档的结尾处注明分析数据的来源和出处即可。
如果此前未做过相关工作,那麻烦一些,需要对每条需求从技术角度进行一个简单地阐述,不需要洋洋洒洒地下笔千言,只需要从专业角度简单地说明行或不行,并附上可能需要的数据论据做支撑,仅此而已。
2. 顶层设计
系统的顶层设计是围绕着需求展开的,其最基本的要求就是设计出来的系统能够覆盖所有可行的需求。
系统的顶层设计包含两个部分的内容:
(1) 环境要求
这个环境要求是指系统运行所需的软硬件环境,其中:
硬件环境包括:基本电气要求、环境温湿度约束、存储约束、网络要求等;
软件环境包括:运行所需软件平台、开发工具链、依赖库、技术栈选择等;
依然不需要长篇大论,搞一张表格足以。
系统的顶层设计是围绕着需求展开的,其最基本的要求就是设计出来的系统能够覆盖所有可行的需求。
系统的顶层设计包含两个部分的内容:
(1) 环境要求
这个环境要求是指系统运行所需的软硬件环境,其中:
硬件环境包括:基本电气要求、环境温湿度约束、存储约束、网络要求等;
软件环境包括:运行所需软件平台、开发工具链、依赖库、技术栈选择等;
依然不需要长篇大论,搞一张表格足以。
(2) 架构设计
实际上,系统顶层的设计通常是从一张系统架构图开始的,图形是信息量极大的一种表达方式,而且我通常也会要求团队中编写设计文档的人一定要提供系统的总体设计框图。
对于系统性的设计文档,尤其是针对初次接触你设计的阅读人员,一份结构清晰的系统架构图能够快速、直观地帮助对方理解你的设计思路,并且在其大脑中建立起对你设计的一个准确的总体印象,为后续的沟通、交流提供一个公共的理解基础,缺少这个总体印象,无论是后续的设计评审还是项目团队沟通常常都会出因此现理解上的偏差。
当然,仅仅是系统的总体框图还不够,根据我的理解,系统的总体设计至少要包含静态和动态两个层次。
所谓静态,就是指系统的架构设计框图,描述了参与系统架构的各个组件,组件的从属,以及组件之间的静态层次关系,UML中的部署图提供了很多不错的基础元素,当然你也可以根据你的需要在部署图的基础上做扩展:
实际上,系统顶层的设计通常是从一张系统架构图开始的,图形是信息量极大的一种表达方式,而且我通常也会要求团队中编写设计文档的人一定要提供系统的总体设计框图。
对于系统性的设计文档,尤其是针对初次接触你设计的阅读人员,一份结构清晰的系统架构图能够快速、直观地帮助对方理解你的设计思路,并且在其大脑中建立起对你设计的一个准确的总体印象,为后续的沟通、交流提供一个公共的理解基础,缺少这个总体印象,无论是后续的设计评审还是项目团队沟通常常都会出因此现理解上的偏差。
当然,仅仅是系统的总体框图还不够,根据我的理解,系统的总体设计至少要包含静态和动态两个层次。
所谓静态,就是指系统的架构设计框图,描述了参与系统架构的各个组件,组件的从属,以及组件之间的静态层次关系,UML中的部署图提供了很多不错的基础元素,当然你也可以根据你的需要在部署图的基础上做扩展:
而动态,包含数据流向和业务逻辑,例如:
组件、数据流和业务逻辑这三者基本上就涵盖了系统顶层设计的基本要素。
之后只要为每张设计图配上说明文字,最简单的做法是将图形表达的信息文字化,此外还建议包含一些其他的重要信息,这就完成了系统的顶层设计。
其中架构设计的文字描述包含:
(1) 系统分层功能描述;
(2) 组件功能描述;
(3) 组件功能约束;
对于数据流图应该包含:
(1) 数据格式规约;
(2) 流量、响应时间与信息安全等约束;
对于业务逻辑应该包含:
(1) 业务的触发条件和终止条件;
(2) 参与对象的生命周期;
之后只要为每张设计图配上说明文字,最简单的做法是将图形表达的信息文字化,此外还建议包含一些其他的重要信息,这就完成了系统的顶层设计。
其中架构设计的文字描述包含:
(1) 系统分层功能描述;
(2) 组件功能描述;
(3) 组件功能约束;
对于数据流图应该包含:
(1) 数据格式规约;
(2) 流量、响应时间与信息安全等约束;
对于业务逻辑应该包含:
(1) 业务的触发条件和终止条件;
(2) 参与对象的生命周期;
此外,如果系统设计到安全领域,还应专门列出关于安全性的相关设计,这里就不过多展开了。
3. 组件级别设计
完成了顶层设计之后,接下来需要围绕着顶层设计进行展开和深入。
组件级别设计的基本要求就是覆盖系统架构设计中描述的所有组件,所以一个很好的实践建议就是,按照系统架构设计中组件的名称和顺序设计这一章的小节目录,为每个组件留一个小节做具体展开描述。
组件级别的设计与系统级别的设计侧重点完全不同,系统级别的设计侧重于系统结构、功能、以及总体的运作方式,而组件级别的设计重点是所需的数据格式、具体功能的实现方案、和接口详细描述。
所以每个组件小节中的内容都可以大体分为这三个部分:
(1) 数据格式
数据格式部分描述了组件涉及的:输入、输出以及重要的运行时数据结构。
对于不同的组件可能有不同的表现形式:
有可能是一张数据库的E-R关系图;
有可能是嵌入式平台的Flash存储划分;
还有可能是磁盘中的文件格式设计;
或者是组件运行中在内存中创建的重要数据结构。
在组件级别的设计中,不要求设计出实现过程中所有可能用到的设计结构,但重要的数据格式,尤其是多个组件共用的数据格式,一定要描述的清楚明白。
完成了顶层设计之后,接下来需要围绕着顶层设计进行展开和深入。
组件级别设计的基本要求就是覆盖系统架构设计中描述的所有组件,所以一个很好的实践建议就是,按照系统架构设计中组件的名称和顺序设计这一章的小节目录,为每个组件留一个小节做具体展开描述。
组件级别的设计与系统级别的设计侧重点完全不同,系统级别的设计侧重于系统结构、功能、以及总体的运作方式,而组件级别的设计重点是所需的数据格式、具体功能的实现方案、和接口详细描述。
所以每个组件小节中的内容都可以大体分为这三个部分:
(1) 数据格式
数据格式部分描述了组件涉及的:输入、输出以及重要的运行时数据结构。
对于不同的组件可能有不同的表现形式:
有可能是一张数据库的E-R关系图;
有可能是嵌入式平台的Flash存储划分;
还有可能是磁盘中的文件格式设计;
或者是组件运行中在内存中创建的重要数据结构。
在组件级别的设计中,不要求设计出实现过程中所有可能用到的设计结构,但重要的数据格式,尤其是多个组件共用的数据格式,一定要描述的清楚明白。
(2) 实现方案
此部分对于结构简单的组件,只需要几句文字描述,复杂一些的组件可能需要提供一张组件工作的流程图,对于更加复杂的顶层组件,也可以在细分为若干子组件,描述方式参考系统的顶层设计。
此部分对于结构简单的组件,只需要几句文字描述,复杂一些的组件可能需要提供一张组件工作的流程图,对于更加复杂的顶层组件,也可以在细分为若干子组件,描述方式参考系统的顶层设计。
(3) 接口描述
接口描述是组件设计中最终要的部分,可以说组件内部的数据格式也好,实现流程也好,都是可以随时变动的,但接口部分,一旦设计成型并且评审通过,一般来讲是尽可能不要修改的,尤其是在多人协作的团队中,擅自修改接口是大忌。
根据组件应用的环境不同,接口的方式也不尽相同,例如:
对于库形式的组件,接口就是暴露出来的API,这类接口描述名称、功能、参数、返回值缺一不可,建议直接粘贴方法的声明;
对于远程组件,接口就是通信规范,包含通信使用的物理媒体、信道参数、加密要求以及最重要的通信协议;
接口描述是组件设计中最终要的部分,可以说组件内部的数据格式也好,实现流程也好,都是可以随时变动的,但接口部分,一旦设计成型并且评审通过,一般来讲是尽可能不要修改的,尤其是在多人协作的团队中,擅自修改接口是大忌。
根据组件应用的环境不同,接口的方式也不尽相同,例如:
对于库形式的组件,接口就是暴露出来的API,这类接口描述名称、功能、参数、返回值缺一不可,建议直接粘贴方法的声明;
对于远程组件,接口就是通信规范,包含通信使用的物理媒体、信道参数、加密要求以及最重要的通信协议;
4. 调试辅助与测试工具
没有哪本书或者哪篇文章中将调试辅助系统与测试工具的设计放到系统设计的文档中,但根据我多年的工作经验,在编写系统设计文档的时候,一定要涉及这方面的内容。
一直以来我们的开发过程对调试和测试的工作重视程度都很不够,认为那是底层开发员和测试人员需要考虑的事情,但往往是调试与测试过程消耗了最多的项目研发时间。
所以我建议,在系统设计之初,就将调试辅助系统与测试的桩程序作为系统设计的一部分,其中:
(1) 调试辅助
在这部分的内容中,明确定义各个组件的故障码表,可能发生的故障以及故障的处理流程。
对于复杂的系统,提供故障的分级机制,并强烈建议区分运行模式和Debug模式。
(2) 测试工具
在我经历的绝大部分项目中,正式的测试工作都起始于集成测试,而白盒测试永远都停留在了教科书中,被项目经理们忽略而过。
为什么会如此呢?究其原因就在于,白盒测试要覆盖代码的执行路径和各种逻辑条件,这样的工作也只有开发人员可以做,但开发人员的时间大都被划分在了功能开发和后期的Bug修复中,测试工作都是由测试人员来承担,而测试人员拿到的又都是关于代码的黑盒子,所以白盒测试过程就这样被放弃了。
不仅是白盒测试,在一些项目中,测试大部份依赖现有的测试工具,很多时候由于已有的测试工具无法很好的适配系统,所以测试过程十分耗时,并且效率低下。
所以,在系统设计之初就应考虑到系统的测试手段,结合不同组件的接口以及内部的逻辑、条件,设计一套自动化的测试方案,一方面可以极大地提升系统质量,另一方面还能有效节省后期测试以及Bug修复所花费的时间。
没有哪本书或者哪篇文章中将调试辅助系统与测试工具的设计放到系统设计的文档中,但根据我多年的工作经验,在编写系统设计文档的时候,一定要涉及这方面的内容。
一直以来我们的开发过程对调试和测试的工作重视程度都很不够,认为那是底层开发员和测试人员需要考虑的事情,但往往是调试与测试过程消耗了最多的项目研发时间。
所以我建议,在系统设计之初,就将调试辅助系统与测试的桩程序作为系统设计的一部分,其中:
(1) 调试辅助
在这部分的内容中,明确定义各个组件的故障码表,可能发生的故障以及故障的处理流程。
对于复杂的系统,提供故障的分级机制,并强烈建议区分运行模式和Debug模式。
(2) 测试工具
在我经历的绝大部分项目中,正式的测试工作都起始于集成测试,而白盒测试永远都停留在了教科书中,被项目经理们忽略而过。
为什么会如此呢?究其原因就在于,白盒测试要覆盖代码的执行路径和各种逻辑条件,这样的工作也只有开发人员可以做,但开发人员的时间大都被划分在了功能开发和后期的Bug修复中,测试工作都是由测试人员来承担,而测试人员拿到的又都是关于代码的黑盒子,所以白盒测试过程就这样被放弃了。
不仅是白盒测试,在一些项目中,测试大部份依赖现有的测试工具,很多时候由于已有的测试工具无法很好的适配系统,所以测试过程十分耗时,并且效率低下。
所以,在系统设计之初就应考虑到系统的测试手段,结合不同组件的接口以及内部的逻辑、条件,设计一套自动化的测试方案,一方面可以极大地提升系统质量,另一方面还能有效节省后期测试以及Bug修复所花费的时间。