Skip to main content

The Clean Coder《代码整洁之道:程序员的职业素养》

罗伯特 C. 马丁

译者序 1:享受职业素养

在你过去的工作中,遭遇过哪些印象深刻的困难,最后是怎么解决的?

你遇到的问题可能很容易也可能很难,但我看重的并不是问题的难度,而是解决问题的方式、步骤以及反思的程度。

技术人员往往太容易说“是”,总是在没有明确目标和期限的情况下,就草率给出了确认的答复,却不将其视为自己的承诺。屡见不鲜的项目延期,有相当原因就是在这种不负责任的情况下说“是”所致。

之所以会搞砸,因为开发人员没有坚决抵制各种不专业的需求(比如一些无关紧要但成本巨大的需求),抵制各种不专业的行为(比如为了赶工期而降低对程序质量的要求)

有时候,获取正确决策的唯一途径,便是勇敢无畏地说出“不”字

花三分的力气去抵制无理的需求,可以节省十分甚至二十分的开发时间

约定共同认可的验收测试标准,并在开发过程中保持沟通。

我曾经尝试在与业务部门确定目标原型之后,要求对方指派对接人在 IT 部坐班,负责协商、跟进整个开发流程,确认每一点修改。这样既保证最终结果符合业务部门的需求,又提高了开发人员的工作效率,综合来看成效非常显著。

译者序 2:负阴抱阳,知行合一

美的东西比丑的东西创建起来更廉价,也更快捷。

构建、维护一个美的软件系统所花费的时间、金钱都要少于丑的系统。

……美的系统是灵活、易于理解的,构建、维护它们就是一种快乐

技术人员不再找借口拖延,而是勇担重任

不再推卸估算工作或置身事外让其他人来做计划(然后对计划抱怨不休),而是真正做到了自组织,并做出了郑重承诺。

当程序员因为运维方面的问题受阻时,他们会打电话给系统管理员,之后,系统管理员就会马上着手清除障碍。

技术人员现在不会说“我们尽力而为吧”,而会代之以“这是我们的承诺; 如果你想调整目标,请随时联系我们”。

前言

在本书的实务性意见背后,隐隐体现出一种奋力突破的积极态度

身为一名工程师,你比任何管理者可能都了解得更透彻。了解这些也意味着你肩负着要敢于行动的重大责任。

第 1 章 专业主义

“专业主义”有很深的含义,它不但象征着荣誉与骄傲,而且明确意味着责任与义务

实际上,专业主义的精髓就在于将公司利益视同个人利益。 看到了吧,“专业主义”就意味着担当责任。

我曾因不负责任尝尽了苦头,所以明白尽职尽责的重要意义。

我其实是想告诉你,要对自己的不完美负责。 代码中难免会出现 bug,但这并不意味着你不用对它们负责; 没人能写出完美的软件,但这并不表示你不用对不完美负责。

所谓专业人士,就是能对自己犯下的错误负责的人,哪怕那些错误实际上在所难免。

雄心勃勃的专业人士们,你们要练习的第一件事就是“道歉”。

你不能一而再、再而三地犯相同的错误。 职业经验多了之后,你的失误率应该快速减少,甚至渐近于零。 失误率永远不可能等于零,但你有责任让它无限接近零。

发布软件时,你应该确保 QA 找不出任何问题。

有些家伙会把 QA 当作啄木鸟看待。 他们把自己没有全盘检查过的代码发送过去,想等 QA 找出 bug 再反馈回来。

你怎么知道代码能否常运行呢? 很简单,测试! 一遍遍地测,翻来覆去、颠来倒去地测,使出浑身解数来测!

如果不停地花时间做测试,你就没时间写别的代码了。 言之有理!所以要实行自动化测试。 写一些随时都能运行的单元测试,然后尽可能多地执行这些测试。

要用这些自动化单元测试去测多少代码呢? 还要说吗?全部!全部都要测!

我是在建议进行百分百测试覆盖吗? 不,我不是在建议,我是在要求! 你写的每一行代码都要测试。完毕!

唯一的解决办法就是要设计易于测试的代码,最好是先写测试,再写要测的代码。

当然,也不排除有些系统因其任务极其关键特殊,不能只靠简短的自动化测试来判断软件是否已经足够高质量,是否可以投入使用。 而且,作为开发人员,你需要有个相对迅捷可靠的机制,以此判断所写的代码可否正常工作,并且不会干扰系统的其他部分。 因此,你的自动化测试至少要能够让你知道,你的系统很有可能通过 QA 的测试。

所有软件项目的根本指导原则是,软件要易于修改。 如果违背这条原则搭建僵化的结构,就破坏了构筑整个行业的经济模型。

简言之,你必须能让修改不必花太高代价就可以完成。

如果你希望自己的软件灵活可变,那就应该时常修改它!

要想证明软件易于修改,唯一办法就是做些实际的修改。 如果发现这些改动并不像你预想的那样简单,你便应该改进设计,使后续修改变简单。

该在什么时候做这些简单的小修改呢? 随时! 关注哪个模块,就对它做点简单的修改来改进结构。 每次通读代码的时候,也可以不时调整一下结构。

这一策略有时也叫“无情重构”。

对每个模块,每检入一次代码,就要让它比上次检出时变得更为简洁。

每次读代码,都别忘了进行点滴的改善。

这完全与大多数人对软件的理解相反。 他们认为对上线运行的软件不断地做修改是危险的。 错!让软件保持固定不变才是危险的! 如果一直不重构代码,等到最后不得不重构时,你就会发现代码已经“僵化了”。

如果你有一套覆盖了全部代码的自动化测试,如果那套测试可以随时快速执行,那么你根本不会害怕修改代码。

专业开发人员对自己的代码和测试极有把握,他们会极其疯狂随意地做各种修改。

你应该计划每周工作 60 小时。 前 40 小时是给雇主的,后 20 小时是给自己的。 在这剩余的 20 小时里,你应该看书、练习、学习,或者做其他能提升职业能力的事情。

确实,行业正迅猛发展,而有趣的是,从多个方面来看,这种进展都只是很浅层的。

我们不再需要为拿到编译结果苦等上 24 小时,我们也已经可以写出 GB 级别的系统,我们置身覆盖全球的网络之中,各种信息唾手可得。 但另一方面,我们还是跟 50 年前一样,写着各种 ifwhile 语句。所以,改变说多也多,说少也少。

总的来说,那些在过去 50 年中来之不易的理念,绝大部分在今天仍像过去一样富有价值,甚至宝贵了。

“不能铭记过去的人,注定要重蹈覆辙。”

