《人月神话》里说过,软件工程,没有银弹。软件的复杂性是不会凭空消失的,但是任由复杂性野蛮生长,就会不知不觉陷入复杂性焦油坑,越挣扎陷得越深。

尽管复杂性不会消失,但是仍然是可以管理好的。我见过很多团队不遵守好的实践,最后被折磨得焦头烂额的反例;也在不同团队中,通过应用以下好的实践和心法成功避开焦油坑甚至力挽狂澜,一次次验证了好的过程一定会产生好的结果这个事实。

工程实践:测试驱动开发


不要再找借口说这个有多难,你以为你写完代码扔给测试,靠祈祷就能不出问题吗?你得使用测试驱动开发,证明不会出问题,才会有信心拍胸脯说不会有问题。

测试过程应该不断提前,直到提到开发行为的前面,以终为始。不这样做,开始很轻松,甚至很随意,不主动用测试驱动开发,就会在后面被各种 BUG 驱动,各种被动。

我最近几年,如果是创建新项目,一定会先将测试框架和 CI/CD 流水线搭好;如果看到已有项目缺少这些,也一定会先补上。事实证明,磨刀不误砍柴功。

心法:结构化思考


这其实是小学数学时就学到的方法,分类讨论时要做到不重叠、不遗漏。非常浅显的道理,不知道为什么会被麦肯锡包装成所谓的 MECE 法则。

在某个团队见过一段冗长的代码,各种 if/else 嵌套,里面的条件就没有做到 MECE,重叠的还好,居然有遗漏的分支!这些被遗漏的逻辑分支就没有机会 callback,这就是当时收到用户投诉是不是页面会卡死没有反应的原因(点了按钮,ajax 请求一直挂起得不到响应)。

当时我拉了测试一起,画了个矩阵列举所有可能的逻辑组合,理清后针对每一种逻辑分支写了自动化测试用例,跑下来就能重现卡死的 BUG。将 if/else 确实的逻辑分支不上,就修复了。当然,后来进一步重构,消除重叠路径,精简代码,最后使用策略模式,消除 if/else,这都得益于有 MECE 的测试用例跑着,让我可以放心重构代码。当然重构后的代码并没有彻底消除复杂性,但是可读性提升了,良好的代码组织结构而不再是一大坨。由于更有序,使得复杂性没有野蛮生长。

总结


其实,没有什么威力强大的银弹,但是却有简单又行之有效的方法。TDD 的开发循环很简单,只需要每次都使用即可。结构化思考小学生都会,由于总被忽视,以至于被麦肯锡包装卖钱去了。

记住大道至简,记住慢就是快