第十周:杂谈与碎碎念

近来也算看了不少别人的博客,有些文章非常优秀,还有一些很无趣。在我看来,一篇好的博文首先不能是官方文档的复读机,也不能让读者完全看不懂,其次,信息来源应该尽量保持一手。从某方面来说,学习知识-输出知识的过程就好比咀嚼或消化。不知道转了多少手的知识好比被多次咀嚼消化后的粘稠状半消化物,多少令人反胃。我曾经也是被百度与CSDN之流坑过,所以看到 csdiy.wiki 中相同的观点时,感同身受。

我希望博客可以介绍解决一个问题的方案,分析问题场景,找出问题本质,再对比几个方案,给出我对它们的评价和偏好;也可以是将网上的回答非常零碎,但别人肯定会遇到的问题进行总结。总之,我希望文章能体现出作者的思考和观点。这篇文章我将输出一些观点和我的一些偏好

实习以来,见过公司内部不少的奇技淫巧。因为别人的框架不能满足业务需求,就为自己量身打造了一个新轮子;为了加快上线速度,将JS动态翻译成dart,为flutter接入了动态化能力,见 基于JS的高性能Flutter动态化框架MXFlutter-腾讯云开发者社区-腾讯云;一个仓库同时开发多个产品,跨桌面端移动端,支持私有化部署,同时服务个人用户和企业用户,开放能力给扩展应用,甚至还开放SDK给第三方定制。在公司里看到了他们为了解决问题的热情和努力,对此我的认识是

  • 程序员(虽然比较俗的称呼,但大差不差)的工作简单来说,就是解决硬件以上的、所有的软件的问题
  • 现实是非常复杂的,为了应对复杂的需求,需要不断地修改设计或架构

这里又会掉进一个陷阱,一般来说,设计越复杂,就越能应对复杂的需求。可以认为,设计的复杂性是它能力的一个指标,但增加设计的复杂性应该是被迫的,即增加设计的原因只能是为了应对复杂的需求。简单来说,不要为了复杂而复杂;或者说,认清需求,不要过度复杂

现实是非常复杂而深刻的,虽然人们面临的问题总是相似的,并且总结出了很多范式或通用解法,也发明了不少理论,造出了不少轮子,但很遗憾的是“软件工程没有最终银弹”,为了解决复杂问题,总是需要反范式、发明新理论、重复造轮子

Note: 欧美古老传说中使用银弹可以杀死吸血鬼、狼人或怪兽,因此”最终银弹“引申为解决所有问题的最佳方法

在前几篇文章中,我已经谈论过分层。简单来说,分层尝试屏蔽底层细节,但它不仅仅不能完全屏蔽,而且还会导致上层只能访问底层共同能力,而无法访问其全部能力。分层是为了减少开发者心智负担,但不代表开发者可以完全不考虑底层、对底层一无所知。如果一个人工作在上层也许可以解决99%的问题,但无论如何,总有一些问题是上层一定无法解决的。如果只工作在上层,使用别人提供的优秀的框架,而不了解其底层原理、机制、概念,这样的人顶多叫做“框架使用者”,而不是“开发者”。很遗憾的是大部分人都是“框架使用者”,熟练使用框架与“熟练使用缝纫机”没有本质区别。我始终认为程序员的工作是一个发明、创造的工作,而不是机械的、流水线的工作。这个话题在reddit已经有讨论: Be an Dev, not a Frameworker

以上已经可以看出我的想法了。相比上层,我更倾向底层,从大二到现在,我也是一直在这个方向努力。底层开发者面临着更复杂、更深刻、更广泛的问题,他们的工作成果决定了上层的形状,底层的革新会带来更长久更广泛的进步。如果将底层开发者的工作比作生产各种玩具,那么上层开发者的工作就是将几种玩具打包、装进漂亮包装盒、缠上红缎带,定高价在商场中出售。很明显上层开发者会获得更多的利益,包括名誉、知名度、金钱。也许未来的我会后悔,但至少现在还有热爱与激情支撑

最近也是打算深入学习一下C/C++了,说来惭愧我连C++ primer都没看完。b乎上关于C++的批评也不少,虽然大部分都是站在高级语言的角度踩一脚C++
我的反驳理由也非常简单:很多高级语言的解释器、虚拟机都是用C/C++写的,例如Python,java,dart,js等