下面列出了每个专业软件开发人员必须精通的事项:

  • 设计模式 必须能描述 GOF 书中的全部 24 种模式,同时还要有 POSA 书(这本书有点老了)中的多数模式的实战经验。

  • 设计原则 必须了解 SOLID 原则,而且要深刻理解组件设计原则。

  • 方法 必须理解 XP、Scrum、精益、看板、瀑布、结构化分析及结构化设计等。

  • 实践 必须掌握测试驱动开发、面向对象设计、结构化编程、持续集成和结对编程。

  • 工件 必须了解如何使用 UML 图、DFD 图、结构图、Petri 网络图、状态迁移图表、流程图和决策表。

不写代码的架构师必然遭殃,他们很快会发现自己跟不上时代了; 不学习新语言的程序员同样会遭殃,他们只能眼睁睁看着软件业一路发展,把自己抛在后面; 学不会新规矩和新技术的开发人员更可怜,他们只能在日渐沦落的时候看着身边人越发优秀。

真正的专业人士往往勤学苦干,以求得自身技能的纯熟精炼。 只完成日常工作是不足以称为练习的,那只能算是种执行性质的操作,而不是练习。

那么软件开发者该怎样来不断训练自己呢? 我常用的一个技巧是重复做一些简单的练习,如“保龄球游戏”或“素数筛选”,我把这些练习叫作“卡塔”(kata)。 卡塔有很多类型。 练卡塔的目的不是找出解决方法(你已经知道方法了),而是训练你的手指和大脑。

专业软件开发人员往往会更加努力地尝试与他人一起编程、一起练习、一起设计、一起计划,这样他们可以从彼此身上学到很多东西,而且能在更短的时间内更高质量地完成更多工作。

最好的办法就是与你负责指导的人交流这些内容。 这样,传道授业的同时,导师也会从中受益。

让新人融入团队的最好办法是和他们坐到一起,向他们传授工作要诀。 专业人士会视辅导新人为己任,他们不会放任未经辅导的新手恣意妄为。

每位专业软件开发人员都有义务了解自己开发的解决方案所对应的业务领域。

如果编写旅游应用程序,那么你需要去了解旅游业。你未必需要成为该领域的专家,但你仍需要用功,付出相当的努力来认识业务领域。

开始一个新领域的项目时,应当读一两本该领域相关的书,要就该领域的基础架构与基本知识作客户和用户访谈,还应当花时间和业内专家交流,了解他们的原则与价值观念。

最糟糕、最不专业的做法是,简单按照规格说明来编写代码,但却对为什么那些业务需要那样的规格定义不求甚解。 相反,你应该对这一领域有所了解,能辨别、质疑规格说明书中的错误。

雇主的问题就是你的问题。你必须弄明白这些问题,并寻求最佳的解决方案。

开发人员之间互相认同是容易的,但把一方换成雇主,人们就容易产生“彼”“此”之分。 专业人士会尽全力避免这样的狭隘之见。

第 2 章 说“不”

“能就是能,不能就是不能。不要说‘试试看’。”

专业人士敢于说明真相而不屈从于权势。 专业人士有勇气对他们的经理说“不”。

不应该照做。只要你是一名专业人士,那就不应该照做。

奴隶没有权利说“不”。劳工或许也对说“不”有所顾虑。 但是专业人士应该懂得说“不”。事实上,优秀的经理人对于敢于说“不”的人,总是求贤若渴。因为只有敢于说“不”,才能真正做成一些事情。

我的经验是,“为什么”远不如“事实”重要。 事实是,“登录页面”还需要两周才能完成。 而为什么需要两周,则只是个细节。

如果 Mike 恰好有技术背景和好脾气去倾听理解,这些解释也许会有用。 另一种情况则是,Mike 可能会不认同 Paula 的结论,他可能会觉得 Paula 的做法不对,他可能会告诉她不用做完整的测试和代码审查,或者可以把第 1,2 步省略掉,诸如此类。 有时候,提供太多细节,只会招致更多的微观管理。

如果承诺尝试,你其实也在承诺将改变自己原来的方案。 你是在承认原来的方案中存在不足。 如果承诺尝试,你其实是在告诉他们,你有新方案。 新方案是什么? 你将对自己的行为做出哪些改变? 你说你在“尝试”,那么你的做法将会有何不同? 如果你既没有新方案,又不准备改变自己的行为,如果事事仍然都按你承诺“尝试”之前的方法去做,那么,所谓的“尝试”指的又是什么呢?

如果你此前并未有所保留,如果你没有新方案,如果你不会改变你的行为,如果你对自己原先的估计有充分的自信,那么,从本质上讲,承诺“尝试”就是一种不诚实的表现。 你在说谎。 你这么做的原因,可能是为了护住面子和避免冲突。

她在被施压修改进度预估时说“不”,在对方软硬兼施、连哄带求时仍坚持说“不”。 最重要的是,她对 Mike 的自欺欺人和不作为也大胆说“不”。 Paula 的这些举动都是出于团队整体的考虑,Mike 需要帮助,而她确实也竭尽所能来帮他。

编程经验: 大多数情况下,选用“组合”比“继承”要好。

多年的从业经验让我渐渐明白,是客户需求阻碍我写出自己想要的真正高品质的应用程序。

客户所要的任何一项功能,一旦写起来,总是远比它开始时所说的要复杂许多,但最终你还是会接下这些活。

客户总会把项目截止日期往后拖延。 他们总是想要更多的功能,他们总是提出需求变更——而且常在最后关头这么做。 而且往往客户公司的经理的人数越多,最后增加的天数越多。

尽管客户一再声明交付日期很重要,尽管他们对此表现得似乎非常迫切,但他们永远不会像你那样在乎应用程序的按时交付。

第 3 章 说“是”

做出承诺,包含三个步骤。 (1)口头上说自己将会去做。 (2)心里认真对待做出的承诺。 (3)真正付诸行动。

我们有竭力逃避承担责任的倾向。

识别真正承诺的诀窍在于,要去搜寻与下列相似的语句: 我将在……之前……(例如,我将在周二之前完成这个任务。) 这句话的关键在哪里呢? 你对自己将会做某件事做了清晰的事实陈述,而且还明确说明了完成期限。 那不是指别人,而是说的自己。 你谈的是自己会去做的一项行动,而且,你不是可能去做,或是可能做到,而是必须做到。

如果最终目标依赖于他人,那么你就应该采取些具体行动,接近最终目标。

有一点相当重要:如果你不尽早告诉他人可能的问题,就错失了让他们帮助你达成目标、兑现承诺的机会。

在碰到不确定的事件时,应该名确表达不确定的含义,如果可以的话,进一步该个合理的区间范围,给领导一个提前的心理预期。

Marge:“Peter,周五前你能完成对评价引擎的修改吗?” Peter:“可能可以,但也可能得到下周一。” Marge:“包括文档吗?” Peter:“写文档要再多花上几个小时,这样的话,有可能下周一可以完成,不过也可能会到下周二。”

