持续交付2.0

设计文档的可读性(3):设计文档的最佳实践(附模版)

Image

本系列文章将分为三篇介绍设计文档的可读性:

1、为什么要写设计文档并强调其可读性

2、设计文档的核心原则:写作三要素和四个要点

3、设计文档的最佳实践


如果你写不清楚,你可能没有想象中那么聪明。

-Kurt Vonnegut Jr

1

遣词

用词要简练、准确、直白。 

1)正确使用专业术语。 

  • 合理地使用常见术语可以降低沟通成本。 

  • 不要过多使用过于小众或自创的术语。如果有必要,需要在文中。 

  • 必要时提供对照的英文术语以方便理解。 

  • 避免无上下文的缩略词。 

2)省略程度副词 

  • 不管作者意图为何, “非常重要” 和 “重要” 在读者看来大同小异。 

3)使用数据 

  • 与其说明“该系统的性能提升明显”,不如“该系统的性能提升了42%”更为可信,也更方便读者做出自己的判断。 

4)忌写八股文 

  • 例如上文,应改为“不要使用过于生僻的词汇,不要过度使用书面语” 

  • 千万不要写文言文

2

造句

1)使用短句,不要使用多从句的复杂句式。 

  • 写文档也不是为了炫耀自己可以驾驭长难句。如下所示

“系统形式问题就是下面这样一个问题:怎样把各种不同的对象种类安排在一个系统中,以使较高的对象种类总能从较低的对象种类构造出来,也就是说前者可还原为后者。为了解决这个问题,我们必须从其相互可还原性来研究各种不同的对象种类。为此目的,我们要根据所涉及的对象领域的实际科学知识为每一个要考察的对象寻找其基本事实存在的充分而必要的条件的各种可能性。对此我们可采取下面的办法来进行,即要求这门实际科学给出基本事实的一个(确实而常在的)表征。”

——Excerpt From: 【德】鲁道夫·卡尔纳普. “世界的逻辑构造。”

2)简单表达,去掉无意义的修饰,去掉试图缓和语气的从句。 

反例:“我们可以看到, NewDB 在一定程度上可以满足我们对事务支持的需求。”

——>修改后:“NewDB 支持事务”。 

反例:“MR 提交信息作为读者查阅修改历史时第一时间看到的信息,其重要性不言而喻。” 

——>修改后:“读者查阅修改历史时会首先关注 MR 提交信息。” 

本段讨论另一个问题,即…

反例:【是, 大臣】汉弗莱教你如何把Yes扩写成一段话(https://www.bilibili.com/video/BV13L411T7jR) 

3)语气要冷静。避免过于口语化。 

  • 不要加顺口溜 

  • 不要使用语气词 

  • 不要使用叹号!如果希望强调,使用粗体或者斜体!也可以使用分级标题!

4)描述要准确 

  • 描述客观事实,避免加入主观情绪。

3

段落

段落应该尽量短。通常,一个段落不要超过 8 个完整的句子。 

每个段落有且仅有一个清晰的主题。每个段落开头应该是主题句,方便读者快速了解段落大意。段落中的每一句话应该与主题紧密相关;否则,它应该另起段落,或者应该删掉。 

注意段落的流动。段落句子应该始于一个读者已经熟悉的概念,将新的内容放在句子结尾。这样,读者可以更连贯地理解。

使用列表

使用 Bullet point 标明无顺序的列表,使用数字序号明确前后顺序。 

如何正确使用列表不在本文详细展开,会在后续文章介绍(如果有后续的话),也可参见文末的参考文献。 

4

结构

1)使用模板 

使用模板可以作为思考辅助,同时也提供了相对较完整且规范的结构。 

文末提供了一份设计文档模板以供参考。 

2)使用图表 

一图胜千言。合理地使用图表可以极大地降低用户的理解成本。 

图表的制作不在本文详细展开,会在后续文章介绍(如果有后续的话),也可参见文末的参考文献。 

