软件幻灭(译)

Translated from English by 江成.

我编程已经15年了。但是现在,我们的行业越来越不关注效率、简单性和卓越性,这让我对我的职业和整个IT行业感到沮丧。

作为对比,现代汽车工作效率可以达到当前引擎设计的98%的效率。现代建筑使用刚好的材料来搭建完成并且在一定条件下保持安全稳固。所有的飞机都收敛到最优的尺寸/形状/负载上,看上去基本一样。

反过来,只有软件行业,大家似乎普遍接受程序运行在1%甚至0.1%理论效率上。有人甚至对程序运行的多么低效而感到自豪,“我们为什么要担心程序低效,计算机有足够的计算能力。”

@tveastman: 我有一个每天都要运行的Python程序,每次运行需要1.5秒。我花了六个小时用rust重写了这个程序,现在这个程序运行一次只需要0.06秒了。41年零24天以后,效率提升节约下来的时间补偿了我重写程序的六个小时。:)

你可能听过这样的说辞:“程序员的时间比计算机时间宝贵多了”。这句话基本上反映出我们现在在极大的浪费计算资源。你会买百公里耗油100升,甚至1000升的汽车吗?在软件行业,我们一直这么做。

所有软件都难以忍受地慢

看看周围,现在笔记本电脑的性能是第一次登月载人航天飞船上电脑的几千倍。然后在最新的顶级Macbook Pro上,所有的网页都不能保持60帧的平滑滚动。我能用笔记本电脑流畅地玩游戏、看4K视频,但是不能平滑地滚动网页。这怎么能接受?

谷歌邮箱,谷歌旗下的网页应用,在谷歌自己开发的Chrome浏览器上,需要花费13秒的时间打开一个中等大小的电子邮件

它为邮件展开添加了动效,但是应用动效的是一个空白区域而不是邮件内容。为什么呢?因为只有这样动效才能达到一个不错的效果。不,不错的效果并不意味着每秒60帧,而只是意味着“尽可能地快”。我迫切想知道当120hz显示器成为主流的时候,Web社区会怎么回答动画效果的问题。现在连60hz刷新率都很难做到。

Windows 10需要花费30分钟来更新系统,怎么可能需要这么长的时间呢?30分钟都够我完全格式化我的SSD硬盘,下载一个全新安装镜像,安装5次系统了。

Pavel Fatin: 在编辑器里面输入文字是一个相对简单的过程,所以即使是285的主机都能够比较顺畅地输入文字。

现代的编辑器的输入延迟比42年前开发的Emacs编辑器高很多。编辑器啊!还有比编辑器更简单的吗?每敲击一次键盘,你所需要做的仅仅是更新一个小小的方形区域,但是现代的编辑器却不能在16毫秒内完成一次键入。16毫秒可是很多时间,一个3D游戏可以在这个时间内刷新整个屏幕,渲染成千上万的多边形。同时还处理用户输入,重新计算环境世界,动态加载或释放资源。为什么会有这么大差距?

一个普遍的趋势是,我们并没有得到更快且更多功能的软件。我们的硬件越来越快,但是软件越来越慢,同时功能并没有增加。所有的软件的运行速度都远低于理想值。你有没有想过为什么你的智能手机需要30到60秒的时间来启动。为什么它不能在1秒内启动?这里并没有硬件限制,我非常想看看是不是真的可以做到。我非常想知道如果我们尽力挖掘每个比特的性能,我们能达到什么样的极限,我们能探索出什么样的极限。

所有软件都巨巨巨巨大无比

软件体积在膨胀。仅仅是屏蔽所有的网页广告,网页应用的打开速度就能提升10倍。谷歌恳求大家不要再用AMP来搬起石头砸自己的脚了——这是一个不需要任何技术,只要一点点常识就能解决问题的技术解决方案。如果你去除掉膨胀,Web应用会变得非常快,这需要多聪明才能理解?

一个没有安装任何其他APP的安卓系统需要差不多6GB的存储空间。请停下来想一下,这是个多么巨大的数字。这里面到底有什么,高清电影吗?我猜大部分应该是代码:系统内核,驱动,字符串和资源等等,但是这些资源都不会很大。所以,一部智能手机到底有多少驱动程序系统才能撑这么大?

