十六、人员技能
我想想来谈谈我为CityDesk 3.0重写的一段程序:档案汇入程序。(广告:CityDesk是本公 司出品的很容易用的内容管理产品。)
它的规格似乎和任何一段程序代码一样简单。使用者用标准对话盒选择一个档案,然后程序 将该档案复制到CityDesk的数据库中。 结果却变成「最后1%程序用掉90%时间」的最佳范例。最早一版的程序结构大概是这样子的:
- 开档案
- 把内容全部读进来,放在一个大字节数组里
- 把字节数组放在一个记录中
效果不错。大小合理的档案几乎是一瞬间就做好了。它有几个小问题,我们一个一个来看。
当我把一个120MB的档案丢进CityDesk时跑出第一个问题。通常人们不太会在网站上放个120 MB的档案。事实上应该是少之又少才对。不过虽然少但还是有可能。程序可以用,不过等了 一分钟而且画面上完全没有反应-程序就这样定住不动,看起来好像完全当掉了。这显然 并不太理想。
由使用接口的观点来看,我真正要的是在长时间处理时出现一个进度棒之类的东西再加上一 个取消钮。理想的状况下,你可以在CityDesk里继续其他操作,档案复制的作业会在背景执 行。这有三种显而易见的方法可以达成:
- 只用一个线程,由该线程经常轮询输入事件
- 启用第二个线程,然后小心的进行同步
- 启用第二个行程,然后不必那么小心的进行同步
我的经验是第一种作法行不通。要确定应用程序里所有程序代码都能在复制档案时安全的执 行,实在是太困难了。另外Eric S. Raymond也让我相信,以解决方案来说线程通常不如单 独的行程好:事实上多年的经验显示,多线程程序设计产生了很多额外的复杂性,而且会引 入全新种类的危险海森堡虫(heisenbugs)。第三项似乎是个好答案,尤其当我们下层用的是 多用户的数据库,不用怕很多行程同时使用。所以我打算等感恩节休假结束就用第三种作法。
不过整体想想看就会发现,我们己经由读文件写作数据库变成复杂许多的东西:启动一个子 行程,让它读文件写入数据库,这个子行程还要有一个进度棒和取消钮,最后还要某种机制 让子行程通知母行程,档案己经处理完可以显示..其他的工作还包括将命令行参数传给子行 程,确定窗口焦点的行为正确,还要能处理使用者在复制档案时关机的状况。我猜等把这些 都做好,大概要十倍的程序才能优雅地处理大档案,而这可能只是用户所看到的程序的百分 之一。
当然会有某类型的程序员出来争论,说我用子行程的新架构比原本的差。这种作法「过度膨胀」(由于那些额外増加的程序代码)。另外潜在的问题也比较多(一样由于是多加的程序代 码)。这个作法做太过头了。这某种程度也表示Windows的确是个烂操作系统。他们会说这和 进度棒有什么关系?他们会嘲笑着说:只要按Ct「l+Z再重复打”Is -I”看看档案大小有没有 増加就好啦!
这个故事的教训是修正一个1%的问题可能会用掉500%的工夫。这种状况并不只软件业会发生。 敢这样讲是因为我现在就在管理这些建筑工程。上星期我们的包商终于完成Fog Creek新办 公室的最后修饰工程。内容包括在前门安装闪亮的蓝色压克力,旁边围着铝框,铝框上每20 公分就镶有一颗螺丝。如果你仔细看照片,会发现每片门都会被铝框整个包住,当门关起来 时,两片直框会并排在一起。虽然图上看不出来,不过中间铝条上的螺丝几乎但没有确实对 齐。差了大概有2公厘。负责这件事的木匠很小心的测量,不过装铝框时门是放在地上,并 不是等好门装好再当场装的。所以等到要把门装上去时,才发现螺丝并没有对齐。
这或许并不罕见;;我们办公室里很多门的螺丝都没有对的很好。问题是当洞钻好之后,要重 新修正的代价贵得离谱。由于正确的螺丝位置离原来的洞只有几公厘,所以不能直接在门上 再钻一个,可能得把整个门换掉才能解决。这实在是很不值得。这正是另一个修正1%的问题 会用掉500%工夫的例子,而且这也解释了为何这世上很多人造物都是99%好而非100%完美。 (不过建筑师倒是不断吹墟,说某些亚历桑那州的高贵金屋里每根螺丝都排得很整齐。)
这点出了软件的一项特性,一项大部份人都视为工匠技艺的特性。当软件是由真正的工匠制 作时,所有螺丝都会对得整整齐齐..当你做些很罕见的事时,应用程序会表现得很聪明。作 者会投入更多的工夫去正确处理特的状况,而不光是让主要的程序会动。即使要额外再花500% 的力气去处理1%的状况也在所不惜。
工匠技艺当然是非常昂贵的。唯一负担得起的方法就是针对大量的客户开发软件。很抱 歉,不过保险公司开发的内部人事管理程序绝对不可能达到这种工艺的境界,因为就是没有 足够的使用者来分散额外的成本。不过就软件包公司而言,这种工艺境界可以让使用者高兴 并提供长期的竞争优势,所以我愿意花时间并正确地执行。请容忍我吧。