Google 如何写设计文档

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

作者Malte是Vercel的CTO。在此之前Malte是负责谷歌搜索渲染的首席工程师以及Search on Laptops, Tablets, 和Desktop的工程总监。

译者许晓斌现任阿里巴巴资深技术专家《Maven实战》作者。

本文获得作者和译者授权在技术琐话公开发布。

Google 软件工程文化中关键元素之一是使用设计文档来定义软件设计。这些文档通常不是非常正式主要是软件系统或应用程序的作者在着手写代码之前编写的。这些设计文档记录了 high-level 的实现策略和关键的设计决策而后者重点描述了决策过程中思考的权衡。

作为软件工程师我们的工作并不是生产代码本身而是解决问题。非结构化的文本例如以设计文档的形式在项目早期或许是解决问题更适宜的工具。因为设计文档可能更简明、更容易被理解相比代码能在更高的层面沟通问题及解决方案。

除了用作软件设计的原始文档记录设计文档还在软件研发生命周期中实现了下述功能

●在修改成本还比较低的时候尽早地识别设计缺陷。

●在组织中围绕设计达成共识。

●确保横切关注点Cross-cutting concern得到充分考虑。

●在组织中传播资深工程师的知识。

●就设计决策形成组织记忆基础。

●成为软件设计者技术资产中的一个摘要性制品。

1. 设计文档的构成

设计文档是非正式的文档因此其内容不需要遵循严格的准则。因此首要原则就是在特定的项目中用任何最合理的形式编写。

在此原则之外Google 也建立了一种颇有效果的设计文档结构。

1.1 上下文和范围Context and scope

这一小节给读者展现一个有关这个系统在哪里被构建以及什么会被构建的非常粗的概览。这不是需求文档。保持言简意赅这里的目标是让读者快速进入状态可以假设读者知道一些前置的知识相关详情可以给到链接。这一节内容应该完全关注在客观的背景事实。

1.2 目标和非目标Goals and non-goals

给出一个简单的列表讲述系统的目标是什么。有时候更重要的是讲述非目标是什么。注意非目标不是对目标的否定例如“系统不应该 crash”而是显示地挑选出来的不是目标的内容。一个好的例子是 “遵循 ACID”当设计一个数据库的时候你必然想要知道这是目标还是非目标。进一步的你仍然可以选择一个方案来实现非目标只要它不会给实现目标带来不必要的权衡。

1.3 实际设计The actual design

这一部分应该以一个概述开头然后逐渐展开细节。

8401fc56d6adb659961df8b321f65e30.png

  设计是文档是在你设计软件的过程中记录设计取舍的地方。应该关注这些取舍以产出一个具备长期价值的文档。具体就是在既定的上下文事实目标和非目标需求下设计文档应该提出解决方案并阐明为什么某个特定的解决方案是满足这些目标的最佳方案。

相较于更为正式媒体形式编写文档的意义在于可以用合适的方式灵活地表述手头的问题集合。因此如何描述设计并没有显式的指引。

话虽如此对于大多数设计文档来说一些最佳实践和重复出现的主题还是有意义的

1.3.1 系统上下文图System-context-diagram

对于很多文档来说系统上下文图是很有用的。这类图展示了当前系统是更大技术图景的一部分能让读者在一个他们已经熟悉的上下文环境中去理解新的设计。

aaa47758e3392ffdb91eaee93267a007.png

系统上下文图的例子

1.3.2 APIs

如果设计的系统会暴露 API那么草拟出 API 通常是好想法。不过在大多数情况我们应该克制住把正式接口和数据定义复制粘贴到文档中的冲动因为这么做会导致文档过于冗长包含不必要的细节并很快过期。相对应的我们应当关注和设计及取舍相关的那部分 API。

1.3.3 数据存储Data storage

需要存储系统的系统应该讨论数据是如何以何种形式如何被存储的。和前面描述 API 的建议一样基于相同的理由应该避免复制粘贴完整的 schema 定义正确的做法是关注在那些和设计取舍相关的部分。

1.3.4 代码和伪代码Code and pseudo-code

设计文档应当很少包含代码或伪装代码除非有一些情况需要描述新的算法。合理的做法是给到设计原型实现的链接。

1.3.5 约束条件Degree of constraint

软件设计形态因此设计文档的主要影响因素是解决方案空间solution space中的约束条件。