Windows 95的大小是30MB。今天有些网页的都比这个大!Windows 10大小是4GB,是Windows 95的133倍大。但是Windows 10比Windows 95高级133倍吗?我的意思是,它们的基本功能是一样的。是的,我们有Cortana(Windows下的私人智能助理),但是它也不可能需要3970MB。我们先不管Windows 10有哪些功能,难道安卓比它还复杂1.5倍?

谷歌的键盘应用需要150MB空间。一个绘制在屏幕上绘制30个键的程序的复杂程度真的是Windows 95复杂程度的五倍吗?谷歌应用,基本上就是谷歌网页搜索的封装,需要350MB空间!谷歌Play服务,我基本不用,需要300MB空间,而且不能删除。

当我安装完必须的应用(社交,聊天,地图,打车,银行应用),我的手机只剩下差不多1GB的空间了。这里面完全没有游戏,没有音乐!回忆一下,当年一张磁盘就能存储操作系统,系统应用和你所有的数据内容。

你桌面的todo应用可能是基于Electron写的,所以它包含了一个Xbox 360控制器的用户态驱动在里面,这个驱动能渲染3D图像,播放音频,用你的网络摄像头拍照。

一个简单的文本聊天应用因为其启动速度和内存消耗而被人诟病。是的,你应该将Slack划分为资源消耗型应用。但是我的意思是,聊天室和简单的文本编辑工具,这些应该都不是资源消耗型的应用才对。欢迎来到2018。

你可能会说,至少它能正常运行。是的,但是越大并不意味着越好。越大意味着有人失去控制了;越大意味着我们不知道到底在发生什么;越大意味着额外的复杂性,额外的性能开销,额外的不稳定性。这不是常态,也不应该成为常态。体积过大的应用应该是一种危险信号,应该避而远之。

所有软件都在腐烂

3年前,16GB存储空间的安卓手机能良好运行使用,但是到了今天,16GB在搭载安卓8.1的手机已经显得不够用了,因为安卓8.1的系统已经膨胀了至少两倍大。而且安卓8.1并没有带来明显的变化和功能提升,并没有变得更快或者优化更好,看上去也差不多。它就是这么膨胀了?

iPhone 4s发布时搭载最新的iOS 5系统,但是很难运行iOS 9系统。基本上来说,iOS 9和iOS 5其实是差不多的,iOS 9并没有变得更高级,而且新的iPhone的硬件变得更强大了,所以事实上是软件变得越来越慢了。别担心—你得到了一个重要的能力……以相同的速度运行相同的应用!我就是不明白为什么。

从iOS 11开始,系统将不再支持32位的应用,这意味着,如果开发者没有赶在iOS11 发布之前修改好应用,或者开发者根本没有意愿去去修改已经完美运行很久的应用,那么这些应用将从应用商店消失。

@jckarter: 在80年代,一个DOS程序不需要任何修改就能在大部分的计算机上运行。但是现在,Chrome的一次更新,就会可能让某个JavaScript应用不能正常运行。

现在能正常运行的网页,可能10年(或者更短时间内)后就不能兼容当时的浏览器了。

“你需要拼命地奔跑,才能保持呆在原地”。但是这有什么意义呢?是的,买了新手机或者新的MacBook我也会感到高兴,但是换上新设备只是为了运行变得越来越慢的应用吗?

我觉得,我们可以也应该做得更好。大家都只忙着应对现在的需求来构建软件,很少考虑长远。但是如果一个软件能运行更长久一些,不也是好事吗。

“差即是好”

现在,没人知道,也没人有意愿知道。我们只是满怀希望地把几乎没用的东西丢出去,还美其名曰“创业智慧”。

如果网页出现什么问题,只会提示你去刷新页面。谁有时间去研究到底什么地方出问题了?

即使在兼容的浏览器上,Web应用都会报出大量的“随机”JS错误。

整个网页/SQL数据库的架构都建立在当用户浏览渲染出来的网页时不会去操作任何数据的假设(或者说希望)上。

