1、背景
随着公司的不断发展,业务对技术的要求也比原来更高,项目的数量越来越多、团队人数也越来越多、项目的质量要求也越来越高。随着项目的不断立项,对项目在前期的设计要求也逐步确立下来。于是,项目中技术设计文档的设计也需要留存下来。
目前,技术项目目前没有统一的规范,当前技术设计的文档也层次不齐。另外一方面,更为重要,我们目前的项目设计到底是一个什么样的标准?是否经得起考验?
如果你要写一个技术技术设计文档,本文将介绍如何写好。
1.1、为什么要写技术设计
如同解释为什么要做计划一样,我们写技术设计文档,主要有如下目的:
1、便于沟通。所谓“一图胜前言”,有文档的存在,将帮助大家了解要解决的问题、实现的方法、当前方案的局限、未来的扩展和升级、维护和运维等,用途上可以用于方案review、技术“遗产”等
2、技术的延续与传承。技术的资料帮助后续开发者可以延续之前的技术,而不是在不清楚的情况下,进行重构
3、完善思考。帮助负责人设计出技术方案更合理、有效,降低项目失败的概率
1.2、本文适合哪些人阅读
项目负责人、架构师、工程师,以及其他感兴趣的同学。
2、开始设计
编写技术设计文档,如同整体思路的完整表达一样,需要有一定的逻辑性(思路),这就是我们所谓的轮廓。
2.1、文档结构
背景介绍 → 设计思路 → 详细方案 → 补充材料
换句话说,上面的逻辑可以描述为:
- 为什么要做这个技术方案
- 我们打算用什么方案做
- 这个方案的实现细节是什么样子
- 我参考了哪些资料(帮助背书)
2.1.1、万事开始:背景介绍
背景介绍主要描述了,为什么要做这个项目。一般这部分可以分为三部分:
1、背景,重点描述“为什么要做”。一般是两种情况:解决问题和获取收益。(当然,有种情况是,产品经理或者老板要这个功能,属于业务需求,这个一般也算在收益类),比如:
最近一个月出现了7起因网络原因导致的投诉,目前已有的方案处理时间太长(4-24)小时,导致客服与技术这边非常被动。因此,我们发起这个项目,来解决类似的问题。
2、范围,对本文描述的话题设定一个边界,即:在有限的范围内讨论,以避免话题一发不可收拾。这里可以说明一下,这次设计方案的适用场景,要解决的问题的边界在哪里(要准确,不要详细),比如:
本文仅描述在APP网络条件下,基于HTTP协议网络不可达的场景
3、定义(可省略),比如:专有名词,文内涉及到的自创名词,公司内名词的解释等,这部分可以省略;比如:
DNS:DeDao Name Service,服务注册与发现(命名服务)
GW: DeDao Gateway,网关
4、现状,描述了我们目前存在的问题,带来的影响面等,比如:
出现网络问题后,我们解决的时间太长:目前用户出现网络相关的问题,我们只能寄希望于CDN服务厂商来解决,这个周期在4-24小时。
5、目标,描述了这次方案要达成的效果,可以获得的好处,比如:
当网络问题出现后,可以在10分钟内完成处理(总体的好处,与现状中描述的问题相呼应)
至于目标中涉及到的一些其他限制,可以放在附录里面(比如:基础的QPS限制等)。
2.1.2、打算怎么做:设计思路
有了问题,有了目标,接下来就是:怎么做了。
首先,我们要看看问题出现的原因是什么(定位问题),这一步非常关键,因为找准敌人是我们能不能提供有效方案的前提。
比如,网络连通性问题,造成用户连不上服务的原因,归结为两类:
1、DNS将用户调度到了错误的节点,导致这个节点无法连通
2、DNS将用户调度到了正确的节点,但是这个节点故障了(无法提供正常的服务)
那么针对上面的两个原因,我们就得有针对性的提供解决方案:
1、DNS的问题,使用IP访问的方案,或者 httpDNS的方案(两个都是可行的方案)
2、节点故障的问题,目前通知CDN合作方来调整节点,另外也可以提供多个节点做灾备
有些时候,我们需要多方案对比。针对一个问题,理论有无数的解,那么这个时候一般需要思考:哪一种核心思路是最合适的(当下)。一般情况下,我们会列至少两种可行的解决思路,来做对比。
比如:
针对节点故障的问题,有如下解决方案:
1、人工容错,通知CDN合作商进行节点调整(修复),周期4-24小时
2、提供多个CDN节点做灾备,由客户端调度
针对上面两个方案,方案1,成本小,但是周期长(SLA达不到);方案2,实现成本大,但是SLA效果好,另外还能带来CDN成本降低的好处(虽然跟目标不符,但是也是一个好处)
这个时候,得开始描述,用了方案2,打算怎么做了。
2.1.3、说说怎么做:技术设计
有了基本的设计思路,那么如何通过设计把思路实现。这里重点描述怎么做,以及在一些技术的选型上如何取舍。
2.1.3.1、设计的准则
我们做设计,都会有一个好坏的标准(比如做技术设计评审,也会有一个是否通过的标准),那么设计什么样的设计是好的设计?从我接触到的一些人(P8P9大神)对这个问题的理解(这部分可以参考:极客时间里李运华的《从0开始学架构》),我整理总结了一下:
- 合适即可,少做过渡设计,不做勉强实现。这里有个度,即:什么程度合适,这个就需要拿捏了(经验的区别就出来了)
- 简单即可,如果可以有更简单的办法实现,那么用简单会比复杂的好
- 演化改进,“演化优于一步到位”
(上面三个我就不解释了,纯是观念问题,理解即可)
另外,我们对设计上有一些基本的描述,这些会有两个方面:设计原则和设计特性。
所谓的设计原则,一般情况下我们讲的是六大设计原则:单一职责(SRP)、开闭原则(OCP)、里式替换(LSP)、依赖导致(DIP)、接口隔离(ISP)、迪米特法则(LoD)。这部分这里不做赘述,各位可以自行Google(红色加粗表示重要)。
设计特性上,我整理了一份常见的特性:稳定性(鲁棒性)、可测试性、高可用性、可扩展性、最小复杂度、可维护性、松散耦合、可重用性、可移植性、层次性。
一般情况下,遵守上面的设计原则的设计总是比较好的(目前我没有找到反例),满足实现原则的设计,在系统特性上,一般也具备比较多的特性。比如:
满足OCP和SRP的设计,一般都松散耦合和可扩展的,做得好的,还可以满足:可维护、最小复杂度、可重用、可移植和层次性。
以上的原则和特性,需要慢慢练习,至于它的好处,也是慢慢会体现出来。
2.1.3.2、概要设计
一般情况下,我们会使用“总-分”的思路来写内容,即:先写整体框架(架构)是怎么样的,然后再分别写每个模块是怎么做的。概要设计,就是描述技术的整体怎么做。
总体设计中,可以分为以下几种场景:
1、整体设计里面包含各子系统或子模块,那么需要做整体的架构图(可以是框图、也可以部署图),来区分彼此的定义
2、如果是业务类型的设计(如实现某个业务的接口、过程等),可以有流程图来描述。
上面说的需要使用图标来描述,这里是个建议,不是必须的;如果觉得文字能够说明白,那么可以省略画图。
概要设计中,描述了多个子系统或者多模块的“定位”,即满足系统三要素:要素(命名)、关系、定位。
命名(要素):这个模块叫什么,比如:接口层、调度器等
关系:模块和模块之间的关系,这里一般讲的是:越往上越贴近业务,越往下越贴近实现
定位:也可以叫功能、作用,它被设立的目的是什么,这部分需要满足单一职责,即:一个模块干一件事
2.1.3.3、业务设计/模块设计
这部分可以有多个,主要描述各个模块的具体实现。这部分一般有两类:
- 业务类,比如:业务接口
- 模块类,比如:存储模块、算法模块
这里,如果是业务类,需要写清楚业务的流程,即:业务实现的各种可能性,输入是什么、输出是什么,实现什么feature,实现的逻辑。注意:这部分需要满足:接口的单一职责和接口隔离,即:一个接口干一件事。
模块类,一般根据模块的定位来描述,比如:这个模块主要是做算法的,那么重点写清楚这个算法实现;如果这个模块是做存储的,可以写清楚存储的数据结构;除此之外,有时候也必要提供对外的接口。
2.1.3.4、存储设计
曾经有位大哥讲:计算机系统=数据结构+算法。那么这部分就是我们所谓的数据结构。在目前的分布式系统中,大部分的存储分为:
- 缓存(Redis、Memcache等)
- 业务数据(Mysql、MongoDB、Redis等)
- 基础数据(Mysql、MongoDB、HBase等)
- 文件存储(OSS、S3、其它文件存储)
这里存储主要在数据库上面(这部分根据重点,有的项目重点在缓存上,有些在文件上),数据库建议这里写清楚:数据库的建表语句和核心查询SQL。
2.1.3.5、降级与预案
这部分主要描述,当我们的正常业务不可用、使用超出系统限制的时候,会如何反应。这部分可以分场景(分情况)来描述,讲清楚即可。
2.1.3.6、运维与部署
一般情况下可以省略。如果涉及到跨系统调用、中间件,这里建议有一个部署图,并描述清楚,哪些是水平扩容、哪些是主备方式。
2.1.4、补充材料
补充材料可以描述,我们在做调研的过程中,涉及到的一些参考资料(可以是书、文章、论文);另外,有一些不方便在前面描写的,也可以在这里写一写,比如:错误编号等。
2.2、如何做得更好
说完了基本结构,表示我们可以先简单写一个文档了。那么如何将文档写得更好一些,可以让他人读起来更准确、高效理解设计的内容?
2.2.1、方案是完整的
方案设计是完整的,一个设计一般会考虑:
- 业务上满足产品需求
- 测试上满足质量需求
- 时间上满足项目需求
- 运维上满足部署需求
(上面的话也不是我说的,是某知名架构师说的)
2.2.2、方案是有条理的
好的设计就像讲故事一样,有前因后果,逻辑清晰,能从一处引出到另外一处。
2.2.3、方案是易读的
并不是晦涩难懂的方式是牛逼的方案,深入浅出的东西容易让他人一目了然(当然也不反对有些人为了写的爽)。那么我们也会有一些技巧可以让方案更容易被读懂。
2.2.3.1、使用结构化表达代替完整的段落
如果某些内容可以用:1、2、3来表达,那么尽量用它来描述,比如:
这个设计讲了如何使用CDN,也讲了CDN的限制,说明的CDN的价格,最后还解释了哪些场景不能使用CDN。
可以修改为:
- 如何使用CDN
- CDN的限制
- CDN的价格
- 哪些场景不适合
2.2.3.2、使用表格来描述二维数据
如果可以用表格描述的,尽量用表格,方便一目了然。
2.2.3.3、使用图
一图胜千言,也是这个意思。关于技术设计的五个图,参考:软件工程常用的五种图
这里,其实还少了一张图(这个图在某些场景下也非常有效),叫:用例图,这种图可以更好的描述用户的功能(需求)。
2.2.3.4、合适的粒度,可以帮助表达得更准
当我们在描述一个概要设计的时候,这个时候更关注整体系统有多少个子系统,分为了多少层,那么在这个设计里面,类图就是不合适的。如果我们的设计是关于某个算法的,那么在这里花大量时间描述为什么要做这个算法也不合适。
合适的粒度可以帮助设计更有主次,更容易让表达不跑偏,从而实现准确。
2.2.3.5、标题有层次有序号
如同本文一样,有不同级别的标题、有序号,是不是更清晰一些?
3、附录
3.1、参考资料
- 《软件开发过程中的浪费--详细设计》http://www.cnblogs.com/stephen-wang/archive/2012/11/12/2767021.html
- 《软件质量模型的6大特性》https://blog.csdn.net/u012841352/article/details/54237654
- 《软件的6大特性》https://blog.csdn.net/wyl836662856/article/details/51860732
- 《代码大全》第二版
- 《设计模式之六大原则》http://www.cnblogs.com/dolphin0520/p/3919839.html
- 《架构设计的三原则》 李运华,极客时间《从0开始学架构》
- 《UML用例图》https://blog.csdn.net/wrs120/article/details/52612107
3.2、参考样例
下面这些参考样例,来自于不同公司的不同风格,供大家参考。
- bilibili 高并发实时弹幕系统的实战之路 ,来自刘丁,重点看文章的逻辑性
网友评论