系统设计中的简单法则

  • 崔康

2012 年 5 月 6 日

话题:架构设计模式语言 & 开发文化 & 方法

最近,包云岗在自己的博客中总结了系统设计中的基本法则——简单之美,列举了不少经典观点和案例。

他首先总结了麻省理工方法(MIT Approach)和新泽西方法(New Jersey Approach)的异同:

  • 简单性:两种方法都强调设计必须简单,这既是对实现的要求,也是对接口的要求。但是,MIT 方法认为接口的简单要比实现的简单更加重要,而 NJ 方法认为实现的简单要比接口的简单更加重要。
  • 正确性:设计在任何值得注意的方面都要保证正确。但是,MIT 方法认为不正确是绝对不允许的,而 NJ 方法认为,为了简单性,正确性可以做轻微的让步。
  • 一致性:MIT 方法认为,设计必须保持一致兼容。设计可以允许轻微少量的不简单和不完整,来避免不一致。一致性和正确性同等重要。而 NJ 方法认为,设计不能过度不兼容一致。为了简单,一致性可以在某些方面做些牺牲,但与其允许设计中的这些处理不常见情况的部分去增加实现的复杂性和不一致性,不如丢掉它们。
  • 完整性:设计必须覆盖到实际应用的各种重要场景。所有可预料到的情况都必须覆盖到。MIT 方法认为,简单性不能过度的损害完整性。NJ 方法则认为,为了保证其它几种特征的品质,完整性可以作出牺牲。事实上,一旦简单性受到危害,完整性必须做出牺牲。一致性可以为实现的完整性作出牺牲;最不重要的是接口上的一致性。

包云岗还引用了一个案例来证明两种方法的差异:

一位 MIT 的教授一直困恼于 Syscall 处理时间过长出现中断时如何保护用户进程某些状态,从而让用户进程能继续执行。他问新泽西人,Unix 是怎么处理这个问题。新泽西人说,Unix 只支持大多数 Syscall 处理时间较短的情况,如果时间太长出现中断 Syscall 不能完成,那就会返回一个错误码,让用户重新调用 Syscall。但 MIT 人不喜欢这个解决方案,因为这不是“正确的做法”

对于现在的互联网企业,是否还需要遵从这些法则呢?国外有篇博文《Scalability lessons from Google, YouTube, Twitter, Amazon, eBay, Facebook and Instagram》总结了各大公司的经验教训,其中第一条就是“保持简单”。但是包云岗认为,这和 New Jersey Approach 的原因和内涵有所不同。不同于 Unix 时代相对简单的单机系统,互联网时代的大公司的系统往往都是成千上万台机器,在这样的系统上部署、管理服务(软件)是一项非常有挑战的任务。而为大规模用户提供的一项服务往往会涉及到众多模块、若干步骤。此时“简单”就是要求每个阶段、每个步骤、每个子任务尽量采用最简单的解决方案,这是由于大规模系统内在的不确定性导致的复杂性决定的。

无独有偶,包云岗发现硅谷创业之父 Paul Graham 也推崇简单法则,Paul 在《黑客与画家》的“设计者的品味”一章中写到,“好的设计是简单的”、“简单就是美,正如漂亮的数学证明往往是简短而巧妙的那种”。他提到,有些创业者希望第一版就能推出功能齐全的产品,满足所有的用户需求,但这种想法是致命的。在硅谷创业最忌讳的就是“Premature Optimization”。因为一方面用户需求是多样的,不同人群都有不同的需求;另一方面开发者想象的需求往往和真实的用户需求有偏差。所以,Graham 推崇那种有用户参与反馈的迭代优化的方式。

包云岗还与普林斯顿大学计算机系的李凯教授有过简单法则的交流和讨论:

李凯老师的做事方式是——只抓住大方向,其他问题尽量简化。但真正要做到 KISS 原则其实并不容易。我在遇到问题时,往往会从各个方面去考虑问题,其中难免包含了各种细枝末节,这种方式导致问题经常会变得非常复杂。之前讲过这个例子,在移植 TCP/IP 协议栈到用户态时,我觉得有约 10 个功能需要考虑。和李老师讨论,他让我把那些功能分成两类:“必须有 (Must Have)”和“可以有 (Nice-to-Have)”。当我试了这种方法,发现原来 Must-Have 的功能其实也不过 2~3 个而已。而最近的例子则是在要设计一个功能让 TCP/IP 连接的 Server 在模拟器中、Client 在真实机器。我考虑是尽量减少模拟器上 OS 的开销,所以打算采用自己写一个设备、然后让用户态程序 Bypass Kernel 直接访问该设备的方案。但李凯老师在了解 OS 开销以后,认为容忍开销、尽量直接使用模拟器自己带的功能,让开发更简单。

这些教训也让我不断地去思考为什么要用 KISS 原则。慢慢地我体会到,KISS 原则目的其实是——“快速推进、逐步优化”。我们设计一个算法,往往可以在大脑中预先思考好,然后直接编程写出来。但是,我们设计实现一个系统,当系统的复杂度超出我们大脑的工作记忆容量时,就无法在大脑中去“模拟”每一个细节。此时,我们应该用最快的速度去把系统建起了,然后再对各个环节进行优化。

读者朋友对系统设计的简单法则有何看法?欢迎大家发表自己的意见。

架构设计模式语言 & 开发文化 & 方法