大多数协作功能的实现都只是“尽力”保持数据一致和安全,然而在很多正常用户使用场景下都会丢失数据。你一定见过“你要保留哪个版本“的对话框。如今整个行业的标准如此之低,你的用户会感到高兴,至少你提供了一个这样的对话框。

不,对我来说,这个对话框的意思是:“我准备要删除某个文件了,你想要删除哪个?“,这对我来说,这是不可接受的。

Linux被设计成会随机销毁进程。然而它是现在最流行的服务端操作系统。

我所有的设备,总会出现这样或那样的问题。我的戴尔显示器经常要重启,因为里面运行着软件。隔空推送?如果你运气好,它能检测到你的设备,如果没检测到,你能做什么?蓝牙?蓝牙的协议如此复杂以至于蓝牙设备之间是不会互相通信的,而且定时重启是解决问题的最佳方法

而且我还没有提到物联网。它有太多槽点了,我都不知道如何吐槽它。

我想为我的工作感到自豪。我想交付可以工作,稳定的东西。要做到这一点,我们需要了解我们正在构建的东西,包括内部和外部,而这在臃肿、过度设计的系统中是不可能做到的。

编程也是一团糟

现在好像没有人有兴趣构建高质量、快速、高效、持久、基础的东西了。就算多年前已经有高效的解决方案了,我们还在努力解决同样的问题:包管理、构建系统、编译器、编程语言设计、集成开发环境。

构建系统本质上就不可靠。即使所有的失效信息都在那里,还是需要时不时完全清理一下。我们完全可以让构建过程变得可靠、可预测、百分百可重现,只是没人觉得这很重要。多年来,NPM一直是“有时候可用”的状态。

@przemyslawdabek: 对我来说,开发Node.js/Javscript项目,rm -rf node_modules 一直是我工作过程不可或缺的部分。

提到编译时间,没人认为编译十几分钟甚至几小时是什么问题。不是说“程序员的时间更重要”吗?几乎所有的编译器,在没有带来明显的好处的情况下,都会在预编译和后编译阶段都会加上巨大的,有时甚至是灾难性的额外时间损耗。

你期望程序员做出理性的决定,然而有时候他们正好相反。例如,即便在笔记本电脑上运行相同的任务比在Hadoop上更快的情况下,依然选择使用Hadoop

当大多数电脑还不可靠的时候,机器学习和“人工智能(AI)”又将软件推向了靠猜测的阶段。

@rakhim: 当某个APP或者服务宣称是“人工智能驱动”或“基于机器学习”的时候,我把它理解为“不可靠,不可预测,行为不可解释”。我尽量避免使用”AI”,因为我希望电脑是可靠的,可预测的,可解释的。

我们在Linux上构建虚拟机,然后在虚拟机上构建Docker容器,就因为没人可以理清楚程序,编程语言和运行环境的混乱状况。我们用毯子盖在狗屎上,而不去处理。“Single binary”依然是Go语言的大卖点。不混乱 == 成功。

提到依赖关系。人们总是很轻松的加上过度工程化的“完整解决方案”去解决最简单的问题,而不去考虑代价。这些依赖关系又带来其他的依赖关系。最后你的依赖树会介于恐怖故事(天呐,这么大,而且到处是冲突)和喜剧(没道理我们需要依赖这些,但是他们就是在依赖关系上)之间。

如果不重启,程序就不能长时间正常工作。有时,甚至都无法正常工作几天的时间。程序经常出现随机的问题,但是没人知道为什么。

更糟糕的是,没人有时间停下来,仔细分析到底发生了什么。既然你有简单的方法,为什么要花费精力去寻根究底。增加AWS的实例。重启进程。重置整个数据库。添加看门狗程序,每20分钟重启你崩溃了的应用。多次引用相同的资源,压缩打包然后交付。快速迭代交付,不要修复。

这不是工程化,这是懒惰。工程化是深入理解性能,深入理解结构,深入理解你构建的局限性。在粗制滥造上粗制滥造正是背道而驰。如果要进步,我们需要理解我们在做什么,为什么要这么做。

我们被困住了