这段对话里,Peter 的措辞更为实诚。 他清楚地向 Marge 表达了自己的不确定感。 Marge 或许能够应付得了这种不确定,但也可能无法接受。

这段对话里,Peter 的措辞更为实诚。 他清楚地向 Marge 表达了自己的不确定感。 Marge 或许能够应付得了这种不确定,但也可能无法接受。

专业人士不需要对所有请求都回答“是”。 不过,他们应该努力寻找创新的方法,尽可能做到有求必应。

第 4 章 编码

我发现,要精熟掌握每项技艺,关键都是要具备“信心”和“出错感知”能力。

如果感到疲劳或者心烦意乱,千万不要编码。 强而为之,最终只能再回头返工。

疲劳的时候,千万不要写代码。 奉献精神和职业素养,更多意义上指要遵循纪律原则而非成为长时间工作的工作狂。要确保自己已经将睡眠、健康和生活方式调整到最佳状况,这样才能做到在每天的 8 小时工作时间内全力以赴。

在办公室里花一个小时解决私人问题,是一件令人惭愧的事。 专业开发人员善于合理分配个人时间,以确保工作时间段中尽可能富有成效。

在家中时就应该专门安排时间去解决焦虑,这样就不会把焦虑情绪带到办公室里。

问题在于,在流态区状态下,你其实放弃了顾及全局,因此,你很可能会做出一些后来不得不推倒重来的决策。 在流态区写代码可能会快些,但是后面你将不得不更多地回头重新审视这些代码。

结对编程最大的一个好处在于,结对中的任一方都不可能进入流态区。 流态区是一种与世隔绝的状态,而结对则要求持续密切地进行沟通。

我经常听到关于结对编程的抱怨便是,结对会阻碍人们进入流态区。 很好!流态区正是要避免进入的状态。

有时候流态区正是你希望进入的状态。 这个时候,就是当你潜心练习的时候。

音乐并没有帮助我专注于编码。 事实上,听音乐似乎消耗了一部分宝贵的脑力资源,而这些资源本该用于编写设计良好的整洁代码。

结对是用以应对中断的一种好方法。 当你接答电话或回答其他同事的问题时,结对搭档能够维护住中断处的上下文。

另一种很有帮助的方法便是采用 TDD。失败的测试能帮你维护住编码进度的上下文。当处理完中断重新回去时,你很清楚下一步任务便是让这个失败的测试通过。

当然,中断无法避免,总有干扰会打断你、消耗你的时间。 发生这种情况时要记住一点,也许下次也会轮到你去打断别人请求帮助。 因此,礼貌地表现出乐于助人的态度才是专业的态度。

哪些原因会导致这些阻塞呢? 如果睡眠不足,我就什么代码也写不出来。 其他因素还包括焦虑、恐惧和沮丧等。

这个方法便是:找一个搭档结对编程。

出于某些原因,软件开发人员会认为调试时间并非编码时间。 他们认为存在调试时间是天经地义的,调试不等于编码。

之所以能够显著降低调试时间,是因为我采用了“测试驱动开发”这一实践,

不管是否采纳 TDD 或其他一些同等效果的实践,衡量你是否是一名专业人士的一个重要方面,便是看你是否能将调试时间尽量降到最低。 绝对的零调试时间是一个理想化的目标,无法达到,但要将之作为努力方向。

经常重新返工的医生或律师会被认为不专业。 同样,制造出许多 bug 的软件开发人员也不专业。

软件开发是一场马拉松,而不是短跑冲刺。 你无法全程一直以最快的速度冲刺来赢得比赛,只有通过保存体力和维持稳定节奏来取胜。

我也曾经在洗澡时解决了大量问题。 也许是清晨的水流能够将我彻底唤醒,使我可以深入盘点昨晚睡觉时大脑中浮现的所有解决方案。

埋头忙于解决问题时,有时候可能会由于和问题贴得太近,无法看清楚所有的可选项。 由于大脑中富有创造性的部分被紧张的专注力所抑制,你会错过很棒的解决方案。

管理延迟的诀窍,便是早期检测和保持透明。 最糟糕的情况是,你一直都在告诉每个人你会按时完成工作,到最后期限来临前你还在这样说,但最终你只能让他们大失所望。

使用三个考虑到多种因素的期限:乐观预估、标称预估、悲观预估。

把全部这三个数字呈现给团队和利益相关者,并每天修正这些数字。

其实快速冲刺是做不到的。 你无法更快地写完代码。 你无法更快地解决问题。 如果试图这么做,最终只会让自己变得更慢,同时也只能制造出一堆混乱,让其他人也慢下来。 因此,必须明白告诉老板、团队和利益相关方,让他们不要抱有这种期望。

不应该采用额外加班加点工作的方案,除非以下三个条件都能满足: (1)你个人能挤出这些时间; (2)短期加班,最多加班两周; (3)你的老板要有后备预案,以防万一加班措施失败了。

最后一条至为关键。 如果老板无法向你清楚说明加班方案失败的后备预案,那么你就不该同意接受加班方案。

在程序员所能表现的各种不专业行为中,最糟糕的是明知道还没有完成任务却宣称已经完成。

但是随着经验渐长,你会开始意识到把这些 ifwhile 语句组装在一起的方式十分重要。 不能期望将它们简单混在一起就能得到最好的代码。 相反,必须小心谨慎地将系统分解为易于理解的小单元,同时使这些单元之间的关系越少越好,这并非易事。

事实上,仅凭一己之力无法写出优秀的代码。 既使你的技能格外高超,也肯定能从另外一名程序员的思考与想法中获益。

互相帮助是每个程序员的职责所在。 将自己封闭在格子间或者办公室里与世隔绝,有悖于专业的职业精神。

事实上,作为专业人士,要以能够随时帮助别人为荣。

帮助别人的时候,你可以坐下来和他一起写代码,为此需预留出一个小时甚至更长的时间,当然实际也许没那么久,但是不要让自己看起来十分仓促,仿佛只是随便应付。 要全情投入到任务中。 当你离开时,可能会发现自己从中收获的东西比给予的还要多。

要记住,如同要以乐于助人为荣一样,也要以乐于接受别人的帮助为荣。

如果帮助唾手可得却让自己一个人堵在那儿,是很不专业的表现。

除了自身的内驱力和资深导师的有效辅导之外,没有东西能将一名年轻的软件开发人员更快地提升为敏捷高效的专业人士。 因此,再强调一次,花时间手把手地辅导年轻程序员是资深程序员的专业职责所在。

向资深导师寻求辅导也是年轻程序员的专业职责。

第 5 章 测试驱动开发

测试驱动开发”(TDD)自在行业中首次亮相,至今已经有十余年了。 它最早是极限编程(XP)运动的一部分,但此后已经被 Scrum 和几乎所有其他敏捷方法所采纳。 即使是非敏捷的团队也在实践 TDD。