C/C++可以说是又古老又现代,诞生了不少历史悠久的工程,其中不乏postgresql这样的老牌开源项目,也有windows这样的商业工程。这些工程经过了广泛的实践与验证,即使是C/C++标准大变样,操作系统、软件工程方法长足进步的现在, 它们解决一些问题的方法也是非常具有参考意义的。这些历史可以认为是C/C++的资产,有了这样的资产,开发大型项目时选择C/C++就不奇怪了

此外,C/C++与其他语言最大的优势有两点

  • 操作系统基本是用C/C++写的
    所以操作系统的API,即系统调用也往往是以C/C++的形式提供的,因此C/C++能访问操作系统的全部能力
  • C API已经成了跨语言互操作的公认事实标准
    使用其他语言容易陷入一个困局:无法在其他语言中调用,所以其他语言总是需要重复实现一些功能

此外,因为很多高级语言的解释器、虚拟机都是用C/C++写的,所以这些解释器、虚拟机也依赖于C/C++的运行时。然而高级语言的运行时很多时候会造成不少麻烦。很多高级语言的运行时不容易嵌入到其他运行时中,在部署时,还需要带上运行时。C/C++的优点是就体现在这里,因为操作系统基本是用C/C++写的,所以绝大部分操作系统都有C/C++的运行时,基本不用考虑C/C++的运行时问题

很想评价一下b乎的风气了。经过长时间使用b乎,我也是发现b乎用户平均水平了。b乎的计算机话题充斥着不少半吊子毫不客气地批评各种东西。看了这些内容后,我产生了一个观点:没有调查就没有发言权

b乎指知乎

一个框架、语言、系统、产品,诸如这样的东西肯定不是完美的,它从诞生时就有各种问题,但在实践中人们总是会用其他方法来弥补甚至绕过这个问题。只有参与到广泛的实践中,才能真正认识到它的优劣。一个原则就是,如果它已经部署在许多生产环境并创造了价值,那么它一定是有可取之处的,至少轮不到b乎用户来批评

这也让我反思,我过去对windows的评价可以说是非常低了,自从主力切换到archlinux后,虽然在archlinux遇到了数不尽的问题,但还是非常喜欢使用linux,并且如果不从事windows开发,我想我可能都不会主动使用windows开发了。认识到 没有调查就没有发言权 后,我也改变了我的观点。

我现在对windows的观点是,它确实存在不少我非常不喜欢的东西,微软雅黑和新宋体非常丑陋,cmd和powershell不好用,GBK编码特别坑,POSIX不兼容,C/C++开发比linux麻烦得多等等。但作为PC市场的霸主,还有windows server跑在服务器,它能取得成功是有原因的。因此我对windows的意见暂时保留,并且开始学习win32、阅读windows文档、计划阅读windows internals(深入理解windows的目地是为了更好的批评windows)

windows有很多老古董,虽然windows的技术栈在不断迭代,但其背后的核心并没有改变。例如 Component Object Model(COM)虽然最开始被使用在ActiveX, IE这样的老古董上,但时至今日,COM仍然是Windows一个非常强大的技术,它间接地解决了C++ ABI问题,让C++开发者之间的协作变得更加容易。即使是2024年的现在,标准委员会也没有要解决C++ ABI问题的意图。linux基本没有解决C++ ABI的问题,在我的认知中,linux是通过以下方式缓解的

  1. linux许多软件包都是源码发行的,只要软件将他们依赖的软件包源码一并编译,就不会遇到ABI问题。而windows商业软件很多,让商业公司重新编译的难度非常高
  2. GNU为了ABI兼容做了许多努力,简单来说,GCC前向兼容跨越了许多个大版本,覆盖了绝大部分linux发行版软件源中gcc版本
  3. 导出C接口,避免C++ ABI问题

此外,Windows Side by Side(WinSxS)解决了动态库依赖问题,这样的技术即使现在放到linux也是非常先进的,而且WinSxS居然在Windows 2000就被发明出来了。linux并没有类似的技术解决这个问题,相反,linux通过包管理器、appimage这些手段避免了动态库依赖问题