3)使用标题 

  • 标题要分级 

  • 标题要简短清晰

Image

(图片来源于网络)

5

篇幅

设计文档不要过长。太多内容堆积在一个文档中会让读者丧失兴趣。 

对于一个大型项目来说,10页(~5000字)左右是一个合适的长度。当超过这个长度时,可以考虑将问题拆分成子问题分别编写设计文档,并在总体设计文档中链接子设计文档。 

对于小问题做增量的改进,考虑使用单页文档(one-pager)。通常这类文档的范围较小,解决问题较简单,目标用户群体仅限于对问题已经有充分了解的内部成员。这时,可以省略背景等内容,而仅使用 目标 -- 方案 两段式论证的结构。

6

排版

使用统一的字体。用户不会意识到不同的字体代表不同的含义,只会感受到混乱。 

微软雅黑是安全选择。

不要使用不同颜色来区分内容。不要在文中使用超过三种颜色。可以在标题及分级标题使用标志性的颜色,同时正文使用黑色。

7

附录:Design Doc Template

设计文档没有定式。即使如此,笔者在此提供一个可供新手参考的设计文档模版,您可以使用此文档模板作为思考的基础。通常,无须事无巨细地填写每一部分,不相关的内容直接略过即可。 

7.1 目标 

“我们要解决什么问题?”

用几句话说明该设计文档的关键目的,让读者能够一眼得知自己是否对该设计文档感兴趣。 

如:“本文描述 Spanner 的顶层设计" 

继而,使用 Bullet Points 描述该设计试图达到的重要目标,如: 

  • 可扩展性 

  • 多版本 

  • 全球分布 

  • 同步复制

非目标也可能很重要。 

非目标并非单纯目标的否定形式,也不是与解决问题无关的其它目标,而是一些可能是读者非预期的、本可作为目标但并没有的目标,如: 

  • 高可用性 

  • 高可靠性 

如果可能,解释是基于哪些方面的考虑将之作为非目标。如: 

  • 可维护性:本服务只是过渡方案,预计寿命三个月,待 XX 上线运行后即可下线

设计不是试图达到完美,而是试图达到平衡。显式地声明哪些是目标,哪些是非目标,有助于帮助读者理解下文中设计决策的合理性,同时也有助于日后迭代设计时,检查最初的假设是否仍然成立。

7.2 背景 

“我们为什么要解决这个问题?” 

为设计文档的目标读者提供理解详细设计所需的背景信息。 

按读者范围来提供背景。见上文关于目标读者的圈定。

设计文档应该是“自包含的”(self-contained),即应该为读者提供足够的背景知识,使其无需进一步的查阅资料即可理解后文的设计。 

保持简洁,通常以几段为宜,每段简要介绍即可。如果需要向读者提供进一步的信息,最好只提供链接。