结论很清楚,TDD 的确切实可行,并且,每个开发人员都要适应和掌握 TDD。

  1. 在编好失败单元测试之前,不要编写任何产品代码。
  2. 只要有一个单元测试失败了,就不要再写测试代码; 无法通过编译也是一种失败情况。
  3. 产品代码恰好能够让当前失败的单元测试成功通过即可,不要多写。

看到混乱的函数时, 你的第一反应是:“真是一团糟,这个函数需要整理。” 你的第二反应是:“我不会去碰它!” 为什么? 因为你知道,如果去动它,就要冒破坏它的风险; 而如果你破坏了它,那么它就缠上你了。

这是 TDD 最强大之处。 拥有一套值得信赖的测试,便可完全打消对修改代码的全部恐惧。 当看见糟糕的代码时,就可以放手整理。 代码会变得具有可塑性,你可以放心打磨出简单而满意的结果。

遵循 TDD 三项法则的话,所编写的每个单元测试都是一个示例,用代码描述系统的用法。

单元测试即是文档。它们描述了系统设计的最底层设计细节。 它们清晰准确,以读者能够理解的语言写成,并且形式规整可以运行。

测试代码的一个问题是必须隔离出待测试的代码。 如果一个函数调用了其他函数,单独测试它通常会比较困难。 为了编写测试,你必须找出将这个函数和其他函数解耦的办法。 换言之,测试先行的需要,会迫使你去考虑什么是好的设计。

但是事后写的测试只是一种防守。 而先行编写的测试则是进攻,事后编写测试的作者已经受制于已有代码,他已经知道问题是如何解决的。 与采用测试先行的方式编写的测试代码比起来,后写的测试在深度和捕获错误的灵敏度方面要逊色很多。

TDD 是专业人士的选择。 它是一项能够提升代码确定性、给程序员鼓励、降低代码缺陷率、优化文档和设计的原则。 对 TDD 的各项尝试表明,不使用 TDD 就说明你可能还不够专业。

尽管 TDD 有诸多优点,但是它既非宗教信仰,也非魔力公式。

即使做到了测试先行,仍有可能写出糟糕的代码。 没错,因为写出的测试代码可能就很糟糕。

在某些场合照这三项法则去做会显得不切实际或不合适。 这种情况很少,但确实存在。

第 6 章 练习

专业人士都需要通过专门训练提升自己的技能,无一例外。乐手练习音阶,球员练习绕桩,医生练习开刀和缝针,律师练习论辩,士兵练习执行任务。要想表现优异,专业人士就会选择练习。

无论是搏斗还是编程,速度都来源于练习。

练习卡塔并不是为了舞台表演。训练意识和身体是为了真正搏斗时能够正确应对。 它的目的在于,在需要的时候,可以凭借本能完美出招。

编程卡塔的最终目标,也是逐步练习以达到纯熟。 反复的练习会训练大脑和手指如何动作和反应。 在不断练习当中,你或许会发现动作的细微进步,或者解决问题效率的小幅提升。

和习武者一样,程序员应该懂得多种不同的卡塔,并定期练习,确保不会淡化或遗忘。

或者一个简单问题,一个人写单元测试,另一个人写程序通过单元测试,然后交换角色。

写单元测试的程序员会极力控制解决问题的方式,他也有足够的空间来施加限制:如果程序员选择实现一个排序算法,写测试的人可以很容易地限制速度和内存,给同伴施压。这样整个游戏就非常考验人……也可以说是非常有趣。

职业程序员通常会受到一种限制,即所解决问题的种类比较单一。 老板通常只强调一种语言、一种平台,以及程序员的专门领域。 经验不够丰富的程序员,履历和思维中都存在某种贻害无穷的盲区。

保持不落伍的一种方法是为开源项目贡献代码,就像律师和医生参加公益活动一样。 开源项目有很多,为其他人真正关心的开源项目做一点贡献,应该可以算是提升技能的最好办法了。

无论如何,专业人士都需要练习。

练习的时候你是赚不到钱的,但是练习之后,你会获得回报,而且是丰厚的回报。

第 7 章 验收测试

专业开发人员既要做好开发,也要做好沟通。“输入糟糕,输出也会糟糕”对程序员同样适用,所以职业程序员会重视与团队及业务部门的沟通,确保这种沟通的准确、流畅。

做业务的人和写程序的人都容易陷入一个陷阱,即过早进行精细化。

在工作中,有一种现象叫观察者效应,或者不确定原则。每次你向业务方展示一项功能,他们就获得了比之前更多的信息,这些新信息反过来又会影响他们对整个系统的看法。

需求完成得越精细,就越容易被忽视,系统因此也谈不上完工。

开发人员也会掉进精确化的陷阱。他们知道必须评估整个系统,而且通常认为需要精确评估。但是,事实并非如此。

首先,即便拥有全面准确的信息,评估也通常会存在巨大的变数。其次,因为不确定原则的存在,不可能通过反复推敲实现早期的精确性。 需求是一定会变化的,所以追求那种精确性是徒劳的。

避免过早精细化的办法是尽可能地推迟精细化。 专业开发人员直到着手开发的前一刻才会把需求具体化。

但是,这可能造成另一个问题:迟来的模糊性。

业务方常常会提出不同意见。这时候他们会发现,相比解决分歧,更好的办法是换一种说法,所以会寻找各方都同意的关于需求的表述,而不是去解决争端。

模糊不只来自于分歧或争论。 有时候,业务方会想当然地认为看文档的人懂得自己的意思。

专业开发人员(也包括业务方)必须确认,需求中没有任何不确定因素。

验收测试就是在接受正式发布之前由用户执行的程序,也有人认为它是 QA 测试。

我们把验收测试定义为业务方与开发方合作编写的测试,其目的在于确定需求已经完成。

验收测试的目的是沟通、澄清、精确化。 开发方、业务方、测试方对验收测试达成共识,大家都能明白系统的行为将会是怎样。 各方都应当记录这种准确的共识。 在专业开发人员看来,与业务方、测试方协同工作,确保大家都明白要做的是什么,是自己的责任。

专业程序员会避免这种情况。 相比手动测试,自动化测试的成本非常低,让人手工执行测试脚本不划算。 专业开发人员认为,实现验收测试的自动化是自己的责任。

在理想状态下,业务方和 QA 会协作编写这些测试,程序员来检查测试之间是否有冲突或矛盾。 但实际上,业务方通常没有时间,或者有时间也难以达到所需要的细致程度,所以他们通常会把测试交给业务分析员、QA 甚至是开发人员。

如果只能由开发人员来写测试,应当确保写测试的程序员与开发所测试功能的程序员不是同一个人。

