软件维护复杂度的因素
一个简单的修改需要在多处更新
简单修改涉及多处更改也是常见的软件维护复杂度因素,而且主要影响的是我们的认知负荷:维护修改代码时需要花费大量的精力确保各处需要修改的地方都被照顾到了。
最简单的情形是代码当中有重复的“常数”,为了修改这个常数,我们需要多处修改代码。程序员也知道如何解决这一问题,例如通过定义个 constant 并处处引用避免 magic number。再例如网页的风格/色彩,每个页面相同配置都重复设置同样的色彩和风格是一种模式,而采用 css 模版则是更加易于维护的架构。这在架构原则中对应了数据归一化原则(Data normalization)。
稍微复杂一些的是类似的逻辑/或者功能被 copy-paste 多次,原因往往是不同的地方需要稍微不同的使用方式,而过去的维护者没有及时 refactor 代码提取公共逻辑(这样做往往需要更多的时间精力),而是省时间情况下选择了 copy-paste。这就是常说的 Don't repeat yourself 原则。
没有给命名足够的重视
软件中的 API、方法、变量的命名,对于理解代码的逻辑、范围非常重要,也是设计者清晰传达意图的关键。然而,在很多的项目里我们没有给命名足够的重视。
我们的代码一般会和一些项目关联,但是需要注意的是项目是抽象的,而代码是具体的。项目或者产品可以随意一些命名,如PhotoShop可以说PS,这些都没有问题。而代码中的 API、变量、方法不能这样命名。
不知道一个简单特性需要在哪些做修改,即 unknown unknowns
代码缺乏充分的测试覆盖,一些重要场景依赖维护者手工测试;代码有隐藏 / 不易被发现的行为或者边界条件,与文档和接口描述并不符合。
对于维护者来说,改动这样的代码(或者是改动影响到了这样代码 / 被这样代码影响到了)时,如果按照接口描述或者文档进行,没发现隐藏行为,同时代码又缺乏足够测试覆盖,那么就存在未知的风险 unknown unknowns。这时出现问题是很难避免的。最好的方式还是要尽量避免我们的系统质量劣化到这个程度。
上线时,我们最大的噩梦就是 unknown unknowns:这类风险,我们无法预知在哪里或者是否有问题,只能在软件上线后遇到问题才有可能发现。其他的问题 尚可通过努力来解决(认知成本),而 unknown unknowns 可以说已经超出了认知成本的范围。我们最希望避免的也是 unknown unknowns。