软件架构最佳实践、企业架构模式以及系统描述的正式方法都是非常重要且实用的工具,总会有合适的场景让它们发挥作用。但在设计系统时,请从简单始、以简单终,尽可能避免一切会无谓提高复杂度的架构与正式工具。
我的职责是设计和构建大型系统。我参与重写了 Uber 的 分布式支付系统 ,设计并交付了Skype on Xbox One,开源了Uber 的移动架构框架 RIBs 。所有这些系统都进行了彻底的设计,经过多次迭代和大量讨论。然后,这些设计被记录到设计文档中,在我们开始构建之前分发出去,从而获得更多的反馈。
所有这些系统的规模都很大:有数百名开发人员在构建它们——或者以它们为基础进行构建——并且它们支撑着每天数百万人使用的系统。它们不仅仅是绿地项目。重写的支付系统就是用于替换两个已有的支付系统,有几十个系统、数十个团队在使用它们,但所有这些都没有对业务产生任何影响。重写 Uber App 是一个由数百名工程师同时参与的项目,他们将现有的功能移植到一个新的架构中。
让我先说些可能会让你觉得吃惊的事。 首先,这些设计都没有使用任何标准的软件架构规划工具 。我们没有使用 UML ,没有使用 4+1 模型 ,没有使用 ADR ,也没有使用 C4 和 依赖关系图。我们创建了大量的图表,但是没有遵循任何严格的规则。只是使用了普通的方框和箭头,类似于这个 描述信息流的图 或这个 概括类结构和组件之间关系的图 。同一个设计文档中的两个图经常会有不同的布局,并且经常由不同的工程师添加和修改。
其次,负责设计的团队中没有架构师。没有IT 架构师或企业架构师 。没错,Uber 和 Skype/ 微软都没有袖手旁观的软件架构师职位。级别较高的工程师和普通工程师一样,仍然需要定期编写代码。对于所有的项目,我们都有经验丰富的工程师参与。然而,没有人专门负责架构或设计。经验丰富的开发人员确实推动了设计过程。不过,即使是最初级的成员也在设计过程中进行了输入,他们经常会挑战决策,并提供其他可供讨论的替代方案。
第三,我们实际上没有引入常见的架构模式以及常见的软件架构文献中引用的其他术语,比如Martin Fowler 的架构指南 。没有提到微服务、无服务器架构、应用程序边界、事件驱动架构等。其中一些在头脑风暴时确实提到过,但是,没有必要在设计文档中引用它们。
科技公司和初创企业的软件设计
那么,我们是如何完成任务的呢?为什么我们不遵循著名的软件架构方法所建议的方法呢?
我曾与其他科技公司(Facebook、亚马逊、Netflix、谷歌)以及规模较小的初创公司的同行们讨论过这个问题。大多数团队和项目无论大小,都采用类似的设计和实现方法:
-
从业务问题入手。我们要解决什么问题?我们要设法构建什么产品?为什么?我们如何度量成功?
-
通过头脑风暴找出方法。与团队一起,通过多次会议,找出可行的解决方案。务必保证这些头脑风暴的规模要小。从高层开始,逐步下降到较低的层次上。
-
借助白板说明方法。把团队聚集在一起,让一个人绘制出团队趋向于采用的方法。你应该能够在白板上清楚地解释你的系统 / 应用程序的架构,从高层开始,根据需要逐步深入。如果你觉得解释有困难或者不够清楚,就需要在细节上做更多的工作。
-
根据你在白板上的解释, 使用简洁的文档和图表详细描述设计。 尽量少用术语:要让初级工程师也明白它的意思。 用清晰易懂的语言写下来 。在 Uber,我们使用一个带有基本模板的类似 RFC 的文档。
-
讨论权衡可选方案。良好的软件设计和架构重点在于做出正确的权衡。设计选择本身没有好坏之分:它完全取决于上下文和目标。架构是否要划分为不同的微服务?说明一下为什么你决定不使用单体架构,它可能有其他一些好处,比如部署更简单更快速。你是否选择使用新功能扩展服务或模块?权衡构建单个服务或模块的选项,以及这种方法的优缺点。
-
在团队 / 组织内分发设计文档并获得反馈。在 Uber,我们曾经把所有的软件设计文档发给所有的工程师,直到我们的工程师人数达到大约 2000 名。现在我们的人数更多了,我们仍然广泛地分发设计文档,但是我们已经开始更多地平衡信噪比。我们鼓励人们提出问题并提供替代方案。要根据实际情况设定合理的反馈时间限制,讨论反馈意见,并根据需要加以采纳。简单明了的反馈可以现场快速处理,而更复杂的反馈现场处理也可能会更快。
为什么我们的方法与软件架构文献中经常提到的方法不同?实际上,我们的方法与大多数架构指南在原则上并没有太大的不同。几乎所有的指南都建议从业务问题开始,概述解决方案并进行权衡:我们也是这样做的。我们没有使用许多架构师或架构书籍提倡的更复杂的工具。我们使用最直接的工具(如谷歌 Docs 或 Office365)尽可能简单地记录设计。
我认为,在这些公司中,我们的方法的主要区别在于工程文化。自主性高和层级少是科技公司和初创企业的共同特点:对于更传统的公司来说,有时并非如此。这也是为什么这些地方使用更严格的规则进行更多的“基于常识的设计”,而不是流程驱动的设计。
我知道一些银行和汽车公司,它们的开发人员在没有得到高层架构师的批准之前,不愿做出任何架构决策,而这些架构师负责管理多个团队。这会是一个比较缓慢的过程,架构师可能会被许多请求淹没。因此,这些架构师创建了更正式的文档,希望通过更多地使用常见文献中介绍的工具使系统更清晰。这些文档还强化了自上向下的方法,因为对于非架构师工程师来说,质疑或挑战已经用正式方法记录下来的、他们不是很熟悉的决策更令人生畏。所以他们通常不会这么做。公平地说,这些公司常常希望使开发人员成为更容易替换的资源,以便他们可以在短时间内重新分配人员从事不同的项目。不同的工具在不同的环境中可以更好地发挥作用,这应该不足为奇。
简单、无术语的软件设计胜过架构模式
系统设计的目标应该是简单。系统越简单,理解起来就越容易,就越容易发现问题,实现起来也就越简单。使用的描述语言越清晰,设计就越容易理解。避免使用团队中每个成员都不理解的术语:即时是经验最少的人也应该能够同样清楚地理解设计。
简洁的设计类似于简洁的代码:易于阅读和理解。有许多好方法可以编写出简洁的代码。然而,你很少听到有人一开始就建议将设计模式应用到你的代码中。简洁的代码从单一职责、清晰的命名和易于理解的约定开始。这些原则同样适用于简洁的架构。
那么,架构模式的作用是什么?我认为它们与编码设计模式同样有用。它们可以为你提供关于如何改进代码或架构的思路。对于编码模式,当我看到一个 单例模式 时,我就会注意到它;当我看到一个类仅充当执行调用的 外观 时,我就会扬起眉毛,深入研究。但我还没有想到“ 这需要一个抽象工厂模式 ”。事实上,在处理了大量依赖注入之后,我花了很多时间来理解这个模式的作用,并且终于在某个时刻恍然大悟——这实际上是这个模式 非常常见并且非常有用 的少数几个领域之一。我还得承认,虽然我花了很多时间阅读和理解 GoF 设计模式,但与我从其他工程师那里得到的代码反馈相比,它们对于我成为一名更好的程序员的影响要小得多。
类似地,了解常见的架构模式是一件好事:它有助于缩短与他人讨论的时间,他们和你一样理解那些模式。但是架构模式并不是我们的目标,它们也不能代替更简单的系统设计。在设计系统时,你可能会无意中应用了一个众所周知的模式:这是一件好事。后续你要参照这个方法就更容易了。但是,你最不希望看到的是使用一个或多个架构模式,把它当成一把锤子,为了用它而到处找钉子。
工程师们观察到,人们在某些情况下会做出类似的设计选择,并且这些选择的实现方式也非常类似,于是,就诞生了架构模式。然后,这些选择被命名、记录,并被广泛地讨论。架构模式是在制定解决方案后出现的工具,目的是简化工程师的工作。 作为一名工程师,你的目标应该更多地是制定解决方案并从中学习,而不是选择一个闪亮的架构模式,希望它能解决你的问题。
广州VI设计公司https://www.houdianzi.com
如何更好地设计系统?
我听到许多人询问如何更好地架构和设计系统。一些有经验的人会推荐你阅读架构模式和软件架构方面的书籍。我肯定也推荐阅读——尤其是书籍,因为它们比一篇短文更有深度——但我还是有一些建议,这些建议比单纯的阅读更具实践性。
-
拉过一名同事,在白板上写下你的设计方法。写出你在做什么,为什么要这么做。确保他们能理解。当他们能理解的时候,征求他们的意见。
-
将你的设计详细记录在一个简单的文档中,并与你的团队分享,寻求反馈。不管你正在处理的事情多么简单或复杂,可能是较小的重构,也可能是大型项目,总结一下。采用一种对你而言有意义的方式,一种别人能理解的方式。为了帮你寻找灵感,我在这里分享了我在 Uber 看到的做法。与团队共享时采用一种可以评论的格式,如谷歌文档、Office365 等。让人们添加他们的想法和问题。
-
设计两种方案,并进行对比。大多数人在设计架构时都只采用一种方法,就是突然出现在他们脑海中的方法。然而,架构并不是非黑即白的。提出第二个同样可行的设计。对比两者,解释为什么一个比另一个更好。简要列出第二种设计作为备选方案,并说明为什么决定不用它。
-
要清楚你所做的权衡,为什么要做这些权衡,以及你优化了哪些东西。要清楚存在的约束,并且必须考虑到这些约束。
-
评审别人的设计,并取长补短做到更好。假设你们有这样一种文化,人们通过白板、会议或文档来分享他们的设计,那么你就能从评审意见中了解到更多的东西。在评审过程中,大多数人只是试着接受一些东西,成为单向的观察者。我们不能局限于此。相反,对于不清楚的部分要通过提问来弄清楚。询问他们考虑过的其他选项。问他们做了哪些权衡,假设了哪些约束条件。要唱反调,提出另一个可能更简单的选项——即使不是更好的选项——问问他们对你的建议有何看法。即使你没有像设计展示者那样思考得那么深入,你仍然可以带来很多有价值的东西并学到更多的东西。
最好的软件设计是简单易懂的设计。下次启动一个新项目时,不要想着“我将如何构建这个系统,我应该使用哪些经过实战检验的模式,以及我应该使用哪些形式化的方法来记录它?”,而是要想一想“我怎么才能想出一种任何人都很容易理解的、最简单的设计?”