软件工程的第一性原理
第一性原理是?
曾几何时,在阅读vibe coding相关的一篇文章中发现,有人在AGENTS.md中为了减少Codex的GPT系列模型减少兜底代码的反复生成,对大模型说:“我需要你写的代码fail-fast,减少无意义兜底,……遵从第一性原理”(记不太清了)。虽然可能记的不太清,但是大致意思如此。
读了4年软件工程,卧槽,什么是第一性原理都没听过。我好奇的去问问哈基米,哈基米说:
“第一性原理”(First Principles Thinking)是一种回归事物最基本的条件,将其拆解到不能再拆解的基础真理,然后以此为基石重新推导的思维方式。
额,很有道理。那这样说,软件工程的第一性原理是什么呢?
谈到软件工程领域
我们第一个想到的一定是那句著名的
没有银弹
作为最具声誉的软件工程概念,这个论点在提出至今仍未被证伪——至少笔者是这样想的。银弹被定义为一种可以数量级优势消减软件开发本质复杂度的一个事物。现在有很多人说,AI会成为这颗银弹,但笔者不这样想。
软件开发中的复杂性分为本质复杂性和偶发复杂性,我们使用的各种语言,工具,框架,都只能消解偶发复杂性,而由软件规模大所导致的本质复杂性只能被管理,而不能被消除。大型软件的各种代码很快就能超出人类的认知负荷,所以我们需要:
管理复杂性
复杂性是软件开发中的第一敌人。我们常常跟随着大厂规范把请求放在controller中,把业务放在service中,如果我们把逻辑都写在Controller中,写出来的“Meta类”会让我们身心俱疲,如果这时有跨业务的调用需求,除了复制一份代码这种积重难返的疯子行为,大家也都会将共有逻辑抽象到一层中,分层其实已经是本能操作了(笑)
正因为上面讲到,人类大脑的处理能力是有限的,所以我们需要管理复杂度以尽量避免复杂度增长速度超过人类的理解能力。分层、抽象、解耦都是这个领域的解决方案。
开发过程中,变化无常的产品总是能把我们逼疯。但是软件总是需要变化的,因为需求在变化,这就是另一个:
变化是唯一不变的常量
需求一定会变,代码一定会腐化,我们所做的分层、抽象不仅仅能帮我们管理复杂度,也能提高拓展性。
但是如果产品催的急,大家总会违心的选择快速的方式,写出难以维护的代码。需求上线确实很快,但是这笔技术债总是要还的:
权衡无处不在
在人力资源不变的情况下,我们是现在花费10人日的工作量快速实现这个需求而不考虑可拓展性,还是花费30人日的工作量进行良好的设计后再开工,亦或者是让产品经理用cc和codex来直接实现?这就是权衡的一部分。我们没有办法找到最完美的设计,最完美的架构;我们只能在有限的时间,有限的人力,有限的Token(?)下试着找到最优解,或者说我们眼中的局部最优。
现在有一个概念很火,在AI coding时代兴起,叫TDD 测试驱动开发。通过详尽的测试覆盖全部代码和Edgecases,来保证代码运行正常。现在流行的CI/CD本质也是下面的一点:
反馈回路越短越好
瀑布模型中,严格遵守的线性架构会让测试远离设计和开发,导致修复错误的成本极高——软件工程著名的Boehm曲线(成本曲线),修复错误的成本随着开发周期的推进呈指数级增长。现在的MVP也是该思想在商业领域的一个体现:尽早的发现错误。
大模型能力强到一定境界时,我看到有不少人说:为什么不直接让大模型生成机器码?且不论大模型对机器码的训练语料完全不够,且大模型并没有真正的逻辑推理能力,最重要的一点是,Harold Abelson曾经说过:
程序首先是写给人看的,只是顺便给机器执行。
技术是为业务服务的,而软件也是如此:软件是人来编写,为人服务的,沟通成本是极大的(人月神话,往已经延期的项目加派人手只会因为沟通成本进一步增加而继续延期)。
那么如果用一句话来概括:
哈基米说:
在人类认知极限和资源约束下,通过抽象与解耦来控制复杂度,构建一套具备快速反馈机制的系统,以最小的代价持续交付价值并应对变化。