经常听到一些程序猿加班是常态的言论,在这里我想尝试下进行反驳。
先来分析一下这个命题:
程序猿加班是常态 => 程序猿的工作量巨大 => 软件系统过于复杂,难以处理。
可见,反驳这个命题的关键就在于回答『如何治理一个复杂的软件系统』这个问题。
接下来深入看下这个问题。软件系统是什么?我认为软件系统无非是一个巨大的逻辑体。你的任何操作(如点击 web 的一个按钮),其实背后都是一层又一层的逻辑堆砌的结果。
以浏览器渲染一个按钮(<button>click</button>
)为例,这其实是浏览器解析 HTML 代码的结果。HTML 本身是有逻辑可循的,你必须要按照他的规则编写才能正确地展示 UI,而 HTML 本身又需要浏览器进行解析,这个过程需要用到许多更低一层的逻辑(比如语法解释器,比如图形渲染的接口),这些被依赖的逻辑本身又会依赖更底层的逻辑(比如显卡接口)。 这些逻辑一层嵌一层的,直至最底层的 CPU 指令集。
因此任何一个复杂系统,本质上其实就是一个庞大的逻辑体。而这个逻辑体,必然是分层的(你见过谁直接用 GPU 的接口来渲染网站吗),否则描述它的复杂度就是指数级增长了,以人脑的计算能力很快就无法处理这么复杂的系统了(这就好比在概率论里面,让你连续抛掷10次硬币,再用文字表示其样本空间,你要是用枚举法来表示,可能得写到手软,但要是用笛卡尔积来表示,只需要一行即可)。
整个网站开发,其实也是分层的,其中最经典的莫过于三层架构了(视图层、业务逻辑层、持久层),演化到今天,视图层独立出去成了前端领域,业务逻辑与持久层则归为后端领域(当然,还有更底层的东西,比如说数据库,操作系统,只是大多数开发仔都不需要参与这一块的开发)。
看起来很完美,前人已经把分层都设计好了,并且那些高难度的底层开发工作往往也有开源组织在承担,稳定性都是经过验证的。我们做应用开发的,只要搞定视图层(前端)跟业务逻辑层(后端)不就好了吗?这点事情都搞不定吗?问题究竟出在哪呢?
其一是,单独某一层内的复杂度,其实很多时候是超乎我们想象的,因此需要进一步分层。
以业务逻辑层为例,一个简单的用户注册,可能就包括了验证码校验,密码复杂度检测,手机绑定,注册成功欢迎邮件发送,发放优惠券等等业务逻辑,以及缓存、冗余之类的非业务逻辑。这些被依赖的逻辑其实也是同属于业务逻辑层的,并且也会被其他业务逻辑依赖,一旦没有做好划分,那又是重复造轮子,复杂度和工作量都会指数级增长!关于这一块,不同领域的解决方案不同,前端是组件化,而后端是面向对象(虽然是很标准的答案,但我认为真正掌握这两技能的研发同学其实很少,最典型的莫过于拿着面向对象语言,写着面向过程代码),非要抽象出一个更通用的方案的话,我认为可以称之为『建模』。前端通过建模,将视图层进一步分拆出组件层;后端通过建模,将业务逻辑层进一步分拆出领域模型层(在微服务架构中亦称之为业务中台)。
其二则是工程问题了,即沟通成本的问题。举个例子,HTML 语言提供了 button 标签,但这个标签除了渲染一个按钮外,还会同时渲染一个文本输入框给我们。这显然不是我们需要的,好在这个标签同时提供了一个配置给我们,可能显式地指定避免渲染文本框(额外的工作是你需要指定该配置 <button disable-input='true'>click</button>
)。这样子虽然最终实现了想要的效果,但却让代码变得不优雅,不仅你花费了不必要的时间,还导致后来的维护者看到会迷惑,沟通成本由此产生。 尝试把这个现象无限放大,如果你在代码中看到的每一个概念定义都是不可信的,都是与现实世界有偏差的,那么当你要解决一个问题或开发一个新功能的时候,你工作中花在的这些无谓的沟通中的时间就会成倍地提升(尤其是当没有人或文档能解答你的问题的时候),最终远远大于你的编码时间。
问题分析到这,答案也就出来了。如何治理一个复杂的软件系统?私以为关键在于做好模型设计,以及维护好概念一致性。前者可以通过组合逻辑空间的不同维度,使得复杂度的表示难度指数下降,最终达到人脑可以处理的水平。后者则保证模型中的概念跟现实世界是一致的,易于理解,可减少不必要的沟通成本。两者任一没有处理好,均会导致复杂度指数上升,进而导致工作量增大,因此程序猿加班也就成了常态了。经常加班的猿们,都应该先反问下自己,这两点是不是都做得足够好了?如果是,再来问是不是人手不够之类的问题。
当然现实往往更复杂,很多同学都会以业务方不给时间,上级不支持为由,写出一堆烂代码来。确实这也是一些阻力,但并不能成为人云亦云的借口。有这种想法的同学,要么是选择躺平了,不想去改变现状;要么是缺乏对事物发展规律的认识的,推荐学习一下教员的《矛盾论》。