通常,业务分析员测试“正确路径”,以证明功能的业务价值; QA 则测试“错误路径”、边界条件、异常、例外情况,因为 QA 的职责是考虑哪些部分可能出问题。

遵循“推迟精细化”的原则,验收测试应该越晚越好,通常是功能执行完成的前几天

在敏捷项目中,只有在选定了下一轮迭代(Iteration)或当前冲刺(Sprint)所需要的功能之后,才编写测试。

迭代开始的第一天,就应当准备好最初的几项验收测试。 然后每天都应当完成一些验收测试,到迭代的中间点,所有的测试都应当准备完毕,如果这时候还没有准备好所有的测试,就必须抽调一些开发人员来补充编写测试。 如果这种情况经常发生,这个团队应当增加 BA 或 QA。

写测试的人也是普通人,也可能犯错误。有时候,你刚开始实现某个功能,就会发现有些测试没什么意义。 有些太复杂,有些不灵活,有些包含愚蠢的假定,还有些干脆就是错的

身为专业开发人员,与编写测试的人协商并改进测试是你的职责。 绝不能被动接受测试,更不能对自己说:“噢,测试是这么要求的,我就得这么办。”

身为专业开发人员,你的职责是协助团队开发出最棒的软件。 也就是说,每个人都需要关心错误和疏忽,并协力改正。

验收测试是业务方写给业务方的(虽然可能最后是身为开发者的你来写)。 它们是正式的需求文档,描述了业务方认为系统应该如何运行。 关心验收测试结果的是业务方和程序员。

尽管单元测试和验收测试的对象通常是相同的,但绝对谈不上“重复”。 尽管两者测试的可能是同一个对象,其机制和路径却是不同的。 单元测试是深入系统内部进行,调用特定类的方法; 验收测试则是在系统外部,通常是在 API 或者是 UI 级别进行。 所以两者的执行路径是截然不同的。

这两种测试并不重复的根本理由在于,它们的主要功能其实不是测试,测试只是它们的附属职能。

单元测试和验收测试首先是文档,然后才是测试。 它们的主要目的是如实描述系统的设计、结构、行为。它们当然可以验证设计、结构、行为是否达到了具体指标,但是,它们的真正价值不在测试上,而在具体指标上。

编写 GUI 的验收测试很麻烦。 但如果把 GUI 当成 API 那样处理,而不是看成按钮、滚动条、格子、菜单,那验收测试就简单多了。

有条设计原则是“单一责任原则”(SRP)。 按照这条原则,应该把根据不同原因而变化的元素分开,把根据同一原因变化的元素归类分组。GUI 的设计也应该这样。

布局、格式、工作流,都会因为效率和美观的原因而变化,但是 GUI 背后的功能却不会因此变化。 所以,在编写 GUI 的验收测试时,必须使用 GUI 背后相对稳定的抽象元素。

如果一个页面有七个按钮,写测试时,就不应当根据按钮的坐标来点击,而应当根据名字来点击。 好一点的办法是,给每个按钮加上唯一 ID。 更好的办法是赋予 ID 明确的意义:某个测试选择的是 ID 为 ok_button 的按钮,而不是控制区域内第 4 行第 3 列的按钮。

几十年来,设计专家一直在教导我们,要把 GUI 和业务逻辑分开。

通过 GUI 来进行测试是非常容易出问题的,除非你要测试的仅仅是 GUI。

因为 GUI 很容易变化,所以针对 GUI 的测试很不稳定。

如果 GUI 的每一次变化之后,都会有成百上千的测试通不过,那么最好放弃这些测试,或者不要改动 GUI。 两者都只是补救,根本的办法还是借助 GUI 背后的 API 来测试业务逻辑。

应当尽可能地减少 GUI 测试。 GUI 很容易变化,所以这类测试是不稳定的。GUI 测试越多,维护它们的难度就越大。

请务必确保在持续集成系统中,单元测试和验收测试每天都能运行好几次。 整套持续集成系统应该由源代码管理系统来触发。 只要有人提交了代码,持续集成系统就会开始构建,并运行所有的测试,测试结果会用电子邮件发送给团队所有人。

保持持续集成系统的时刻运行是非常重要的。 持续集成不应该失败,如果失败了,团队里的所有人都应该停下手里的活,看看如何让测试通过。 在持续集成系统里,失败的集成应该视为紧急情况,也就是“立刻中止”型事件。

交流细节信息是件麻烦事。尤其是开发方和业务方交流关于程序的细节时,更是如此。通常,各方握手言欢,以为其他人都明白自己的意思。 双方以为取得了共识,然后带着截然不同的想法离开,这种事太平常不过了。

要解决开发方和业务方沟通问题,我所知道的唯一有效的办法就是编写自动化的验收测试。这些测试足够正式,所以其结果有权威性。 这些测试不会造成模糊,也不可能与真实系统脱节。 它们,就是无可挑剔的需求文档。

第 8 章 测试策略

每个专业的开发团队都需要一套好的测试策略。

尽管公司可能设有独立的 QA 小组专门测试软件,但是开发小组仍然要把“QA 应该找不到任何错误”作为努力的目标。

如果有一群聪明人联合起来绞尽脑汁找出产品中所有的瑕疵和不足,他们肯定是能找出一些问题的。对 QA 找到的每一个问题,开发团队都应该高度重视、认真对待。应该反思为什么会出现这种错误,并采取措施避免今后重犯。

QA 和开发人员应该紧密协作,携手保障系统的质量。

QA 在团队中要扮演的便是需求规约定义者(specifier)和特性描述者(characterizer)。

QA 的任务便是和业务人员一起创建自动化验收测试,作为系统真正的需求规约文档。每轮迭代中,他们都可以从业务人员那里收集需求,将之翻译为向开发人员描述系统行为的测试(参考第 7 章)。

通常,业务人员编写针对正常路径的测试(happy-path test),而由 QA 编写针对极端情况(corner)、边界状态(boundary)和异常路径(unhappy-path)的测试。

QA 的另一项任务是遵循探索式测试的原则,描述系统运行中的真实情况,将之反馈给开发人员和业务人员。

QA 并没有解析需求,而是在鉴别系统的真实情况。

自动化测试金字塔

上图显示的是自动化测试金字塔,以图形化方式展现了专业开发组织中所需要的测试种类。

在金字塔底部是单元测试,这些测试由程序员使用与系统开发相同的语言来编写,供程序员自己使用。 编写这些测试的目的是在最低层次上来定义系统。

单元测试是可行的,而且可以做到接近 100% 的覆盖率。 通常而言,这个数字应该保持在 90% 以上。 这里说的是真实的覆盖率,而不是那种虽然能通过但并不关心运行结果的错误的单元测试。

组件测试是验收测试的一种。 通常,它们是针对系统的各个组件而编写的。 系统的组件封装了业务规则,因此,对这些组件的测试便是对其中业务规则的验收测试。