一个方向的极端情况是 “绿地软件项目greenfield”在这种情况下我们知道所有的目标解决方案只要是合理的没什么限制。这样的文档可能就会显得很宽泛但是也应该快速定义一组规则以便让大家尽快把目光收敛到一组可控的解决方案中。

另一个方向的极端情况是所有可能的解决方案都被定义得很清楚了但是如何把它们结合起来以达成目标却毫不清晰。这往往是因为遗留系统难以改动或者遗留系统不是被设计用来解决当前所面临的问题的又或者是一个类库的设计要求我们在它的宿主编程语言限制下工作。

在这种情况下你或许可以遍历所有可行的简单方法但更需要创新地把所有这些方法整合起来以完成目标。也许存在多种方案每一种方案都不是特别出色的因此文档应该关注在如何从已经识别的各项取舍中选择最合适的方案。

1.4 候选设计Alternatives considered

这一小节列出那些同样可以实现类似产出的可选设计。这里关注的应该是各种方案各自的取舍以及这些取舍的对比如何引向最终的设计 —— 文档的核心主题。

虽然描述候选设计可以简洁一些但是这一小节实际上非常核心的因为这里非常清晰地展示了在给定的项目目标和所有可选方案下为什么选择了最终方案在给定目标下权衡的判断是如何做出的而这正是文档的读者所关注的核心。

1.5 横切关注点Cross-cutting Concerns

在这里组织可以确保一些横切关注点如安全隐私可观测性总是被考虑到。这部分内容通常相对较短只是用来解释设计会如何影响到横切关注点以及相关影响如何得到解决。团队应该标准化在他们场景下的关注点。

例如由于隐私非常重要Google 的项目就必须写一个专门的隐私设计文档这个文档会别专门用来 Review 隐私和安全。虽然 Review 只要在项目启动前完成但通常最好是尽早让隐私和安全团队介入确保设计从一开始就重视他们的意见。关于这部分内容更专门的细节核心文档不一定要全部包含有时候给到这些专门文档的引用即可。

1.6 设计文档的长度The length of a design doc

设计文档应该具备充分的细节但同时足够简短以能够被忙碌的人阅读。对于一个大型项目来说最佳的长度似乎是 10 到 20 页。如果你的内容超过这个大小更合理的做法可能是把这个问题划分成更易管理的子问题。当然需要注意的是编写 1-3 页的“迷你设计文档”完全是可能的。这类文档对于敏捷项目中的增量改进或者子任务尤其有用 —— 但你仍然需要和编写长文档一样执行一样的步骤区别只是让内容更精炼并且只关注有限的问题集合。

2. 什么时候不要编写设计文档When not to write a design doc

编写设计文档是需要成本的。关于是否编写设计文档的决定实际上是在做权衡。权衡的一边是围绕设计、文档、高层评审等工作形成组织共识的益处权衡的另一边是这块工作投入的精力成本。决策的核心在于设计问题的解决方案是否模糊 —— 这往往是因为问题复杂度或者解决方案的复杂度引起的或者两者皆有。如果不存在这个问题那么走一个设计文档编写的流程价值就有限。

设计文档可能没有必要的一个明显征兆是设计文档实际上是实现手册。如果文档基本上说的是“这是我们将如何实现之”而没有深入讨论取舍、可选方案、没有解释决策或者说解决方案太明显了以致于没什么取舍可讨论那么很有可能直接编写真实代码是更好的选择。

最后创建和评审设计文档的投入可能与快速制作原型并迭代理念并不相容。但是大多数软件项目都是有一组实际上已知的问题。拥抱敏捷方法不应该成为不花时间去寻找已知问题正确解决方案的借口。此外原型本身可能就是创建设计文档过程的一部分。“我试过了这么做可行” 就是选择一种设计最好的论据之一。

3. 设计文档的生命周期The design doc lifecycle

一份设计文档的生命周期包括如下阶段

1.创建和快速迭代

2.审查可能有多轮

3.实现和迭代

4.维护和学习

3.1 创建和快速迭代Creation and rapic iteration

你编写文档有时候是和几个合作者一起编写。

这一阶段快速演进成为快速迭代的时期文档被分享给一些同事他们拥有关于问题空间problem space的最多的知识通常属于同一个团队通过他们不断澄清问题并提出建议文档逐渐形成第一个相对稳定的版本。

