软件开发是一门手艺活

来源:互联网 发布:mobi 阅读器 for mac 编辑:程序博客网 时间:2024/04/28 21:52

开发软件并不像是工厂在制造产品。20世纪80年代,大家惊闻日本在建造“软件工厂”,这些工厂能通过流水线作业批量生产高质量的软件。而这在当时的技术水平下是天方夜谭,即使是现在的技术也还达不到。把一群程序员塞进车间,让他们站成一排,并不能有效地减少bug数量。

如果写代码不同于流水线作业,那它更像什么呢?有人提出,软件开发是一门手艺活。当然,这种说法也并不总是成立的,因为不知你怎么想,反正我觉得那个询问你该如何索引帮助文件的Windows对话框,从形状到样式,无论从哪个角度看,都和人们常说的“工艺品”差远了。

写代码不是工业生产活动,也不总是一门手艺活(但它可以是),它其实更像是一种设计活动。设计是一个很模糊的概念,其特征在于用较慢的成本增长换取较快的价值增长。《纽约时报杂志》对iPod赞不绝口 ,称赞苹果公司是世间少有的知道如何用优秀设计来增加价值的公司。

图片由苹果公司提供,详情参见:http://www.apple.com/pr/photos/ipod/03ipod.html
但是,关于设计这个话题我已经讲了很多,现在想谈谈软件开发的工艺品特性,包括它是什么以及如何辨识。 我想和大家分享一段我正在重写的代码,它实现的是CityDesk 3.0的文件导入功能。(插播一条广告:CityDesk是我的公司发布的一款方便易用的内容管理产品。)

功能规格说明非常简单。用户通过标准的对话框选择一个文件,然后程序把该文件复制到CityDesk数据库中。

结果这个例子很好地说明了,有的时候“写好最终1%的代码要耗去90%的时间”。程序的第一版设计草案是这样的:

(1) 打开文件;

(2) 把全部内容装进一个庞大的字节数组;

(3) 把字节数组储存为数据表中的一个条目。

这个程序在通常情况下运行得很顺畅。对于一般大小的文件,程序基本上一眨眼的工夫就跑完了。但是它存在几个小bug,容我一一列举。

其中一个较大bug出现在压力测试中,当时我试着把一个120MB的文件拖入CityDesk。一般情况下,人们不会在网站上放这么大的文件。这在实际中几乎不可能出现,但也不是没有可能。程序运行了大概一分钟才结束,其间没有任何视觉反馈,界面呈现假死状态,点击哪里都没有反应。这明显不够理想。

从用户界面设计的角度来考虑,我应该在程序进行耗时操作的时候显示某种进度条,以及一个取消按钮。更理想的情况是让文件在CityDesk的后台进行复制,不影响用户进行其他的操作。要实现这一功能,比较容易想到的方案有三种:

(1) 只开一个线程,每隔一段时间检查是否有输入事件;

(2) 再开一个线程,仔细地做好线程同步;

(3) 再开一个进程,不需要很仔细地做好进程同步。

我的经验是,方法1的效果总是不够理想。当程序正在进行文件复制操作时,很难保证其他所有代码能安全运行。埃里克•雷蒙德的论述让我相信,多线程的方案不如多进程的方案 ,而且根据我多年的经验,多线程编程增加了太多额外的复杂度,会引入不少这种方案所独有的、危险的海森堡bug 。3号方案看起来更好,尤其是考虑到我们使用的数据库支持多用户操作,并不介意多进程的并发访问。所以过了这个感恩节假期,我准备用方案3来实现这个功能。

但是退一步想,我们从最初简单的读取文件、保存到数据库出发,最终采取了一个复杂得多的方案:开一个子进程,让它读取文件并保存到数据库;子进程还要有进度条和取消按钮; 并且要建立一种机制,让子进程能在文件保存完毕时通知父进程,即时显示处理完毕的文件。另外要做的一部分工作是通过命令行把参数传递到子进程;还要处理窗口焦点的行为,让它符合使用者的心理预期;以及特殊情况的处理,比如用户在程序正在复制文件的时候关闭电脑。我猜最后要写的代码量是原来的10倍,只是为了优雅地处理大文件,而这些工作的成果只有大概1%的用户能看到。 当然了,还有一种程序员会认为,子进程的方案还不如原来的方案。它被“过度设计”了,增加了太多额外的代码行,这也导致程序更有可能出现错误。这叫过犹不及。他们会说,这种做法是是典型的Windows思维,Windows在他们眼中并不是一个设计得很高明的操作系统。他们对设计进度条的必要性嗤之以鼻。因为在UNIX下,只要敲击Ctrl+Z然后用“ls -l”看看文件体积有没有增加就知道程序是否还在运行了!

这个故事告诉我们,修复出现概率为1%的问题,有时候需要花500%的精力。这种情形并不是软件开发活动所独有的,我这样说是因为我管理过一些建筑施工工程。上周,我们的建筑承包商最终完成了对Fog Creek新办公楼 的装修完善工作,包括给正门装上崭新的蓝色丙烯面板,并在门的边缘每隔20厘米包上一条铝片。仔细看下面这张照片,你会发现每扇门的四周都有铝条。在两扇门接触的部分,两条铝片呈竖向对齐排列。在图上可能看得不明显,但这两条铝片中央的固定螺丝并没有完全对齐,大约相差2毫米。木工师傅事先做了仔细测算,但是在安装铝条的时候,门是放在地上的。等到门挂到轴上才发现,坏了,螺丝很明显地没有对齐。

这种情况绝非个例,我们的办公室里有很多螺丝不是完全对齐的。问题在于打完孔后再矫正位置的成本是很高的。正确的位置只偏那么几毫米,因此不好重新打孔。如果追求完美的话,只能换掉整扇门,太不值了。这个例子再一次阐释了有时候修复出现概率为1%的缺陷需要500%的努力,也同样揭示了为什么这个世界上有那么多工艺品的完美程度停留在99%,而不是100%。(我们的建筑师总是喋喋不休,声称亚利桑那州的某些豪宅中,每颗螺丝都是完全对齐的。)

大部分人都认为,软件具有与之类似的特性,让它带有工艺品的气息。当一群真正具有工匠情怀的人们构建一个软件产品时,他们会想办法对齐所有的螺丝。也就是说对于一些极其罕见的情况,软件也能处理得很漂亮。但要做到这一点,开发者势必将大部分的精力投入到对各种罕见情况的正确处理上,而不是集中精力让主要的业务逻辑正常运转。这也就意味着开发者要用500%的精力去处理好出现概率为1%的情况。

要达到一定的工艺水平,需要付出巨额的开发成本。软件行业里唯一值得付出巨额的开发成本的情形,是开发面向海量用户的软件。很遗憾,譬如保险公司的内部人力资源系统,就永远不会达到这种完美的工艺水平,因为没有足够的用户数量来摊薄成本。然而对于开发零售软件的公司,这种不断追求并完善软件品质的做法正是用户想看到的,因为那将会为公司提供长期的竞争优势,所以请广大用户耐心期待,我们会慢慢来,因为慢工才能出细活。

封面

摘自《软件随想录 卷1》

0 0