组件测试由 QA 和业务人员编写,开发人员提供辅助。

组件测试差不多可以覆盖系统的一半。 它们更主要测试的是成功路径的情况,以及一些明显的极端情况、边界状态和可选路径。

大多数的异常路径是由单元测试来覆盖测试的。 在组件测试层次,对异常路径进行测试并无意义。

集成测试是编排性(choreography)测试。 它们并不会测试业务规则,而是主要测试组件装配在一起时是否协调。 它们是装配测试,用以确认这些组件之间已经正确连接,彼此间通信畅通。

集成测试一般由系统架构师或主设计师来编写,用以确认系统架构层面的结构是否正确无误。 在这个层次上,也许已经可以进行性能测试和吞吐率测试了。

集成测试多使用与组件测试同样的语言和环境来编写,一般不会作为持续集成的一部分,因为集成测试的运行时间通常都比较长。 但是,只要集成测试的编写人员认为有必要,这些测试就可以周期性(如每天一次或每周一次)运行。

这些测试是针对整个集成完毕的系统来运行的自动化测试,是最终的集成测试。

不会直接测试业务规则,而是测试系统是否已正确组装完毕,以及系统各个组成部件之间是否能正确交互。

在这个层次的测试集中,应该包含吞吐率测试和性能测试。

系统测试由系统架构师和技术负责人来编写,一般使用和 UI 集成测试同样的语言和环境。

测试周期视测试运行时间长短而定,相对而言不会过于频繁,但越频繁越好。

系统测试约占测试的 10%。 其目的不是要确保正确的系统行为,而是要确保正确的系统构造。 底层代码和组件的正确性已经有金字塔中较低层的测试来验证保障。

有一些团队可能会安排专人来进行探索式测试。 也有一些团队可能只会安排一两天的“抓虫”活动,让尽可能多的人参与其中,其中也许会包括管理人员、秘书、程序员、测试人员和技术写作人员,大家一哄而上,看是否会让系统崩溃。

覆盖率并非此类测试的目标。 探索式测试不是要证明每条业务规则、每条运行路径都正确,而是要确保系统在人工操作下表现良好,同时富有创造性地找出尽可能多的“古怪之处”。

第 9 章 时间管理

关于会议,有两条真理: (1)会议是必需的; (2)会议浪费了大量的时间。

专业开发人员同样清楚会议的高昂成本,他们同样清楚自己的时间是宝贵的,他们同样需要时间来写代码,来处理日程表上的事务。 所以,如果会议没有现实且显著的成效,他们会主动拒绝。

领导的最重要责任之一,就是帮你从某些会议脱身。 好的领导一定会主动维护你拒绝出席的决定,因为他和你一样关心你的时间。

仔细管理自己的时间是你的责任。如果你发现参加某个会议是在浪费时间,就应当想个礼貌的办法退出来。

你可以解释说,自己抽不出更多时间用于这场会议,问问有没有办法加快讨论,或者另选时间。

为了合理使用与会者的时间,会议应当有清晰的议程,确定每个议题所花的时间,以及明确的目标。

敏捷开发的武器库中包含“立会”:在开会时,所有参会者都必须站着。到场的人依次回答以下 3 个问题: (1)我昨天干了什么? (2)我今天打算干什么? (3)我遇到了什么问题?

每个问题的回答时间不应当超过 20 秒,所以每个人的发言不超过 1 分钟。即便是 10 个人的小组,开一次这种会议的时间也不会超过 10 分钟。

迭代计划会议用来选择在下一轮迭代中实现的开发任务。

在会议召开前必须完成两项任务:评估可选择任务的开发时间,确定这些任务的业务价值。 如果组织得足够好,验收/组件测试也应当在会议召开前完成,或者至少要有概略方案。

会议的节奏应该足够快,简明扼要地讨论各个候选任务,然后决定是选择还是放弃。 会议在每个任务上所花的时间应该限制在 5 到 10 分钟。如果需要更详细的讨论,则应当另选时间,挑出团队中的一部分人专门进行。

凭我的经验,在每轮迭代中,这类会议所花的时间不应当超过 5%。 如果一周(40 小时)为一个迭代周期,这类会议时间应当限制在 2 小时内。

这类会议在迭代的末尾召开。团队成员讨论本轮迭代中什么做得对,什么做得不对。 业务方可以看到最新工作成果的 demo。 如果组织不当,这类会议可能浪费很多时间, 所以不妨在最后一天下班前 45 分钟召开。 花 20 分钟来回顾,花 25 分钟来演示。

Kent Beck 曾告诉我一个深刻的道理:“凡是不能在 5 分钟内解决的争论,都不能靠辩论解决。 ”争论之所以要花这么多时间,是因为各方都拿不出足够有力的证据。 所以这类争论依据的不是事实,而是信念。

长期来看,强力是无法解决争论的,最终还是需要数据。

有人会表现得非常被动。 他们同意结束争论,之后却消极对待结果,拒绝为解决问题出一份力。 他们会安慰自己说:“既然其他人想要这么办,就这么办吧。”这可能是非专业的行为中最糟糕的了。 千万千万不要这样做。 如果你同意了,就必须拿出行动来。

编程是需要持续投入精力和注意力的智力活动。 注意力是稀缺的资源,它类似魔力点数。 如果你用光了自己的注意力点数,必须花一个小时或更多的时间做不需要注意力的事情,来补充它。

职业开发人员会学习安排时间,妥善使用自己的注意力点数。 我们选择注意力点数充裕的时候编程,在注意力点数匮乏时做其他事情。

注意力点数也会随时间流逝而减少。 如果不及时使用,它就会消失。

会议之所以具有巨大的破坏力,原因之一就在于此。 如果你所有的注意力点数都用在了会议上,编程时就大脑空空了。

忧虑和分心也会消耗注意力点数。

在你不集中注意力的时候,注意力点数可以缓慢恢复。 漫步一段长路,与朋友聊天,看看窗外,都有助于恢复注意力点数。

一旦注意力点数耗尽,你就没法控制注意力。 你仍然可以写代码,但是多半需要第二天重写,或者在几周或几个月之后备受这段代码的煎熬。 所以,更好的办法还是花 30 到 60 分钟来换换脑子。

定期训练肌肉注意力,可以提升心智注意力的上限。

我训练肌肉注意力的办法是骑车。

我发现,如果能接触到其他人的创造性思维,我的创造力也最旺盛,所以我阅读大量的科幻小说。这些作者的创造力会激发我对软件的创造力。

这种行为叫作优先级错乱——提高某个任务的优先级,之后就有借口推迟真正急迫的任务。 优先级错乱是自我麻醉的谎言,因为不能面对真正需要做的事情,所以我们告诉自己,其他事情更重要。