警惕知识的诅咒。知识的诅咒(Curse of knowledge)是一种认知偏差(https://zh.wikipedia.org/wiki/%E8%AA%8D%E7%9F%A5%E5%81%8F%E5%B7%AE),指人在与他人交流的时候,下意识地假设对方拥有理解交流主题所需要的背景知识。

背景通常可以包括: 

  • 需求动机以及可能的例子。如,“(mRPC) 微服务模式正在公司内变得流行,但是缺少一个通用的、封装了常用内部工具及服务接口的微服务框架”。 

  • 这是放置需求文档的链接的好地方。 

  • 此前的版本以及它们的问题。如,“(tRPC) Taf 是之前的应用框架, 有以下特点,…………, 但是有以下局限性及历史遗留问题”。 

  • 其它已有方案, 如公司内其它方案或开源方案, "mRPC vs. gRPC vs. Stubby" 

  • 相关的项目,如 "mRPC 框架中可能会对接的公司其它系统"

不要在背景中写你的设计,或对问题的解决思路。 

7.3 总体设计 

“我们如何解决这个问题?” 

用一页描述高层设计。 

说明系统的主要组成部分,以及一些关键设计决策。应该说明该系统的模块和决策如何满足前文所列出的目标。 

本设计文档的评审人应该能够根据该总体设计理解你的设计思路并做出评价。描述应该对一个新加入的、不在该项目工作的腾讯工程师而言是可以理解的。

推荐使用系统关系图(https://zh.wikipedia.org/wiki/%E7%B3%BB%E7%BB%9F%E5%85%B3%E7%B3%BB%E5%9B%BE)描述设计。它可以使读者清晰地了解文中的新系统和已经熟悉的系统间的关系。它也可以包含新系统内部概要的组成模块。 

注意:不要只放一个图而不做任何说明,请根据上面小节的要求用文字描述设计思想。 

不要在这里描述细节,放在下一章节中;不要在这里描述背景,放在上一章节中。

7.4 详细设计 

在这一节中,除了介绍设计方案的细节,还应该包括在产生最终方案过程中,主要的设计思想及权衡(tradeoff)。这一节的结构和内容因设计对象(系统,API,流程等)的不同可以自由决定,可以划分一些小节来更好地组织内容,尽可能以简洁明了的结构阐明整个设计。

不要过多写实现细节。就像我们不推荐添加只是为了说明代码做了什么的注释,我们也不推荐在设计文档中只说明你具体要怎么实现该系统。否则,为什么不直接实现呢? 

以下内容可能是实现细节例子,不适合在设计文档中讨论: 

  • API 的所有细节 

  • 存储系统的 Data Schema 

  • 具体代码或伪代码 

  • 该系统各模块代码的存放位置、各模块代码的布局 

  • 该系统使用的编译器版本 

  • 开发规范

通常可以包含以下内容(注意,小节的命名可以更改为更清晰体现内容的标题): 

1)各子模块的设计 

阐明一些复杂模块内部的细节,可以包含一些模块图、流程图来帮助读者理解。可以借助时序图进行展现,如一次调用在各子模块中的运行过程。 

每个子模块需要说明自己存在的意义。如无必要,勿添模块。 

如果没有特殊情况(例如该设计文档是为了描述并实现一个核心算法),不要在系统设计加入代码或者伪代码。

2)API接口 

如果设计的系统会暴露 API 接口,那么简要地描述一下API会帮助读者理解系统的边界。

避免将整个接口复制粘贴到文档中,因为在特定编程语言中的接口通常包含一些语言细节而显得冗长,并且有一些细节也会很快变化。着重表现API接口跟设计最相关的主要部分即可。

3)存储 

介绍系统依赖的存储设计。该部分内容应该回答以下问题,如果答案并非显而易见: 

该系统对数据/存储有哪些要求? 

  • 该系统会如何使用数据? 

  • 数据是什么类型的? 

  • 数据规模有多大? 

  • 读写比是多少?读写频率有多高? 

  • 对可扩展性是否有要求? 

  • 对原子性要求是什么? 

  • 对一致性要求是什么?是否需要支持事务? 

  • 对可用性要求是什么? 

  • 对性能的要求是什么? 

  • ………… 

基于上面的事实,数据库应该如何选型? 

  • 选用关系型数据库还是非关系型数据库?是否有合适的中间件可以使用? 

  • 如何分片?是否需要分库分表?是否需要副本? 

  • 是否需要异地容灾? 

  • 是否需要冷热分离? 

  • …………

