2023 / 05 / 03

编程随笔

命名

  • 命名,无论怎么强调都不过分。

  • 命名的本质是概括,换言之,代码必须形成概念。

  • 命名做不好,根本原因往往是,代码背后的概念本就含糊不清。不深究本因,一味追求「命名规范」,其实是颠倒了因果关系,无异于水中捞月。

  • 代码复用,绝不是简单把重复代码抽离为公共模块。如果代码没有形成完整概念,如果你给不了它一个简短的名字,那么即使有再多地方出现这段重复的代码,也不要复用。

  • 为使一个概念从残缺变完整,不一定要增加什么,常常也可以扔掉什么。

  • 如果某件东西,把其任意一个部件移除,它就彻底坏了:我愿称之为「精妙」。

分治法

  • 分治法:把一个问题拆解成为数不太多的若干个独立子问题。

  • 面对复杂问题,自顶向下不断分治,将问题拆解为树,使每个叶子节点都足够简单。这是设计复杂系统最重要的方法论。

  • 分治的结果,不应是子问题的简单加和(把一箱苹果拆分为每个苹果),而应是子问题的有机组合(把一箱苹果拆分为一些苹果和一个纸箱,苹果在纸箱内)。

  • 一个问题的拆解,必须由架构师一人独立完成,因为概念只能源于一人的脑海。

架构师

  • 架构师的职责:划分边界,规定依赖。一个项目中,谁能在这两件事上做决定,使他人遵照你的方案,谁就是架构师;如果没人能在这两件事上做决定,那就没有架构师。

  • 为什么架构师追求形式正确?因为分治的结果(至少在未实现前)是形式。

  • 为什么架构师关注接口甚于实现?因为正确的接口能够避免错误的实现导致的 bug 在各个子系统间传染。

  • 举一个追求形式 / 接口正确的极端案例:考虑「根据 id 查询 item 详情」的接口,返回的详情数据中,是否应该包含 id 本身?我认为,如果不包含,接口就从形式上就杜绝了出错的可能。调用方原本就知道 id,试问如果详情数据中包含的 id 与调用时传入的不同,调用方应如何自处?如果采信了错误的 id,责任究竟在调用方还是接口提供方呢?

技术债

  • 我认为「技术债」这个比喻并不恰当:首先,你可以永远不偿还技术债,因为技术债不影响软件运行,背负沉重技术债的软件甚至可以运行得非常稳定;其次,你似乎永远可以借到技术债:不管现状是多么的千疮百孔,你似乎总能找到「临时方案」。

  • 如果把软件开发活动,视为使用「开发资源」这种货币来购买「软件需求实现」这种商品,那么我更愿意把「技术债」称为「技术贷」:一种特殊的消费贷。如果你看上某件商品,但囊中羞涩,可以选择贷款,自己只需支付 5% 的首付即可买下。这笔贷款没有任何偿还期限,但贷款存续期间,其他支付行为会增加 5% 手续费。结束贷款的方式有两种:重新按原价购买商品,或再次支付 5% 的手续费来扔掉商品(需注意,结束贷款支付的费用,也会受存续贷款的影响)。同时,银行承诺永远提供这样的贷款合约。

  • 举债似乎是完全无成本的,但是举贷必须有 5% 的首付(再巧妙的临时方案都有开发成本)。最终压垮软件系统的,并不是无债可借,而是在大量存续贷款手续费的加持下,我们连最廉价商品的 5% 首付都无力承担。

  • 如何评价软件的腐败程度:开发新功能时,多少精力投入在功能本身的开发上,又有多少精力投入在防止把原有功能弄坏上。

诊断

  • 如何把不稳定复现的问题转化为稳定复现的问题?把复现的过程自动化,然后重复运行足够多次。

  • 软件性能问题就像发烧。发烧不是一种病,而是一种症状,你不应指望「退烧药(性能最佳实践)」能真正治好什么大病。

  • 「再多的药也比不上一次正确的诊断」——《霍乱时期的爱情》。

  • 问程序员「这个 bug 什么时候可以修好」,可类比于问医生「这个病什么时候可以看好」。

程序员

  • 程序员喜欢抬杠,因为赞同意见不会实质地推动讨论的进展,可以不说(只会在心里默默赞同)。

  • 我不喜欢「打磨」的说法,它暗示了这件事是容易的、表面的、可替代的。软件开发工作中不存在容易的部分,因为容易的部分已经被优秀的工程师自动化了。

  • 当队友说「这个盒子真好看,我要留着装东西」时,我听到的是:「房子太大了,这块空间扔了吧」。

  • 软件开发工作中的沟通成本比任何外行估计的都高。这就是为什么单枪匹马的程序员,与传统的开发团队相比,有着巨大的成本优势。个人英雄主义在软件开发行业并未过时。