专业开发人员会评估每个任务的优先级,排除个人的喜好和需要,按照真实的紧急程度来执行任务。

所有软件开发者都要遇到死胡同。 比如你做了决定,选择了走不通的技术道路。你对这个决定越是坚持,浪费的时间就越多。 如果你认为这关系到自己的专业信誉,就永远也走不出来。

慎重的态度和积累的经验可以帮你避免某些死胡同,但是没法完全避免所有的。

真正需要的是,在走入死胡同时可以迅速意识到,并有足够的勇气走回头路。 这就是所谓的坑法则(The Rule of Holes):如果你掉进了坑里,别挖。

比死胡同更糟的是泥潭。

之所以说泥潭比死胡同更麻烦,是因为在泥潭中,你仍然可以看到前进的道路,而且看起来总是比走回头路要短(虽然实际不是这样)。

真正的问题在于,泥潭和死胡同一样是无可避免的。 慎重的态度和积累的经验有助于避开泥潭,但无法彻底避开每一处泥潭。

在泥潭中继续前进的危害是不易察觉的。 面对简单问题,你给出解决方案,保持代码的简单、整洁。之后问题不断扩展,越来越复杂,你则扩展代码库,尽可能保持整洁。 某天,你发现自己从一开始就做了错误的选择,在需求变化的方向上,程序跟不上节奏。

这就是转折点!你可以回头修正设计,也可以继续走下去。 走回头路看起来代价很高,因为要把已有代码推翻重来,但是走回头路绝对是最简单的方法。 如果继续前进,系统就可能陷入泥潭,永远不得脱身。

专业开发人员对泥潭的恐惧远远大于死胡同。 他们会时刻留神显露出来的泥潭,然后运用各种努力,尽早尽快地脱身。

发现自己身处泥潭还要固执前进,是最严重的优先级错乱。 继续前进无异于欺骗自己,欺骗团队,欺骗公司,欺骗客户。

专业开发人员会用心管理自己的时间和注意力。 他们知道优先级错乱的诱惑,他们也珍视自己的声誉,所以会抵制优先级错乱。 他们永远有多种选择,永远敞开心扉听取其他解决方案,他们从来不会执拗于某个无法放弃的解决方案。 他们也时刻警惕着正在显露的泥潭,一旦看清楚,就会避开。 最糟糕的事情,莫过于看到一群开发人员在徒劳地拼力工作,结果却陷入越来越深的泥潭。

第 10 章 预估

预估是软件开发人员面对的最简单、也是最可怕的活动之一了。

问题在于,不同的人对预估有不同的看法。 业务方觉得预估就是承诺。 开发方认为预估就是猜测。两者相差迥异。

预估是一种猜测。 它不包含任何承诺的色彩。 它不需要做任何约定。预估错误无关声誉。 我们之所以要预估,是因为不知道到底要花多少时间。

预估不是个定数,预估的结果是一种概率分布。

专业开发人员能够清楚区分预估和承诺。 只有在确切知道可以完成的前提下,他们才会给出承诺。 此外,他们也会小心避免给出暗示性的承诺。 他们会尽可能清楚地说明预估的概率分布,这样主管就可以做出合适的计划。

你可以根据 3 个数字预估某项任务。 这就是三元分析法。

  • O:乐观预估。 这是非常乐观的数字。 如果一切都异常顺利,你可以在这个时间内完成。 实际上,为了保证乐观预估有意义,这个数字对应的发生概率应当小于 1%。

  • N:标称预估。 这是概率最大的数字。 如果画一张柱状图,标称预估就是最高的那个。

  • P:悲观预估。 这是最糟糕的数字。 它应当考虑到各种意外,比如飓风、核战争、黑洞、其他灾难等。 为保证悲观预估有意义,这个数字对应的发生概率也应当小于 1%。 在 Peter 的例子中,这个数字是最右边的柱条,也就是 12 天。

有了以上三个预估,我们可以像下面这样描述概率分布:

μ=O+4N+P/6μ =(O+4N+P)/ 6

μ 是任务的期望完成时间。在 Peter 的例子中,它等于(1+12+12)/6,也就是大概 4.2 天。

在预估时,最重要的资源是你周围的人。他们可以看到你看不到的东西。相比自己单干,他们可以帮你更精确地预估任务。

一组人集合起来,讨论某项任务,预估完成时间,然后重复“讨论-预估”的过程,直到意见统一。

预估是非常容易出错的,所以才叫预估。 控制错误的办法之一是使用大数定律。 该定律的意思是:把大任务分成许多小任务,分开预估再加总,结果会比单独评估大任务要准确很多。

预估中的错误通常会被低估而不是高估,所以拆分再加总很难做到完美。

把大任务拆分成小任务分开预估,仍然是个好办法。 有些错误会被忽略,而且拆分成小任务也更利于理解任务本身及其他意外因素。

专业开发人员懂得如何为业务人员提供可信的预估结果,以便做出计划。 如果做不到,或者不确定能做到,专业开发人员不会给出承诺。

专业开发人员一旦做了承诺,就会提供确定的数字,按时兑现。 但是大多数情况下,他们都不会做这种承诺,而是提供概率预估,来描述期望的完成时间及可能的变数。

第 11 章 压力

即使有压力,专业开发人员也会冷静果断。尽管压力不断增大,他仍然会坚守所受的训练和纪律,他知道这些是他赖以战胜由最后期限和承诺所带来的压力感的最好方法。

在压力下保持冷静的最好方式,便是规避会导致压力的处境。

业务人员可能在没有事先咨询我们的情况下就向客户做出了承诺。发生这种事情时,出于责任感我们必须主动帮助业务方找到方法来兑现这些承诺,但是一定不能接受这些承诺。

专业人士总会千方百计地帮助业务方找到达成目标的方法,但并不一定要接受业务方代为做出的承诺。 最终,如果我们无法兑现业务方所做出的承诺,那么该由当时做出承诺的人来承担责任。

快速前进确保最后期限的方法,便是保持整洁。 专业人士不会为了快点前进而乱来。他们明白“快而脏”是自相矛盾的说法。脏乱只会导致缓慢!

让系统、代码和设计尽可能整洁,就可以避免压力。

如果在危机中改变行为,就说明你并不真正相信常规行为中的原则。

如果在非危机时刻你会遵循测试驱动开发的纪律,但是在危机时刻你放弃了这种做法,就说明你并不真正相信 TDD 是有帮助的。

你可能会犯的最严重的错误,就是鲁莽仓促! 要避免产生孤注一掷的想法。鲁莽仓促只会把你带入更深的深渊。

相反,要放松下来。对问题深思熟虑。努力寻找可以带来最好结果的路径,然后沿着那条路径以合理稳定的节奏前进。

让你的团队和主管知道你正身陷困境之中。 告诉他们你所制定的走出困境的最佳计划。请求他们的支援和指引。