数据的抽象以及数据间关系的描述至关重要。可以借助ER( https://zh.wikipedia.org/wiki/ER%E6%A8%A1%E5%9E%8B)、图(https://zh.wikipedia.org/wiki/ER%E6%A8%A1%E5%9E%8Bhttps://zh.wikipedia.org/wiki/ER%E6%A8%A1%E5%9E%8B)(Entity Relationshiop) 的方式展现数据关系。

回答上述问题时,尽可能提供数据,将数据作为答案或作为辅助。不要回答“数据规模很大,读写频繁”,而是回答“预计数据规模为 300T, 3M 日读出, 0.3M 日写入, 巅峰 QPS 为 300”。这样才能为下一步的具体数据库造型提供详细的决策依据,并让读者信服。 

注意:在选型时也应包括可能会造成显著影响的非技术因素,如费用。

避免将所有数据定义(data schema)复制粘贴到文档中,因为 data schema 更偏实现细节。

7.5 其他方案

“我们为什么不这么解决这个问题?”

在介绍了最终方案后,可以有一节介绍一下设计过程中考虑过的其他设计方案(Alternatives Considered)、它们各自的优缺点和权衡点、以及导致选择最终方案的原因等。通常,有经验的读者(尤其是方案的审阅者)会很自然地想到一些其他设计方案,如果这里的介绍描述了没有选择这些方案的原因,就避免读者带着疑问看完整个设计再来询问作者。这一节可以体现设计的严谨性和全面性。

7.6 交叉关注点

1)基础设施 

如果基础设施的选用需要特殊考量,则应该列出。如果该系统的实现需要对基础设施进行增强或变更,也应该在此讨论。

2)可扩展性 

你的系统如何扩展?横向扩展还是纵向扩展?注意数据存储量和流量都可能会需要扩展。

3)安全 & 隐私 

安全性通常需要在设计初期做设计。不同于其它部分是可选的,安全部分往往是必需的。即使你的系统不需要考虑安全和隐私,也需要显式地在本章说明为何是不必要的。

安全性如何保证? 

系统如何授权、鉴权和审计(Authorization, Authentication and Auditing, AAA)?是否需要破窗(break-glass)机制?有哪些已知漏洞和潜在的不安全依赖关系?是否应该与专业安全团队讨论安全性设计评审?…… 

4)数据完整性 

如何保证数据完整性(Data Integrity)? 

如何发现存储数据的损坏或丢失?如何恢复?由数据库保证即可,还是需要额外的安全措施?为了数据完整性,需要对稳定性、性能、可复用性、可维护性造成哪些影响?

5)延迟 

声明延迟的预期目标。描述预期延迟可能造成的影响,以及相关的应对措施。

6)冗余 & 可靠性 

是否需要容灾?是否需要过载保护、有损降级、接口熔断、轻重分离? 

是否需要备份?备份策略是什么?如何修复?在数据丢失和恢复之间会发生什么? 

7)稳定性 

SLA 目标是什么?如果监控?如何保证? 功能开发设计文档模版·稳定性设计清单(https://t2doc.woa.com/t2doc/templates/DEVSPEC.html#%E7%A8%B3%E5%AE%9A%E6%80%A7%E8%AE%BE%E8%AE%A1%E6%B8%85%E5%8D%95checklist)包含了更加详尽的清单,可供参考。

8)外部依赖 

你的外部依赖的可靠性(如 SLA)如何?会对你的系统的可靠性造成何种影响?

如果你的外部依赖不可用,会对你的系统造成何种影响?

除了服务级的依赖外,不要忘记一些隐含的依赖,如 DNS 服务、时间协议服务、运行集群等。

7.7 实现计划 

描述时间及人力安排(如里程碑)。这利于相关人员了解预期,调整工作计划。

7.8 未来计划 

未来可能的计划会方便读者更好地理解该设计以及其定位。 

我们确实应该把设计限定在当前问题,但是该设计可能是更高层系统所要解决问题的一部分,或者只是阶段性方案。读者可能会对方案的完整性有所疑问,会质疑到底问题是否得到完整解决,甚至会质疑该问题在更高层的系统中是否确实值得解决。“背景(过去)-- 当前方案 -- 未来计划” 三者的结合会为读者提供更好的全景图。

8

附录:参考文献

8.1 参考文档 

技术写作 

  • https://developers.google.com/tech-writing 

  • https://docs.microsoft.com/en-us/style-guide/welcome/

8.2 参考书籍 

