致编辑:关于题名,有意转译为(我们是手艺人),这样可能更有吸引力且贴合博文对自我的定位

日期:2003年12月1日,周一

做软件不能算是制造业。 上个世纪80年代,人们奔走相告,害怕日本软件公司在建的那些“软件工厂”会从流水线上吐出高质量的代码。 这完全是无稽之谈——过去是,现在也是。 把一堆程序员撵进一间屋子,再把他们整整齐齐码成流水线,这真的无助于减少软件错误。
如果写代码不是流水线式的生产,那它是什么? 有人提出用手艺这个字眼。 这同样不是十分准确,因为我不管你怎么看,反正Windows里那种询问你希望如何索引帮助文件的对话框——无论从方式上、表现上还是形态上——都绝对不像一个正常人嘴里所说的“手艺”。
写代码不是生产代码,有时也算不上什么手艺(不过有时写代码确实是一种手艺);写代码是一种设计。 设计是那种价值产出速度会超过成本投入速度的灰色地带。 《纽约时报》杂志版曾经倾倒于iPod,拜服于“苹果”为什么会是那少数几家知道如何利用优秀设计来提升产品价值的公司之一。1

1. 典出于Rob Walker于2003年11月30日发表的一篇时文,标题为 the guts of a new machine,网址为 http://www.nytimes.com/2003/11/30/magazine/30IPOD.html ——译者草注

ipod

2. 图注,优雅的“苹果电脑公司”,于2007年更名为“苹果公司”。参考链接已轶失, http://www.apple.com/pr/photos/ipod/03ipod.html ——译者草注

不过我已经谈够设计了,现在我想稍微谈会儿手艺: 手艺是什么?怎样知道这算不算手艺呢?
我想跟大家谈谈鄙人曾经为CityDesk 3.0重写过的一小段代码,就是负责文件导入的那部分代码。 (广告时间: CityDesk是鄙公司一款易用的内容管理产品。)3

3. CityDesk简介及链接 http://www.fogcreek.com/CityDesk/ 以及http://en.wikipedia.org/wiki/CityDesk——译者草注

这段代码的规格说明书看起来很简单,就像所有那些代码小片段一样。 用户利用标准对话框选择文件,程序将选中的那个文件拷入CityDesk数据库。
结果却发现,这个例子很好地展示了什么叫“行百里而半九十九”——最后那1%的代码却占用了多达90%的开发时间。致编辑 第一稿代码看起来像这样:

致编辑:原文直译不太好玩,套用改写中文现有说法更有趣味性,且贴合原文含义

  1. 打开文件。
  2. 将整个文件读入至一个大型字节数组。
  3. 将这个字节数组存为CityDesk数据库的一条记录。

它非常好用。 文件如果存在且大小合理,效果可谓立竿见影。 它确实有一些小问题,不过我都逐一解决掉了。
然而,将一个120MB的文件拖入CityDesk以进行强度测试时,我遇到了一个大问题。 即便是现在,也绝对很少看到有人会在网站上发布120MB的文件。 实际上,这相当罕见——不过也并非不可能。 那段代码仍然管用,但费时将近一分钟,而且没有提供任何视觉反馈——程序就那样冻结着,像是完全被锁死了。 这显然不够完美。
从UI(User Interface, 用户界面)的角度看,我真正想要的是:长时间的操作应该带一个像模像样的进度条并搭配一个“取消”按钮。 理想情况下,我们应该能继续对CityDesk做其他操作,而后台还不间断地拷贝着文件。 要做到这一点,有三种显而易见的方法如下:

  1. 只用一个线程,并由它频繁轮询是否存在输入事件。
  2. 启动另一个线程,并作严格同步。
  3. 启动另一个进程,并仅作宽松同步。

根据我的亲身体验,1号方案从来就没好用过。 不间断地拷贝着文件,同时还要保证整个应用中的所有代码都可以安全运行,这太难了。 而Eric S. Raymond已经说服我相信,多线程通常并不像彼此独立的多个进程那样好用。4 事实上,多年的经验也告诉我,多线程编程会造成很多额外的复杂性,而且会引发各种危险可怕的全新海森堡蚁虫之灾。5 3号方案看起来不错,特别是考虑到我们底层的数据库就是多用户的,它并不介意同时有很多进程跟自己打交道。 好了,我打算一放完感恩节的假就这么干。

