《A Philosophy of Software Design》——软件系统的复杂度

《A Philosophy of Software Design》的作者通过衡量软件系统是否容易被理解和修改来定义「复杂度」。

「复杂」表现出三个症状:

  • Change amplification:一个看似简单的修改实际上却需要修改很多代码。

    • 比如说多个网页有着同样的背景色,却各自单独维护,因此想要改变多个页面的背景色就得同时改动多个页面的代码——我们很容易想到,可以通过维护一个共享变量来控制各个页面的背景色,这样只需要修改一次就能满足要求了。

    • 一堆字段重合度非常高的列表展示页,全都各自有一个页面。一次需求让将列表中部分字段脱敏展示,于是只能一个页面一个页面地修改。

  • Cognitive load:认知负荷,指开发人员需要了解多少前提才能完成任务。更高的认知负荷意味着开发人员必须花费更多时间来学习所需的信息,并且存在更大的错误风险。

    • 比方说要修改一个 API 的返回值、业务逻辑,那至少得掌握这个 API 被多少地方调用。比方说同事 A 炫技写了个短却复杂的代码——看代码量是够精简的,可其它同事甚至他自己在后面修改时,都很难搞清楚这些代码在干嘛。假如函数名语义不明确、有副作用……都会增加开发人员认知负担。
  • Unknown unknowns:开发人员不知道自己不知道,他不知道必须修改哪些代码或者掌握哪些信息才能完成任务。

    • 比方说网站使用一个共享变量控制字体颜色,但一些网页却单独控制自己的背景色。因此对于大部分网页来说,更改字体颜色后没啥问题,但对于一些有着特殊背景色的网页,则有可能出现原来的背景色对于现在的字体来说太亮,导致网页没有可读性的情况。而即使开发人员意识到这样的问题,也不清楚具体涉及哪些特殊页面,因此又需要花大量时间排查。

    • 有次在项目 A 新增了一个异常码,而这类异常码会被另一个好久没动过的项目 B 用到,B 使用时需要将 A 的异常码转换成 B 的格式——没人意识到这点,结果问题在需求后期才暴露出来。

造成「复杂」的两个原因:

  • 依赖关系:依赖不可避免,但应该尽量简单明了。

  • 模糊:重要的信息不够明显就会变得模糊。比如变量名取的太通用了,完全搞不清楚它会被用于啥;(想象一个叫 data 的变量名)一个变量的文档没有说明单位,因此我们只能扫描代码看看使用这个变量的地方是怎么用的;模糊性经常和依赖关系相关,即依赖关系没那么明显——比如新增的错误码需要维护一个字符串,这个字符串对开发人员来说就没那么明显。

    • 遇到过接口文档的一个金额字段的单位是「元」,而建表用的「分」。

书中举的例子都太他妈真实了。