写作/表达 

  • Style:https://book.douban.com/subject/20062380/, Joseph M. Williams / Joseph Bizup

  • 金字塔原理:思考、协作和解决问题的逻辑:https://book.douban.com/subject/1020644/, 巴巴拉·明托 

  • 写作这回事:https://book.douban.com/subject/3888123/,斯蒂芬·金 

  • The elements of style:https://book.douban.com/subject/1433835/ William Strunk Jr. / E. B. White

Coders at work:https://book.douban.com/subject/3673223/,Peter Siebel 采访 Joshua Block 原文: 

Joshua Block: "Another is Elements of Style, which isn’t even a programming book. You should read it for two reasons: The first is that a large part of every software engineer’s job is writing prose. If you can’t write precise, coherent, readable specs, nobody is going to be able to use your stuff. So anything that improves your prose style is good. The second reason is that most of the ideas in that book are also applicable to programs." 

  • 精准表达:https://book.douban.com/subject/30256364/,高田贵久 

  • 写作提高一点点:https://book.douban.com/subject/30331613/,Mary-Kate Mackey

  • On writing well:https://book.douban.com/subject/4740002/, William Zinsser

  • Artful Sentences:https://book.douban.com/subject/2350137/, Virginia Tufte

技术写作/文献写作 

  • 写作是门手艺:https://book.douban.com/subject/35143751/,刘军强 

  • How to write a lot:https://book.douban.com/subject/2486955/, Paul J. Silvia

  • Writing for computer science:https://book.douban.com/subject/26686729/, Justin Zobel

  • The craft of research:https://book.douban.com/subject/4035330/, Wayne C. Booth / Gregory G. Colomb / Joseph M. Williams

  • 会读才会写:https://book.douban.com/subject/26655043/, Phillip C. Shon

  • Writing Science:https://book.douban.com/subject/10567201/, Joshua Schimel

故事 

前文强调了要讲故事。以下书目阐述了何谓故事、为什么要讲故事及如何讲故事: 

  • Writing Science:https://book.douban.com/subject/10567201/, Joshua Schimel 

  • 金字塔原理:思考、协作和解决问题的逻辑:https://book.douban.com/subject/1020644/, 巴巴拉·明托 

  • 故事:https://book.douban.com/subject/25976544/,Robert McKee

  • 如何阅读一本文学书:https://book.douban.com/subject/26676528/, 托马斯·福斯特

架构设计 

  • Software Engineering at Google:https://book.douban.com/subject/34875994/, Titus Winters / Tom Manshreck / Hyrum K. Wright

  • Build Secure and Realiable System:https://book.douban.com/subject/34796016/, Heather Adkins / Betsy Beyer / Paul Blankinship / Piotr Lewandowski / Ana Oprea / Adam Stubblefield

  • The Design of Design:https://book.douban.com/subject/4046371/, Fredrick P. Brooks Jr.

  • Fundamentals of Software Architecture:https://www.goodreads.com/en/book/show/44144493-fundamentals-of-software-architecture, Mark Richards / Neal Ford

图表 

  • Storytelling with data:https://book.douban.com/subject/26663704/,Cole Nussbaumer Knaflic

列表 

  • https://developers.google.com/tech-writing/one/lists-and-tables

Image

乔梁老师开课啦~视频课程《持续部署训练营(Python版)》, 限时特价!

你的软件开发效率够高吗?质量够好吗?你的团队多长时间才能向用户实时推送一个生产变更?你的软件在发布时,你是否因担心软件交付质量而感到压力倍增?你是否遇到过部署失败,甚至导致停机的情况?持续部署可以帮助你消除软件交付的痛苦,让你能专注于为客户高价值的需求,而不会因为这些交付执行类问题而花太多精力。本课程通过学练结合,理论结合实战,让你体验如何使用构建-测试-部署管道,进行持续部署。并练习如何在有效监控部署的同时,逐步发布功能特性,并作数据库结构变化。

Image

(扫码订阅)

Image