所有的软件都是在之前几乎不能工作的代码上叠上几乎不能工作的代码。代码量和复杂性持续增加,改正的可能性越来越小。

要想构建健康的生态,你需要倒回去完全重构。有时候你需要有所放弃,然后用更好的替换掉。

但是谁有时间这么做呢?我们应该有25年没有开发新的操作系统内核了吧?内核太复杂了,以至于没有办法重新写一个。浏览器充满了边缘情况和历史遗留问题,以至于现在没有人敢重头写一个布局引擎。

现如今进步的定义就像火上浇油:

@sahrizv: 2014 - 为了解决单例应用的问题,我们一定要引入#微服务。

2016 - 为了解决微服务的问题,我们一定要引入#docker容器。

2018 - 为了解决docker容器的问题

或是重复造轮子:

@dr_c0d3: 2000: 写100行XML来声明式的配置servlets和EJBs。

2018: 写100行YAML来声明式的配置你的微服务。

至少XML有结构模式

我们被我们自己困住了,而且没人会拯救我们。

业务并不关心

用户同样不关心。用户只是学会只期待我们能提供的。我们(工程师)声称安卓应用需要350MB的空间?好吧,用户他们接受。我们声称我们没办法提供平滑的滚动?好吧,用户他们接受不流畅的手机。我们指导:“如果它不工作了,试着重启”?用户就会重启。毕竟,用户并没有其他选择。

行业也没有竞争。大家都在构建同样很慢,体积膨胀的,不可靠的产品。偶尔出现的高质量的产品马上就能带来竞争上的优势(iPhone/iOS对比其他智能手机,Chrome浏览器对比其他浏览器),也激励所有人向他们靠齐,但是这种情况持续不了很久。

所以,向世界展示以今天的计算机在性能、可靠性、质量、可用性上能达到什么程度,是我们工程师的使命。如果我们关注,人们也会学习到。而且也只有我们才能展示这是很有可能的,只要我们关注。

并不是所有的都糟糕

行业还是有一些亮点,也表明超越最先进的技术并不是不可能。

Martin Thompson 的工作(LMAX Disruptor, SBE, Aeron)就令人印象深刻,简单且高效,令人眼前一亮。

Raph Levien开发的Xi 编辑器 看起来有着正确的设计原则。

Jonathan Blow 自己开发给自己游戏开发用的编程语言,在笔记本上每秒可以编译50万行代码。而且是全新编译,没有中间缓存,没有增量编译。

编写高效的程序不需要你是天才。这并不需要什么魔法。只是不要在现在的臃肿不堪的工具链上去构建就行。

更好世界宣言

我希望看到进步。我希望改变。我希望最先进的软件开发技术得到提升,而不是停滞不前。我不希望看到不停的重复构建同一样东西,而且每次性能更差,体积更庞大。我希望有值得相信的目标,一个有价值的终极目标,一个比现在更好的未来,一个有相同愿景的工程师社区。

今天我们拥有的不是进步,我们用简陋的工具构建勉强符合业务需求的软件。我们被困在局部的最佳状态,没人愿意离开。这不是一个好地方,这里体积膨胀而且低效。我们只是习惯了而已。

所以我想提出:我们今天所在的地方是糟糕的。作为工程师,我们有能力也应该做得更好。我们应该有更好的工具,我们应该构建更好的应用,更快,更可预测,更可靠,资源消耗更低(低几个数量级!)。我们应该深入理解我们在做什么以及为什么要做。我们可靠、可预测、最高质量地交付。我们有能力也应该为我们的工作感到自豪。我们不应该只是“交付我们已有的……”,没有但是!

我希望我不孤单。我希望有更多的人也想这么做。如果我们现在能够开始讨论软件行业的现状有多糟糕,我也会感到欣慰。或许我们能找到出路。

Hi!

I’m Niki. Here I write about programming and UI design Subscribe

I consult companies on all things Clojure: web, backend, Datomic, DataScript, performance, etc. Get in touch: niki@tonsky.me

I also create open-source stuff: Fira Code, DataScript, Clojure Sublimed, Humble UI. Support it on Patreon or Github