4. 典出Eric Steven Raymond的大作《The art of Unix Programming》第7章第3节的一个段落,其联机地址为 http://www.faqs.org/docs/artu/ch07s03.html#id2923889 ——译者草注
5. 原链接已轶失 http://c2.com/cgi/like?HeisenBug 。wiki百科为 http://en.wikipedia.org/wiki/Heisenbug。指的是在成品环境下会不经意出现,但费尽九牛二虎之力却无法重现的计算机bug。海森堡是数学中著名不确定性原理的发现人——译者草注

但是,请留意现在的大场面。 我们不再是简单的读取文件并将其存入数据库,而是置身于某种明显复杂很多的图景中: 启动一个子进程,让读取文件并将其存入数据库,向这个子进程附加进度条和“取消”按钮,接着再加上某种机制以使得这个子进程一旦收悉文件可作展示时就通知其父进程。 这里还有一些其他的工作要做,譬如:向子进程传递命令行参数;保证窗口的焦点得失行为符合预期;应对用户在文件拷贝结束之前就关闭系统的特殊情况。 我估摸着,所有这些要说到做到的话,需要10倍于现在的代码才能优雅地处理大文件,而只有1%的使用者有机会见证到这些新增的代码!
当然,有那么一群程序员会嘈嘈着说,我新做的这个子进程架构比原来的那个还要低劣。 它臃肿不堪(这全是因为所有那些新增的代码行)。 它更具有犯错误的潜质——这同样是因为所有那些新增的代码行。 它太小题大作了。 他们会说,这么做在某种程度上彰显了Windows为什么会是这样一个低劣的操作系统。 “所有这些扯得上进度指示器吗?!”他们冷笑着。 只要按下“Ctrl+Z”再重复“ls -l”命令行命令,就能看出文件是不是在增大了!
这个故事背后的寓意在于“行百里而半九十九”——要想修正那1%的的瑕疵,有时需要投入500%的精力。 唯独只有软件如此吗?不是这样的,先生。现在我负责这里的所有建筑工程,因此我可以明确告诉您这一点。 终于,就在上一周,我们的包工头最后还是给Fog Creek的新办公地点画上了一个句号。6 扫尾工作中有一项是安装锃光瓦亮的蓝色亚克力有机玻璃门面,每块玻璃板都要包裹上螺钉间距为20厘米的铝条。 如果仔细观察照片,就会发现每块门玻璃周围都包上了铝条。 合上门的时候,会有两条竖条彼此贴在一起。 从照片里可能看不清楚,但中间这两条竖条的螺钉确实是接近对齐,但绝对没有完全对齐——大概有2厘米的偏差。 负责干这活儿的木工师傅丈量得很仔细,但他在安装铝条时,门还躺在地上,并没有安装到位,而在装上玻璃门之后……哎呀,很明显那些螺钉并没有完全对齐。

6. 新办公地点的各种得瑟, http://www.joelonsoftware.com/articles/BionicOffice.html ——译者草注

newDoor

这种情况可能没那么稀罕——我们办公室里就有很多螺钉没有做到完美对齐。 问题在于,钻孔之后再作矫正的成本昂贵得一塌糊涂。 由于螺钉的精确位置仅仅相差几厘米,我们完全无法在门上另钻新孔;你可能不得不更换掉整扇门。 这真的不值得。 这又是一个“行百里而半九十九”的例子,它也解释了为什么这个世界上如此多的工艺品只能做到99%的好,而不会100%的完美无缺。 (我们的某位建筑商倒是在不停地鼓噪亚利桑那州那些非常非常昂贵的房子,号称那里的每一颗螺钉都排得笔管条直。)
这些可以归结为软件的一种被多数人视作手艺的特性。 如果是真正的手艺人打造的软件,那么所有的螺钉都会排得笔直。 即使你做了什么不同寻常的事,应用软件也能做出智能回应。 更多的精力投放在让那些罕见情形也能得到准确回应上,而为了让主体代码管用只花费了很少的投入——为了处理好那1%的罕见情形,即便额外投入500%的精力也在所不惜。
当然,手艺会金贵得难以置信。 唯一能追求高超手艺的机会是,所开发的软件面向着最为广大的群众。 遗憾的是,保险公司里开发的HR(Human Resource, 人力资源)内部软件永远达不到这么高的工艺水准,因为那里就是没有足够多的用户以摊薄成本。 不过,对于一家精致小巧的软件公司来说,恰恰是这种高超手艺才能赢得用户的欢心,并带来持久不衰的竞争优势,因此,我会去花时间,会去把它做好。 请给我一点耐心吧!