你会发现很多工程师和团队更偏向于使用版本控制和代码审查工具来管理文档但是 Google 的大多数文档是使用 Google Docs 创建的并重度使用了协作特性。

3.2 评审Review

在评审阶段设计文档被分享给原始作者和紧密协作者之外更广范的一批受众。评审可以给文档增加很多价值但是也有可能是危险的投入成本陷阱因此要明智对待。

评审可以有很多形式最轻量的版本就是简单把文档发给更大范围的团队让大伙有机会可以看一眼。随着而来的讨论主要就发生在文档的评论区。而形式较重的评审就是发起正式的设计文档评审会议在会议中作者面向通常是较为资深的工程师听众演示文档通常是专门的演示。Google 有很多团队为此目的排了周期性的会议工程师可以注册用来发起设计评审。自然的等待此类会议来评审设计文档会大幅降低研发速度。工程师可以通过直接向同事获取最关键的反馈同时也不阻塞更广泛的评审进而降低这一风险。

当 Google 是一个小一些的公司的时候大家习惯上会把设计发到一个中心的邮件列表中在这里资深的工程师会抽空进行评审。这种方式对公司来说可能就不错。这种方式一大好处是它在整个公司层面建立了一种相对一致的软件设计文化。但是随着公司逐渐增长细形成一个较大的工程师团队维护这种中心化的方法就不可行了。

设计评审增加的主要价值是它形成了一个让组织的综合经验可以融合到设计中的机会。如何让设计能够充分考虑横切关注点如可观测性、安全性、以及隐私这一点就能够非常一致地在评审阶段得到保障。评审的主要价值不是问题被发现本身而是让问题在软件研发阶段相对早期的时候也就是修复成本相对较低的时候被发现。

3.3 实现和迭代Implementation and iteration

当事情获得了充分的进展看起来进一步的评审不太会要求设计做重大的改变那就是时候开始实现了。当计划和现实冲突不可避免的会发现设计的缺陷未被充分考虑的需求或者基于经验的推测实际上错误的进而发现需要对设计做修改。在这种情况下强烈推荐更新设计文档一般说来如果系统还没有上线那么很确定应该更新文档。在实践中我们普通人在更新文档方面都做得不好以及因为一些其他的实际因素更新还通常会落到新的独立的文档中去。这就导致了一种近似美国宪法的最终态有一堆修正案而非一份一致的文档。对于将来维护系统的可怜程序员来说当他们像考古学家一样翻阅历史设计文档去试图理解目标系统的时候原始文档中给出这些“修正案”的链接会非常有帮助。

3.4 维护和学习Maintenance and learning

当 Google 工程师首次接触一个系统的时候他们的第一个问题通常是“设计文档在哪里”。虽然说设计文档和所有其他文档一样都会随着时间的流逝变得和现实不一致但它们依旧是学习系统在创建之初其背后思考的最佳起步材料。

作为作者从为自己着想的角度考虑可以在一两年后重新阅读自己的设计文档。你哪里做对了哪里做错了在今天来看你会做哪些不同的决策对于工程师来说回答这些问题是一种绝佳的提升软件设计能力和自我进步的方式。

4. 结论Conclusions

在软件项目中围绕解决最难的问题设计文档是一种获得清晰度以并达成共识的绝佳方法。设计文档可以节省金钱因为在前期足够的调研可以帮助避免过早就进入编码细节却未能完成项目目标设计文档又花费金钱因为编写和审查文档消耗时间。因此在你项目中明智地做出选择。

在考虑是否编写设计文档的时候思考如下问题

●你是否对正确软件设计不确信在前期消耗时间来获取确定性是否合理

●在设计阶段引入相关的资深工程师是否有帮助他们可能没有时间审查所有代码。

●软件设计是否是模糊的甚至是有争议的因此围绕这一问题在组织层面达成共识会很有价值

●我团队是否有时候会在设计中忘记考虑隐私、安全、日志或者其他横切关注点

●在组织中是否非常需要遗留系统设计的文档这样可以让大家在 high-level 快速了解系统。

如果对于上述的问题你有三个或更多“是”的回答那么在你开始下一个软件项目的时候设计文档大概率是个不错的方法。

往期推荐

技术琐话 

以分布式设计、架构、体系思想为基础兼论研发相关的点点滴滴不限于代码、质量体系和研发管理。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: go