法国诗人 Antoine de Saint Exupery 曾写道,"不是在不能添加更多的时候,而是没有什么可以去掉的时候,才能达到完美。"(参见文献【Sai39】)这个原则同样适用于软件的设计和构建。API是这个规则应该遵循的一个清晰的例子。
书写一个明确的、最小的API 是管理软件系统管理简单性必要的部分。我们向API消费者提供的方法和参数越少,这些API就越容易理解,我们就能用更多的精力去尽可能地完善这些方法。同时,一个反复出现的主题是∶有意识地不解决某些问题可以让我们能够更专注核心问题,使得我们已有的解决方案更好。在软件工程上,少就是多!一个很小的,很简单的 API 通常也是一个对问题深刻理解的标志。
模块化 在 API与单个二进制文件以外,适用于面向对象编程的许多经验法则也适用于分布式系统的设计。对系统中某个部分进行隔离式的变更的能力对创建一个可以运维的系统来说非常必要。具体而言,在二进制文件之间或者二进制文件与配置之间推行松耦合,是一种同时提高开发人员的灵活性和系统的稳定性的简化模式。如果在一个更大系统的某个组件中发现一个错误,我们可以修复这个错误并且独立于系统的其他部分更新该程序。
虽然API提供的模块化可能看上去很容易理解,但是如何将模块化的概念延伸到API的变更就没那么明显了。某个API的一个变更就可以迫使开发人员重建他们的整个系统,同时承担引入新问题的风险。通过将API版本化,可以允许开发人员继续使用它们的系统所依赖的版本,以更安全和深思熟虑的方法升级到新的版本。这样不必要求整个系统的每一次功能增加或改进都需要全面的生产更新,整个系统中的更新节奏可以不同。
随着系统变得越来越复杂,API与二进制文件之间的责任分离变得越来越重要。某种程度上,这与面向对象编程中的类设计类似∶正如普遍认同的,编写一个其中包含无关功能的"大杂烩"类是一个糟糕的实践。构建和发布"util"或"misc"二进制文件同样也是个糟糕的实践。一个设计良好的分布式系统是由一系列合作者组成的,每一个合作者都具有明确的、良好定义的范围。
模块化的概念同样适用于数据格式。Google的Protobuf的一个主要优势和设计目标就是创建一个同时向后和向前兼容的传输格式。
发布的简单化 简单的发布流程总的来说要比复杂的发布流程更好。测量和理解单一变化的影响要比同时应对一系列变化更加容易。如果同时发布100个不相关的系统更改,而系统性能变差了,我们需要花费大量时间和努力来定位哪些改变影响了系统性能、以及它们是怎样影响的。如果发布是按更小的批次进行的,我们就可以更有信心地进行更快的发布,因为每个变更在系统中的影响可以独立理解。这种发布方式跟机器学习中的梯度下降法类似。我们通过每次进展一点,同时考虑每次改变对系统的改善和退化来寻找最佳方案。
这一章反复重申的主题是∶软件的简单性是可靠性的前提条件。当我们考虑如何简化一个给定的任务的每一步时,我们并不是在偷懒。相反,我们是在明确实际上要完成的任务是什么,以及如何更容易地做到。我们对新功能说"不"的时候,不是在限制创新,是在保持环境整洁,以免分心。这样我们可以持续关注创新,并且可以进行真正的工程工作。
|