英文原文:The Joel Test: 12 Steps to Better Code
你听说过 SEMA 么?它是一个用来测试一个软件团队有多好的相当深奥的系统。不,等等!不要手贱点开这个链接!它会花费你大概六年的时间来了解这个东西。所以我提出了我自己的、跟它相比极不负责任的、草率的评价一个软件团队的质量的测试。这个测试最棒的方面是它只会花费你 3 分钟的时间。你节省下来的所有时间,还可以去上个医学院。
Joel 测试
Joel 测试的好处是很容易快速得出针对每一个问题的“是”或“不是”。你不必去翻出那些每日编程行数和每个拐点的平均 bug 数。如果你的团队有一个“是”就得一分。关于 Joel 测试令人失望的是,你真的不应该用它来确保你的核电站软件的安全。
获得 12 分是完美的,11 分也还可以容忍,但 10 分或更低的分数表明你有严重的问题。事实上,大多数软件企业都以 2 分或 3 分的分数在运转着,他们真的很需要帮助,因为像微软这样的公司一直以来都以 12 分的完美表现在运转。
当然,这些都不是决定成败的唯一因素:特别是当你有一个正在开发没人要的产品的伟大的软件团队的时候,那么,人们是真的不会接受这个产品的。同时一个没有这么做的“神枪手”仍然能产生出令人难以置信的改变世界的软件也是可能存在的。但是,在其他条件相同的情况下,如果你把这 12 件事情都做好了,你就会拥有一个能始终如一完成任务的团队。
1. 你使用源代码管理么?
我用过商业源代码管理包,也用过 CVS,它是免费的,让我来告诉你,CVS 很好用。但如果你没有对源代码进行管理,你就要应激尝试把程序员都弄到一块来工作。程序员根本不会知道别人都做了什么。犯过的错误不能轻易改过来。关于源代码管理系统的另一个好处就是源代码本身可以在每个程序员的硬盘上进行验证---我还从没听说过哪个使用源代码管理的项目丢失了很多代码。
2. 你能在一步之内编译程序么?
通过这条测试我想明白:从最新的源代码的快速复制到进行能输出的编译需要多少步骤?再优秀的团队里,有一个单独的脚本,它能从零开始对代码做一个全面的检查,重新编译每一行代码,生成 EXE 文件,在他们各种各样的版本、编程语言和#ifdef 宏定义组合,创建安装包和最终媒体--CDROM 布局、下载网站等等。
如果这个过程需要一个以上的步骤,就很容易出现误差。当你接近完工的时候,你很想有很快的修复“最后一个”bug 的周期,生成最后的 EXE 文件等。如果编译代码要用 20 步才能完成,运行安装编译器,……,等等,你会抓狂的,并且会导致你犯下愚蠢的错误。
正式由于这个原因,我曾工作过的上个公司就从“聪敏”模式切换到了“软件安装打包”模式:我们要求安装过程中可以运行,使用 NT 调度从脚本整晚自动地运行,“聪明”模式做不到这些,所以我们就抛弃了这个模式。
3. 你每天都编译代码么?
当你使用源代码管理的时候,有时候程序员偶然会检查出会停止编译的东西。比如,他们添加了一个源文件,一切在他们的机器上编译起来都很好,但他们忘记把源文件添加到代码库里了。所以他们锁定了机器就回家去了,比较健忘也比较高兴。但其他人都不能继续工作了,所以他们也不得不很不愉快地卷铺盖回家了。
打断编译是很糟糕的(也很常见的),但它能帮助程序员每天都编译代码,以确保不会出现没有预兆的编译中断。在大型团队里面,一个很好的确保中断能迅速修复的方法是每天下午都对代码进行编译,比如可以在午饭时间做这个。每个人都尽可能多地在午饭前做代码检查。这样当他们吃完午饭回来的时候,这个编译就已经完成了。如果它能用,就太好了!每个人都对最新的源代码进行检查,然后才继续工作。如果编译失败了,你就修复它,但每个人还能在预编译、没有中断版本的源代码上继续工作。
在 Excel 项目组,我们有一条规则:谁中断了编译,作为对他的“惩罚”,他就要在其他人中断编译之前临时照顾代码编译的工作。这是一个很好的不中断编译的激励方式,也是一个让每个人轮流参与编译工作从而都能知道编译原理的好方法。
4. 你有 bug 数据库么?
我不在乎你怎么说。如果你在开发代码,即使是在一个人的团队,没有一个组织的列出代码里所有已知 bug 的数据库,你将会产生出低质量代码。许多程序员都认为他们能把众多的 bud 存在脑子里。真是胡说八道。我根本就不能再同一时间记住两个或三个 bug,第二天早上,或者在写代码的高峰时期,就记不起它们了。你绝对要正式地跟踪 bug。
但数据库可以是复杂或简单的。一个最小限度的 bug 数据库必须包含以下对每个 bug 的数据:
如果 bug 跟踪软件的复杂性是让你不想跟踪你的 bug 的唯一理由,只用上面包含简单的 5 个元素的表格用在这些至关重要的领域,开始使用它吧。
5. 你在写新代码前会修复 bug 么?
第一个版本的 Windows 系统上的微软 Word 被认为是一个“死亡行军”项目。不知道这项目要什么时候才能完成,它不断的延期。整个项目组在荒谬的时间里工作着,项目再次、再次、再次延期,这时候的压力的令人难以置信的。当讨厌的事情最终出货,几年以后,微软把这整个团队都送到坎昆去度假了,他们可以静下来做些严重的自我反省了。
他们意识到,项目经理一直在坚持按照“日程安排”部署工作,而程序员们只是头脑简单的赶紧完成敲代码的过程,又因为修复 bug 阶段并没有成为正式日程安排的一部分,这导致他们写出了及其恶劣的代码。没有能试图保持住 bug 不发作的倒计时。恰恰相反,据说一个程序员写了计算文本行高的代码,仅仅写了“renturn 12;”然后就开始等待关于他的功能总是正确的 bug 报道。日程安排仅仅是即将变为 bug 的功能的检查清单。事后,这个被称为“无穷大缺陷的方法”。
为了解决这个问题,微软普遍地采取了一种叫做“零缺陷方法”的东西。公司的许多程序员都咯咯地笑,因为它听起来是一种好像通过行政法令就能够减少 bug 数量的管理思想。其实,“零缺陷”意味着在任意给定的时间内,优先级最高的是消除 bug,然后才是编写新代码。让我来讲讲为什么。
一般情况下,你等待修复 bug 的时间越长,这个 bug 就需要付出的代价就越大(在实践和金钱上)。
比如,当你犯了一个编译器能捕捉的拼写或语法错误时,修复它的代价微不足道。
当你第一时间看到你尝试运行的代码里有 bug 的时候,你能够很快的把它解决掉,因为所以的代码在你脑子里印象还很清楚。
如果你在几天前的代码里发现了 bug,你会花费一段时间来找到它,但当你重读先前写下的代码后,你就会记起一切然后就能在一个合理的时间内修复这个 bug。
但如果你在几个月前的代码里发现了 bug,你很可能把关于这段代码的大部分东西都忘了,要修复它就很难了。这个时候你可能正在修复别人代码里的 bug,他们可能会在阿鲁巴岛度假,这种情况下,修复 bug 就像科学一样:你不得不放缓步调、有条不紊、细致地开始工作,并且你还不能确定需要多长时间才能找到问题的治疗方法。
如果你在已经售出的代码中发现了 bug,你会招致令人难以置信的代价修复它。
这是要立刻修复 bug 的一个原因:因为这样花费更少的时间。这关系到写新代码之前而不是修复 bug 之前还要等多长时间。比如,如果我要你预测写一个给列表排序的代买要多长时间,你可以给我一个很好的估计值。但如果我要你预测修复一个安装 Explorer 5.5 版本后代码就不能工作的 bug 需要多长时间,你猜都猜不出来,因为根本不知道到底什么导致了这个 bug。它可能需要 3 天时间才能跟踪到,或者仅仅 3 分钟就足够了。
这句话的意思是,如果你有一个很多 bug 有待修复的日程安排,这个日程是不靠谱的。但如果你修复了所有已知的 bug,并且所有剩下的都是新代码,这样你的日程安排才会更加惊人的精确。
关于把 bug 控制到零还有另一件重要的事,那就是你可以对竞争响应更快。一些程序员认为这点能让产品在任何时候为发售准备好。如果你的竞争对手从你的客户那里引入了一个杀手级的新功能,你就能在发售之前只实现这个功能,而不必去修复大量累计下来的 bug。
6. 你有最新的日程安排么?
这条测试把我们带到了日程安排上来。如果你的代码对生意是很重要的,会有很多知道代码完成日期怎么对生意很重要的原因。程序员在制定日程安排上是出了名的倔。“该完成的时候就完成了!”他们会这样对商务人士尖叫。
不幸的是,这并没有让一切变得更好。在发售代码之前,公司需要做太多的计划好的决定:软件演示,展会,广告等。而要做到这一点的唯一方法就是拥有一个日程安排,并保证其为最新版本。
关于要有一个日程安排的另一个至关重要的原因是,它能强迫你决定要做哪些功能,然后迫使你挑选出最无关紧要的功能并砍掉它们,而不是陷入长期的犹豫中去。
同时,跟随日程安排做事并不一定要很苛刻。
7. 你写代码有规范么?
书写规范就像牙线,每个人都同意这是一个很好的事情,但却没人做。
我不知道这是为什么,但很可能是因为大多数程序员讨厌写文档。结果,对一个大部分有程序员组成的团队遇到了一个难题的时候,他们更倾向于用代码来表达他们的解决办法,而不是用文档。他们宁愿埋头写代码而不是先写一份规范。
在设计阶段,当你发现问题时,你可以很容易地通过编辑几行文本就修复它。一旦开始写代码,修复问题的代价就大大地提高了,无论在感情上(人们讨厌扔掉代码)还是在时间上,所以这时候修复问题是有阻力的。不是从规范开始建立起来的软件通常会很糟糕地结束设计,并且日程安排会不受控。这个在 Netscape 好像已经成为大问题了,Netscape 的前四个版本慢慢变得一团糟,然后管理层愚蠢地决定抛弃旧代码再重新开始。然后他们又在 Mozilla 上再一次犯了这个错误,创建了一个失控的怪物,并且浪费了好几年时间才又回到初始阶段。
我的一贯主张是,这问题可以通过把程序员送去学习写作的集中课程,把他们变为差不多的写手来解决。另一个方案是雇佣一些聪明的程序管理人员,让他们来写代码规范。在这两种情况下,你应该执行简单的规则“无规范不出代码”。
8. 程序员有安静的工作环境么?
广泛的记录表明,通过给知识型员工提供空间、安静和隐私就能提高生产力。经典的软件管理书《人件》就广泛地记录了生产力受益于这些方面。
问题来了。我们都知道知识型员工随着“灵感流动”工作最好,就是我们所说的“进入状态”,在哪里他们会全身心专注于他们的工作,并且完全脱离了周围的环境。通过绝对的专注,他们忘记了时间,产生出伟大的代码。这是他们把工作完成的过程。作家、程序员、科学家,甚至篮球运动员都会告诉你要进入状态。
问题是,进入状态并不那么容易。当你尝试去考量它的时候,在最大生产力下好像需要 15 分钟才能开始工作。有时,如果你累了或那天已做了很多创造性工作时,你就是进入不了状态,你会把这天剩下的时间都用来摆弄点什么,看看网页,或玩玩俄罗斯方块。
另一个问题是,很容易脱离那个状态。噪声,来电话,出去吃午饭,不得不开 5 分钟车去星巴克喝咖啡,还有被同事打扰--尤其是被同事打扰--都会把你从那个状态里拉出来。如果同事问你问题,导致了一分钟的中断,但这个会很悲惨地把你从状态里脱离出来,你的话费半个小时才能再次变的高效起来,你的整体生产力会遇到很大的麻烦。如果你在一个含咖啡因的网络公司喜欢创造的嘈杂的牛棚一样的环境里,有营销人员在程序员身边尖叫着打电话,你的工作效率会大幅下跌,因为知识型工作者一次又一次的被打断,一直都进入不了状态。
对程序员来说,这就更难了。工作效率依赖于能够同时在短期记忆中兼顾很多小细节。任何类型的打断都会导致这些细节轰然倒下。当你重新开始工作,你已经记不起任何细节(比如你刚才还在使用的本地变量名字,或你刚想出来的实施搜索算法的好点子)了,你不得不一直回想这些事情,这会让你变得很慢,直到你重新变得高效起来。
这有一个简单的代数运算。可以这么说(有证据暗示)如果我们打断了一个程序员,即使只有一分钟,我们真的会失去 15 分钟的工作效率。在这个例子里,我们假设有两个程序员,Jeff 和 Mutt,在一个标准的相邻开放的格子间里。Mutt 记不起 strcpy 函数的 Unicode 版本的名字。他可以查一查,这需要 30 秒,或者他可以问问 Jeff,这需要 15 秒。因为他就坐在 Jeff 旁边,所以他选择直接问 Jeff。Jeff 被分心了,失去了 15 分钟的工作效率(仅仅节省了 Mutt 的 15 秒)。
现在我们把他们两个分到有门和墙隔开的两个办公室去。这时如果 Mutt 忘记那个函数名,他可以花 30 秒去查一查,或者花 45 秒去问问 Jeff,这过程包括了站立起来(考虑到程序员的平均体能这并不是一项简单的任务!)。所以他会选择自己查一查。这样 Mutt 失去了 30 秒的工作效率,但同时为 Jeff 节省了 15 分钟。哈哈哈哈!
9. 你使用钱能买到的最棒的工具么?
在公园里使用家用电脑立即用一门编译语言写代码仍然是最不能做的事情之一。如果你的编译过程超过几秒钟,使用最新和性能最强的电脑会让你节省点时间,当编译器运行的时候,程序员会感到厌倦,这是他们会切换到阅读点别的书籍,这会吸引他们的注意力,失去好几个小时的工作效率。
使用单个显示器调试 GUI 代码是很痛苦的,这也不是不可能。如果你在写 GUI 代码,两台显示器会让很多事情变得更加容易。
大多数程序员最终要处理图标或工具栏的位图,但他们大多数都没有一个好用的位图编辑器。尝试用 Microsoft Paint 来处理位图是个笑话,但这就是大部分程序员们所要做的。
在我上一份工作中,系统管理员一直给我发自动的垃圾邮件抱怨,说我用了超过 220 兆字节的服务器上的硬盘存贮空间。我指出,鉴于最近硬盘的价格,这些硬盘空间的成本比我使用的卫生纸的成本都低多了。甚至花费我 10 分钟来清理我的邮件目录,这真是对我工作效率的极为荒诞的浪费。
顶尖的开发团队不会折磨他们的程序员。即使是因为功能不完善的工具引起的小挫折累加起来,也会使程序员脾气暴躁和不愉快。一个脾气暴躁的程序员是不会有工作效率的。
程序员最容易接受最酷、最新的东西贿赂了。与支付有竞争力的薪水比起来,这是一种让他们为你工作更便宜的方式。
10. 你有测试人员么?
如果你的团队没有专门的测试人员,至少应该为两三个程序员就配一个测试人员,你会写出有很多 bug 的产品,或者你在花冤枉钱让测试人员 30 刀每小时就能做的工作交给 100 刀每小时的程序员来做。在测试人员身上节省下来的钱是一个离谱的虚假的经济,我只是想让更多的人认识到这一点。
11. 来找工作的人在他们面试的时候会写代码么?
你会雇佣一个没看过他魔术技巧的魔术师么?当然不会。
你会雇佣一个没尝过他的食物的餐饮服务商来为你的婚礼服务么?我对此表示怀疑。
然而,一天天的,程序员通过让人印象深刻的简历被雇用,或是因为面试者喜欢跟他们聊天。或者他们被问到很细的问题(“CreateDialog()和 DialogBox()之间有啥不同?”),这些看文档就能回答了。你不关心他们是否记得关于编程的成千上万的细节,你只关心他们到底能不能写出代码。或者,更糟糕的是,他们被问到的都是“啊?”问题:就是那种你知道答案就看起来很简单,但如果不知道答案就什么也回答不上来的问题。
拜托,以后不要这么干了。在面试期间你想怎么问就怎么问,但一定要让参加招聘的程序员写点代码。
12. 你会做走廊可用性测试么?
走廊可用性测试就是当你在走廊上遇到一个路人就强迫他试着用你刚写的代码。如果你对五个人做过这种事,你会学到你代码里需要学习的 95% 的实用性问题的答案。
好的用户界面设计并不像你想的那么难,如果你想让用户喜欢并购买你的产品这就至关重要了。
但关于用户界面最重要的事情是,如果你把你的程序展示给很多人看(实际上五六个就足够了),你就会发现人们存在的最大的问题。即使你的用户界面设计技术还不怎么样,只要你强迫自己去做走廊可用性测试,这并没什么代价,而你的用户界面会越来越好的。