不要惊慌失措地茫然四顾另寻依靠,而要从容不迫、专心致志地依靠你自己的纪律原则,这将帮助你更快地走出困境。 如果你遵循 TDD,那么这时写的测试甚至要比平时多。 如果你笃行无情的重构,这时就要更多地进行重构。 如果你相信要保持函数尽量地小,这时就要让函数变得更小。战胜压力煎熬的唯一方法,便是依靠那些你已经知道切实有效的东西——你平时遵守的纪律。

结对!当头脑发热时,找一个愿意和你一起结对编程的伙伴。

你会前进得更快,而缺陷却会更少。 结对伙伴会帮助你守住原则,制止你的精神错乱。 搭档会捕捉住你疏忽遗漏的事情,会提出有帮助的想法,会在你注意力迷失的时候接过你手中的工作继续前进。

第 12 章 协作

大多数软件都是由团队开发出来的。 当团队成员能够十分专业地互相协作时,整个团队是最为高效的。 单打独斗与游离于团队之外都是不专业的表现。

我们并非是因为喜欢和其他人在一起工作才选择做程序员的。 我们都认为人际关系难以应付而且毫无规律。 编程用的机器则整洁,行为也可预见。 如果可以一个人待在房间里数个小时沉浸在一些真正有趣的问题上,那将会是最开心的时光。

我们,程序员们,还是最享受面无表情的沉思,把自己像蚕茧一样裹起来,沉浸于问题思考中。

专业程序员最糟糕的表现是两耳不闻窗外事,只顾一头将自己埋在技术堆里,甚至连公司业务火烧眉毛行将崩溃了也不闻不问。 你的工作职责就是要让业务免于陷入困顿,让公司可以长久发展下去。

专业人士结对工作,还因为这是分享知识的最好途径。 专业人士并不会仅凭一己之力从零开始创建知识,而是通过互相结对来学习系统的不同部分和业务。

专业人士之所以结对,是因为结对是复查代码最好的方式。 系统中不应该包含未经其他程序员复查过的代码。

最有效率且最有效果的代码复查方法,就是以互相协作的方式完成代码编写。

如果我们真想终生能以编程度日,那么,一定要学会交流——和大家交流。

第 13 章 团队与项目

让一个程序员把一半的时间投入在项目 A 中,把其余时间投入在项目 B 中,这并不可行,尤其是当这两个项目的项目经理不同、业务分析师不同、程序员不同、测试人员不同时,更不可行。

形成团队是需要时间的。团队成员需要首先建立关系。他们需要学习如何互相协作,需要了解彼此的癖好、强项、弱项,最终,才能凝聚成团队。

有凝聚力的团队确实有些神奇之处。他们能够一起创造奇迹。他们互为知己,能够替对方着想,互相支持,激励对方拿出自己最好的表现。他们攻无不克。

有凝聚力的团队通常有大约 12 名成员。最多的可以有 20 人,最少可以只有 3 个人,但是 12 个人是最好的。

这个团队应该配有程序员、测试人员和分析师,同时还要有一名项目经理。

所以由 12 个人组成的理想团队,人员配备情况是这样的:7 名程序员、2 名测试人员、2 名分析师和 1 名项目经理。

分析师开发需求,为需求编写自动化验收测试。

分析师关注业务价值,而测试人员关注正确性。 分析师编写成功路径场景; 测试人员要关心的是那些可能出错的地方,他们编写的是失败场景和边界场景。

项目经理跟踪项目团队的进度,确保团队成员理解项目时间表和优先级。

其中有一名团队成员可能会拿出部分时间充任团队教练或 Master 的角色,负责确保项目进展,监督成员遵守纪律。

专业的开发组织会把项目分配给已形成凝聚力的团队,而不会围绕着项目来组建团队。

第 14 章 辅导、学徒期与技艺

计算机科班毕业生的质量一直令我颇感失望。

由于大学并没有教授真正的编程之道。

那些符合要求的毕业生有个共同点:他们几乎都在进入大学之前就已经自学编程,并且在大学里依然保持自学的习惯。

如果有一名老师能带着我陪伴我一起工作,那就要好得多了。 但是这也没有关系,因为我会观察他们的工作,从中快速学习。

大学毕业生在成为软件开发人员之前有一段合理的督导实训期,并不是什么不合时宜的过分建议。

人类文化进化的基本单位是 meme。 meme 在很大程度上指的是“以非遗传方式(如模仿)传递的行为或文化属性”。 任何一个信息,只要它能够通过广义上称为“模仿”的过程而被复制,它就可以称为 meme。 也就是说通过模仿获得并加以传播的任何想法、说法或做法都可以算作是 meme,如“曲调旋律、想法思潮、时髦用语、时尚服饰、陶器制作或搭建拱门的方法”等等。如今,研究 meme 及其社会文化影响的学科被称为 Memetics。 在本文中,将 meme 译为“模因”。 道金斯根据 gene 杜撰出了 meme,而“模因”一词则是模仿了“基因”一词翻译而成的。

附录 工具

永远不要签入没有通过全部测试的代码。 永远不要。

我的持续构建哲学很简单:把它和源代码控制系统对接起来。 不管什么时候,只要有人签入代码,就要能自动进行构建,并把结果状态报告给团队。

团队必须一直确保构建成功。如果构建失败了,就必须“停止一切行动”,整个团队都必须聚在一起快速解决这个问题。

每种语言都有自己独特的单元测试工具。

不论选择什么样的单元测试工具,这些工具都要支持如下一些基本的特性。

  1. 必须能够快速便捷地运行测试。 是通过 IDE 插件还是简单地通过命令行工具来运行,并无关紧要,但是开发人员必须随时都能运行单元测试。运行这些测试的方法不甚关键。

  2. 对于测试是通过还是失败了,这些工具应该给出清楚的视觉提示。 是在图形界面中给出绿条还是以控制台消息提示“测试全部通过”都无所谓,关键是必须要能够快速运行全部测试,而且运行结果必须清晰明确。

  3. 对于测试进度,这些工具也应该给出清楚的视觉提示。 是在图形界面中显示进度条还是以一串小点来显示并不要紧,关键在于它要能够清晰说明测试是仍在运行中、没有卡住,还是已经中止了。

  4. 这些工具应该避免测试用例之间彼此通信。 防止一个测试依赖于前面另外一个测试运行的情况。 测试用例间互有依赖是要极力避免的陷阱,千万不要掉进这样的陷阱之中。

  5. 这些工具应该使编写测试变得十分容易。 这些工具用于在 API 层对组件进行测试。 它们的任务是要确保组件行为是以业务人员和 QA 能够理解的语言来描述的。 事实上,最理想的情况是业务分析师和 QA 能够使用这些工具来编写规约。