简介
本书是编程大师 “Bob 大叔” 40 余年编程生涯的心得体会的总结,讲解要成为真正专业的程序员需要具备什么样的态度,需要遵循什么样的原则,需要采取什么样的行动。作者以自己以及身边的同事走过的弯路、犯过的错误为例,意在为后来者引路,助其职业生涯迈上更高台阶。本书适合所有程序员阅读,也可供所有想成为具备职业素养的职场人士参考。
什么是软件专业人士? 软件专业人士如何行事? 软件专业人士如何处理冲突,应对很紧的工期,如何和不讲道理的管理人员打交道? 软件专业人士何时应该说“不”?怎么说? 软件专业人士如何应对压力?
在本书的实务性意见背后,隐隐体现出一种奋力突破的积极态度。这种态度提倡要诚信,要富有荣誉感、自尊心和自豪感,要勇于承担作为一名手艺人和工程师所肩负的重大责任。这种责任包括要努力工作,出色完成任务;要擅于沟通,能够就事论事;要管理好时间,能够坦然面对艰难的“风险回报”决策。除此之外,这种责任之中还包括神圣的使命感。身为一名工程师,你比任何管理者可能都了解得更透彻。了解这些也意味着你肩负着要敢于行动的重大责任。
一、专业主义 or 职业道德
1 .1 清楚你要什么
“专业主义”有很深的含义,它不但象征着荣誉与骄傲,而且明确意味着责任与义务。这两者密切相关,因为从你无法负责的事情上不可能获得荣誉与骄傲。
1 .2 担当责任
案例:我推出了一个新版本,内容包括:修复了几个小故障,增加了客户要求的一项新功能。曾承诺会在截止日期之前提供那项新功能。连夜赶工,总算在约定日期前交付了项目。
项目上线两天后,经理打电话,有很多用户投诉项目的一个主要功能没有执行。原来为了按时交付项目,没有测试例行程序;测试了部分功能,但测试例行程序要费好几个小时,而当时又必须交付软件。因为故障修复部分都不涉及例行程序部分的代码,所以也没担心会有什么不妥。
项目的问题导致,用户使用不了主要功能,经理也抱怨炮轰。
之后对代码进行例行程序,果然复现了问题,打电话给经理说明情况,建议用户退回旧版本。经理发火了,说那对客户来说无疑是个双重打击,因为客户不仅为此不能正常使用产品,而且还无法使用事先承诺的新功能。
故障排查并修复问题,已经过去好几天了,这期间,经理每隔几小时就打电话问我问题什么时候能解决,他还把那些用户喋喋不休的抱怨如数传达给我,并一再告诉我让那些客户重新起用旧软件令他多么尴尬。
最后,终于找出了缺陷所在,重新交付修复了问题的新程序,一切恢复正常。老板过来说:“你最好别再犯同样的错误。”我只能默默地点点头。
经过反省,我意识到没有对例行程序进行测试就交付软件是不负责任的。为了如期交付产品,我忽略了测试环节,整个过程中只考虑要如何保全自己的颜面,却没顾及客户和雇主的声誉。我本该早点儿担起责任,告诉经理测试还未完成、自己不能按时交付产品。那么做绝非易事,经理一定会不高兴,但客户不会影响使用,更不会流失用户。
1 .3 首先,不行损害之事
从纯软件角度看,软件开发人员会破坏软件的功能与架构。那么如何避免带来这些破坏呢?
1.3.1 不要破坏软件功能
要对自己的不完美负责。代码中难免会出现 bug,但这并不意味着你不用对它们负责;没人能写出完美的软件,但这并不表示你不用对不完美负责。
所谓专业人士,就是能对自己犯下的错误负责的人,哪怕那些错误实际上在所难免。所以,要练习的第一件事就是“道歉”。道歉是必要的,但还不够。你不能一而再、再而三地犯相同的错误。职业经验多了之后,你的失误率应该快速减少,甚至渐近于零。失误率永远不可能等于零,但你有责任让它无限接近零。
-
让 QA 找不出任何问题: 代码提交之前,要在自测阶段没有任何问题。绝对不可以把没有把握的代码提交给测试。测试是项目的最后一道防线,而不应该是唯一的防线。
-
要确信代码正常运行: 测试!一遍遍地测,翻来覆去、颠来倒去地测,使出浑身解数来测!实行自动化测试。写一些随时都能运行的单元测试,然后尽可能多地执行这些测试。要设计易于测试的代码,最好是先写测试,再写要测的代码。这一方法叫做测试驱动开发(TDD)。
-
自动化 QA
1.3.2 不要破坏结构
在发布新功能时,千万不要破坏结构。所有软件项目的构建根本原则是,软件要易于修改。简言之,就是必须能让修改不必花太高代价就可以完成。
关于如何创建灵活可维护的结构的软件设计原则和模式有很多教程和方法。要牢记这些原则和模式,并在开发软件时认真遵循。但是其中有一条实在是没几个软件开发人员会认真照做,那就是,如果你希望自己的软件灵活可变,那就应该时常修改它!
要随时的修改,开发哪个模块,就对它做点简单的修改来改进结构。每次通读代码的时候,也可以不时调整一下结构。
这一策略有时也叫 “无情重构” :对每个模块,每检入一次代码,就要让它比上次检出时变得更为简洁。每次读代码,都别忘了进行点滴的改善。
通常认为对上线运行的软件不断地做修改是危险的。错!让软件保持固定不变才是危险的!如果一直不重构代码,等到最后不得不重构时,你就会发现代码已经“僵化了”。
不敢不断修改代码,是因为破坏代码,之所以会破坏代码,是因为没有一套覆盖全代码的自动化测试,如果那套测试可以随时快速执行,那么也根本不会害怕修改代码。
专业开发人员对自己的代码和测试极有把握,他们会随意地做各种修改。
1 .4 职业道德
任何的公司或雇主没有任何的义务留给你学习的时间和渠道,如果公司或雇主为员工买各种书籍或者对员工进行技能培训与分享。说明他们待你不薄。但可千万别就此认为这些是雇主该做的。如果他们不为你做这些,你就该自己想办法去做。
公司或雇主出钱,你必须要付出时间和精力。例如一周的工作 40 小时,在工作的时间内应该用来解决公司或雇主的问题,而不是你自己的问题。
你应该计划每周工作 60 小时。前 40 小时是给雇主的,后 20 小时是给自己的。在这剩余的 20 小时里,你应该看书、练习、学习,或者做其他能提升职业能力的事情。
在此,不是说要占用你全部的业余时间。我是指每周额外增加 20 小时,也就是大约每天 3 小时。如果你在午饭时间看看书,在通勤路上听听播客,那么你就都能兼顾到了。
或许你不愿那么勤勉。没问题。只是那样的话你也不能自视为专业人士了,因为所谓“术业有专攻”那也是需要投入时间去追求的。
或许你会觉得这样做只会让人精力枯竭。恰恰相反,这样做其实能让你免于枯竭匮乏。假设你是因为热爱软件而成为软件开发者,渴望成为专业开发者的动力也正是来自对软件的热情,那么在那 20 小时里,就应该做能够激发、强化你的热情的事。那 20 小时应该充满乐趣!
1.4.1 了解你的领域
许多技术随着时间的流逝而淘汰,而有些技术在今天仍像过去一样富有价值,甚至宝贵了。这些技术更应该是专业人士所掌握的。
设计模式。必须能描述 GOF 书中的全部 24 种模式,同时还要有 POSA 书中的多数模式的实战经验。 设计原则。必须了解 SOLID 原则,而且要深刻理解组件设计原则。 方法。必须理解 XP、Scrum、精益、看板、瀑布、结构化分析及结构化设计等。 实践。必须掌握测试驱动开发、面向对象设计、结构化编程、持续集成和结对编程。 工件。必须了解如何使用 UML 图、DFD 图、结构图、Petri 网络图、状态迁移图表、流程图和决策表。
1.4.2 坚持学习
软件行业的飞速改变,意味着软件开发人员必须坚持广泛学习才不至于落伍。
1.4.3 练习
业精于勤。真正的专业人士往往勤学苦干,以求得自身技能的纯熟精炼。只完成日常工作是不足以称为练习的,那只能算是种执行性质的操作,而不是练习。练习,指的是在日常工作之余专门练习技能,以期自我提升。
“卡塔”,第六章详细讲解
1.4.4 合作
专业软件开发人员往往会更加努力地尝试与他人一起编程、一起练习、一起设计、一起计划,这样他们可以从彼此身上学到很多东西,而且能在更短的时间内更高质量地完成更多工作。
1.4.5 辅导
俗话说:教学相长。想迅速牢固地掌握某些事实和观念,最好的办法就是与你负责指导的人交流这些内容。这样,传道授业的同时,导师也会从中受益。
1.4.6 了解业务领域
每位专业软件开发人员都有义务了解自己开发的解决方案所对应的业务领域。你未必需要成为该领域的专家,但你仍需要用功,付出相当的努力来认识业务领域。
读一两本相关专业的书籍、看相关领域的教学视频、与相关行业人士进行交流访谈... 这些都将大大的提升对业务领域的理解,从而更好地符合或辨别业务逻辑。
1.4.7 与雇主/客户保持一致(客户第一)
雇主的问题就是你的问题。你必须弄明白这些问题,并寻求最佳的解决方案。每次开发系统,都应该站在雇主的角度来思考,确保开发的功能真正能满足雇主的需要。
1.4.8 谦逊
编程是一种创造性活动。写代码是无中生有的创造过程,我们大胆地从混沌之中创建秩序。我们自信地发布准确无误的指令,稍有差错,机器的错误行为就可能造成无法估量的损失。因此,编程也是极其自负的行为。 专业人士知道自己自负,不会故作谦逊。他们熟知自己的工作,并引以为荣;他们对自己的能力充满自信,并因此勇于承担有把握的风险。专业人士不是胆小鬼。
要有足够的自信,也要时刻谦虚谨慎。
二、说 "不"
能就是能,不能就是不能。不要说 ‘试试看’。
案例:我们的系统预期在某一天上线,为了能按时交付系统,我们的团队连续几周加班加点。虽然在上线前一周完成了系统搭建,但是有很多待解决的 bug 和问题。而负责的经理命令我们必须按期完工。于是系统上线后,出现了各种各样的问题,导致新系统的效率只有旧系统的一半,系统也随之关闭。当经理等人问我们多久可以让系统稳定下来。我说:"四周"。而经理依然像从前一样,命令我们一周内完成!经过数次的沟通后,我们的组长于是说:“好吧,我们试试看吧。” 经过一周的修复,系统依然存在一些问题,此外还有其他问题也暴露出来了。慢慢地又过了几周,对系统问题的终于逐渐减少,似乎一切回复正轨了。
有时,要敢于说“不”,才能减少错误和损失发生,才能真正做成一些事情。
2.1 对抗角色
每个岗位都有自己的目标和职责。经理的目标是一周内上线一个功能,这是他的目标和职责;当你明明知道不能按时完成这个功能目标,嘴上却说:"好的,我试试看。",那么便是你的失职。这时候,尽职的唯一选择是说“不,这不可能”。
2.2 高风险时刻
最要说 “不” 的是那些高风险的关键时刻。越是关键时刻,“不” 字就越具价值。 这一点应该不证自明。当公司存亡成败皆系于此时,你必须尽己所能,把最好的信息传递给你的经理。这往往意味着要说“不”。
2.3 要有团队精神
2 .3.1 试试看
2 .3.2 消极对抗
2.4 说“是”的成本
2.5 如何写出好代码
三、说 "是"
如何做到对项目时间的预估和对经理需求的承诺有足够的底气?
3.1 承诺用语
口头上说。心里认真。付诸行动。
做出承诺,包含三个步骤。
- 口头上说自己将会去做。
- 心里认真对待做出的承诺。
- 真正付诸行动
四、编码
要精熟掌握每项技艺,关键都是要具备“信心”和“出错感知”能力。本章将介绍关于编码的一套规则与原则。这些规则与原则并非关于代码本身,而是描述在编码时的行为、情绪与态度。它们所描述的是在编码时的心理、精神和情绪,而这些是“信心”和“出错感知”的源泉。
4.1 做好准备
编码是一项颇具挑战也十分累人的智力活动。相比其他类型的活动,编码要求更加聚精会神。因为在编码时你必须平衡互相牵制的多种因素。
- 首先,代码必须能够正常工作。必须理解当前要解决的是什么问题以及该如何解决。必须确保编写的代码忠实遵循解决方案。必须管理好解决方案的每一处细节,并且使语言、平台、现有架构以及当前系统的所有问题和平共处。
- 代码必须能够帮你解决客户提出的问题。很多时候,客户提出的需求其实并没能真正解决他们自己的问题。这有赖于你去发现这些问题并与客户交流,以确保代码能够满足客户的真实需求。
- 代码必须要能和现有系统结合得天衣无缝。你的代码不能让系统变得更僵硬、更脆弱、更晦涩,必须要妥善管理好各种依赖关系。简而言之,编写代码时必须遵循稳健的工程原则。
- 其他程序员必须能读懂你的代码。这不仅包括要写好注释这类事,还包括要精心锤炼代码,使它能够表达你的编程意图。要做到这点很不容易。事实上,这可能是程序员最难精通的一件事。
长时间维持高度集中精神是有难度的。再加上在团队或组织中工作时常会遭遇到各种问题与干扰,以及需要留意和关注的各种日常琐事。总之,编码时无可避免地会受到各种干扰。
当你无法全神贯注地编码时,所写代码就有可能出错,造成严重的影响,必须返工修改代码甚至重写。在心烦意乱的状态下工作,只会造成严重的浪费。
如果感到疲劳或者心烦意乱,千万不要编码。强而为之,最终只能再回头返工。相反,要找到一种方法来消除干扰,让心绪平静下来。
4.1.1 凌晨 3 点写出的代码
加班到凌晨 3 点,写出来的是什么样的代码?可能会写出漏洞百出的代码,数不过来的 BUG,亦或者代码本身的逻辑和设计就是一个错误。
疲劳的时候,千万不要写代码。奉献精神和职业素养,更多意义上指要遵循纪律原则而非成为长时间工作的工作狂。要确保自己已经将睡眠、健康和生活方式调整到最佳状况,这样才能做到在每天的 8 小时工作时间内全力以赴。
4.1.2 焦虑时写下的代码
人有七情六欲,生活中避免不了一些烦心事,与家人朋友吵架、孩子生病、个人财务问题等。这些事情或多或少会让我们产生焦虑、压力,这种感受让人心烦意乱,从而影响到我们的工作情绪。
虽然眼睛盯在屏幕上,手指也搭在键盘上,但什么都没干,此时并没有在努力解决面前的编程问题,而是在心里为那些问题躁动不安,无法集中注意力。
这时根本就不应该编写代码。这时产出的任何代码都会是垃圾。因此,这时不该写代码,而应该先解除焦虑情绪。
当然这些焦虑的情绪是不可能在一两个小时内解决的。我们更不能占用工作的时间来处理私人问题。但是关键所在是如何阻止焦虑持续的干扰。
花专门的一块时间,也许是一个小时,来处理造成焦虑的问题,而不是强迫自己忍受着内心的焦虑煎熬继续编程。如果小孩生病了,就打个电话回家询问一下情况。如果和家人朋友之间此前有点争论,就打电话好好沟通清楚。如果出现金钱方面的问题,就花些时间思考如何才能处理好财务问题。当然不可能在这一小时里就解决全部问题,但这样做可能就可以减少心中的焦虑。
理想情况下,私人时间处理私人问题,在工作期间处理这些事情是令人惭愧的,更是不专业的。
专业开发人员善于合理分配个人时间,以确保工作时间段中尽可能富有成效。也即是说,在家中时就应该专门安排时间去解决焦虑,这样就不会把焦虑情绪带到办公室里。
另一方面,如果发现自己虽然人坐在办公室里,但内心的焦虑正在不断削弱工作效率,那么最好还是花上一个小时让它们先安静下来,这要好过硬逼自己去写代码,因为这样憋出来的代码以后也将不得不抛弃。
4.2 流态区
关于高效率状态,有很多种叫法,"流态"、“流态区”、“心流”...
这是程序员在编写代码时会进入的一种意识高度专注但思维视野却会收拢到狭窄的状态。在这种状态下,他们会感到效率极高;在这种状态中,他们会感到“绝无错误”。
一些曾经进入这种状态但终又从中摆脱出来的人给出了一点儿忠告:避免进入流态区。这种意识状态并非真的极为高效,也绝非毫无错误。这其实只是一种“浅层冥想”状态,在这种状态下,为了追求所谓的速度,理性思考的能力会下降。产出的代码速度变快了,但是会无法顾及全局。
所以要努力避免进入流态区,当感觉要进入流态区时,就要即时退出。
4.2.1 音乐
很多人在工作期间,都会听音乐,以为这样有助于集中注意力。但是这是错误的。事实上,听音乐似乎消耗了一部分宝贵的脑力资源,而这些资源本该用于编写设计良好的整洁代码。音乐正带领他们进入流态区。
4.2.2 中断
当你在工作中,经常会被打断。这让人很烦躁,主要原因是无法快速回到之前的工作状态中去。
结对是用以应对中断的一种好方法。当你被打断时,结对搭档能够维护住中断处的上下文。等到你重新回去和结对搭档一起工作时,他能够很快地帮你恢复被打断前的思维。
另一种很有帮助的方法便是采用 TDD。失败的测试能帮你维护住编码进度的上下文。当处理完中断重新回去时,你很清楚下一步任务便是让这个失败的测试通过。
当然,中断无法避免,总有干扰会打断你、消耗你的时间。发生这种情况时要记住一点,也许下次也会轮到你去打断别人请求帮助。因此,礼貌地表现出乐于助人的态度才是专业的态度。
4.3 阻塞
有时候会出现干坐在电脑前,却一行代码也写不出来。
哪些原因会导致这些阻塞呢?主要因素便是睡眠。如果睡眠不足,什么代码也写不出来。其他因素还包括焦虑、恐惧和沮丧等。
有一个很简单的好办法可以解决这个问题。这个办法几乎屡试不爽,既简单易行,又能够帮助你写出很多代码。
这个方法便是:找一个搭档结对编程。
4.4 调试
很多软件开发人员会认为调试时间并非编码时间。他们认为存在调试时间是天经地义的,调试不等于编码。但是对于公司来讲,调试时间和编码时间是一样昂贵的,因此,如果我们能够做些事情避免甚或消除调试活动,那是最为理想不过的。
测试驱动开发,衡量你是否是一名专业人士的一个重要方面,便是看你是否能将调试时间尽量降到最低。绝对的零调试时间是一个理想化的目标,无法达到,但要将之作为努力方向。
4.5 保持节奏
软件开发是一场马拉松,不能像短跑一样冲刺,马拉松选手都会仔细调整好自己的身体状态。专业程序员也会同样仔细地保存好自己的精力和创造力。
当遇到一些难以解决的问题时,继续加班熬夜可能会事倍功半,此时此刻头脑已经变成了一团浆糊,不仅解决不了问题,而且还会把自己搞得十分疲惫。
此时此刻,已经开车回家,洗一个澡,或许就会突发灵感,豁然开朗。
4.6 进度延迟
即使是最优秀的程序员、最敬业的员工,也不能避免碰到延迟。有时候,则只是因为我们预估时过于乐观夸下了海口,最后延迟的情况无可避免。
管理延迟的诀窍,便是早期检测和保持透明。最糟糕的情况是,你一直都在告诉每个人你会按时完成工作,到最后期限来临前你还在这样说,但最终你只能让他们大失所望。不要这么做!
相反,要根据目标定期衡量进度,使用三个考虑到多种因素的期限:乐观预估、标称预估、悲观预估。8、12、20 ,尽量严守这三个时间点。不要把预估和期望混淆在一起!把全部这三个数字呈现给团队,并每天修正这些数字。
4.6.1 期望
如果有一个紧急情况,期限定在了 10 天后呢?不要对在 10 天内全部完成特性开发抱有期望!这种期望会杀死整个项目。期望会毁掉项目进度表,玷污你的名声,期望会把你拖进大麻烦中。你是绝不可能完成任务的。要让团队和利益相关者明白这个形势,除非另有后备预案,否则不要轻易松口退步。不要让其他任何人对此抱有期望。
4.6.2 盲目冲刺
盲目冲刺的结果会很严重,会给自己和团队带来一个错误的期望,其实快速冲刺是做不到的。你无法更快地写完代码。你无法更快地解决问题。如果试图这么做,最终只会让自己变得更慢,同时也只能制造出一堆混乱。
4.6.3 加班加点
加班确实有用,而且有时候也有必要。
- 你个人能挤出这些时间;
- 短期加班,最多加班两周;
- 有后备预案,以防万一加班措施失败了。
4.6.4 交付失误
在程序员所能表现的各种不专业行为中,最糟糕的是明知道还没有完成任务却宣称已经完成。这种做法非常的不专业,更没有职业道德可言。
4.6.5 定义“完成”
可以通过创建一个确切定义的“完成”标准来避免交付失误。最好的方法是让业务分析师和测试人员创建一个自动化的验收测试,只有完全通过这些验收测试,开发任务才能算已经完成。
4.7 帮助
编程并非易事,事实上,仅凭一己之力无法写出优秀的代码。既使你的技能格外高超,也肯定能从另外一名程序员的思考与想法中获益。
4.7.1 帮助他人
互相帮助是每个程序员的职责所在。将自己封闭在格子间或者办公室里与世隔绝,有悖于专业的职业精神。事实上,作为专业人士,要以能够随时帮助别人为荣。当你帮助了别人,会发现自己从中收获的东西#比给予的还要多。
4.7.2 接受他人的帮助
如同要以乐于助人为荣一样,也要以乐于接受别人的帮助为荣。
大胆的在群里说出:“我需要帮助!” 这体现的是一种专业的职业精神。如果帮助唾手可得却让自己一个人堵在那儿,是很不专业的表现。为了能够实现高效编程,好的协作至为重要。
4.7.3 辅导
辅导缺乏经验的程序员是那些经验丰富的程序员的职责。除了自身的内驱力和资深导师的有效辅导之外,没有东西能将一名年轻的软件开发人员更快地提升为敏捷高效的专业人士。花时间手把手地辅导年轻程序员是资深程序员的专业职责所在。同样道理,向资深导师寻求辅导也是年轻程序员的专业职责。
五、测试驱动开发
“测试驱动开发”(TDD),先写单元测试,再写代码。几乎所有的敏捷开发都会采纳。即使是非敏捷的团队也在实践 TDD。
5.1 此事已有定论
TDD 绝不仅仅是一种用于缩短编码周期的简单技巧。TDD 的确切实可行,并且,每个开发人员都要适应和掌握 TDD。
如果连所有代码是否都可以正常运行都不知道,还算什么专业人士?如果每次修改代码后没有测试,如何能够知道所有代码可以正常运行?如果缺乏极高覆盖率的自动化单元测试,如何能够做到每次修改代码后都对代码进行测试?如果不采用 TDD,如何能够获得极高覆盖率的自动化单元测试?
5.2 TDD 的三项法则
(1)在编好失败单元测试之前,不要编写任何产品代码。 (2)只要有一个单元测试失败了,就不要再写测试代码;无法通过编译也是一种失败情况。 (3)产品代码恰好能够让当前失败的单元测试成功通过即可,不要多写。
遵循这三项法则的话,大概 30 秒钟就要运行一次代码。先写好一个单元测试的一小部分代码,很快,你会发现还缺少一些类或函数,所以单元测试无法编译。因此必须编写产品代码,让这些测试能够编译成功。产品代码够用即可,然后再回头接着写单元测试代码。
这个循环不断反复。写一些测试代码,然后再写一些产品代码。这两套代码同步增长,互为补充。测试代码之匹配于产品代码,就如抗体之匹配于抗原一样。
5.3 TDD 的优势
5.3.1 确定性
如果将 TDD 作为一项行业纪律,任何时刻,代码有任何修改,都必须运行手头有的全部测试。
FitNesse 是一个基于 Java 的验收测试工具。是作者的开源项目,FitNesse 拥有 6.4 万行代码,其中 2.8 万行代码是单元测试代码,共计有超过 2200 个独立的单元测试用例。这些测试至少覆盖了 90%的产品代码,90 秒便可以完整执行一遍。
任何时刻,一旦修改了 FitNesse 的任何部分,只需再次运行全部的单元测试即可。如果单元测试全部通过,有相当有把握,足以交付了!
5.3.2 缺陷注入率
很多公司和团队都在使用 TDD,产生的 BUG 和缺陷很低。
5.3.3 勇气
如果能够确认修改代码时,是否破坏到代码。就不会畏惧对糟糕代码进行重构修改。
这是 TDD 最强大之处。拥有一套值得信赖的测试,便可完全打消对修改代码的全部恐惧。当看见糟糕的代码时,就可以放手整理。代码会变得具有可塑性,你可以放心打磨出简单而满意的结果。
当程序员不再惧怕整理代码时,他们便会动手整理!整洁的代码更易于理解,更易于修改,也更易于扩展。代码更简洁了,缺陷也更少了。整个代码库也会随之稳步改善,杜绝业界常见的放任代码劣化而视若不见的状况。
5.3.4 文档
遵循测试驱动开发(TDD)原则时,单元测试本身就成为了文档。它们通过代码详细描述了系统中对象的创建和函数的调用方式,是理解系统设计的最佳参考。单元测试以准确、可运行的方式记录了系统的底层细节,是理想的技术文档。哪个专业人士不希望提供这样的文档呢?
5.3.5 设计
遵循测试驱动开发的三项法则意味着必须先编写尚无法通过的单元测试。虽然这看似困难,但它促使你设计出松耦合的代码,以便测试单个函数,避免大量耦合的代码难以测试。事后编写测试虽然可以达到一定的覆盖率,但往往只是防守性的,无法深入捕捉错误,且受已有代码的限制。先写测试则是一种主动的设计驱动,帮助专业人士实现更好的设计。
5.3.6 专业人士的选择
TDD 是专业人士的选择。它是一项能够提升代码确定性、给程序员鼓励、降低代码缺陷率、优化文档和设计的原则。对 TDD 的各项尝试表明,不使用 TDD 就说明你可能还不够专业。
5.4 TDD 的局限
尽管 TDD 有诸多优点,但是它既非宗教信仰,也非魔力公式。遵循这三项法则并不能担保一定会带来上述好处。即使做到了测试先行,仍有可能写出糟糕的代码。没错,因为写出的测试代码可能就很糟糕。
另外,在某些场合照这三项法则去做会显得不切实际或不合适。这种情况很少,但确实存在。如果遵循某项法则会弊大于利,专业的开发人员就当然不会选用它。
六、练习
专业人士都需要通过专门训练提升自己的技能,无一例外。要想表现优异,专业人士就会选择练习。
任何事情,只要想做得快,都离不开练习。要想尽可能快地重复编码/测试过程,就必须能迅速做出决定。一而再、再而三地练习,直到烂熟于心。
保持不落伍的一种方法是为开源项目贡献代码,就像律师和医生参加公益活动一样。开源项目有很多,为其他人真正关心的开源项目做一点贡献,应该可以算是提升技能的最好办法了。
无论如何,专业人士都需要练习。他们这么做,是因为他们关心自己能做到的最好结果。更重要的是,他们用自己的时间练习,因为他们知道保持自己的技能不落伍是自己的责任,而不是雇主的责任。练习的时候你是赚不到钱的,但是练习之后,你会获得回报,而且是丰厚的回报。
七、验收测试
专业开发人员既要做好开发,也要做好沟通。“输入糟糕,输出也会糟糕”对程序员同样适用,所以职业程序员会重视与团队及业务部门的沟通,确保这种沟通的准确、流畅。
7.1 需求的沟通
通常开发方与业务方之间,关于需求的沟通是不容易的,其中会出现各种问题。
7.1.1 过早精细化
业务方还没有启动项目,就要精确知道最后能得到什么;开发方还没有评估整个项目,就希望精确知道要交付什么。双方都贪求不现实的精确性,而且经常愿意花大价钱来追求这种精确。
1 .不确定原则
有一种现象叫观察者效应,或者不确定原则。每次你向业务方展示一项功能,他们就获得了比之前更多的信息,这些新信息反过来又会影响他们对整个系统的看法。 最终结果就是,需求完成得越精细,就越容易被忽视,系统因此也谈不上完工。
2 预估焦虑
开发人员常陷入精确评估的误区,认为必须给出准确的系统评估,但事实并非如此。即使信息全面,评估也有很大变数。由于需求变化,追求早期精确无意义。专业人员明白,评估基于不精确需求,而且只是估计而已。他们通常会附加误差范围,以帮助业务方理解不确定性。
7.1.2 迟来的模糊性
为避免过早细化,专业开发人员倾向于在开发前的最后一刻才具体化需求。然而,这可能导致“迟来的模糊性”问题。业务方常在意见不一致时,选择用模糊的说法掩盖分歧,而不是真正解决问题。需求文档中的模糊之处往往反映了业务方的分歧。此外,模糊性有时也源于业务方默认读者能理解其意图。
7.2 验收测试
"验收测试"一词常被广泛和不一致地使用,有时被认为是用户在正式发布前执行的程序,有时则是 QA 测试。在这里,验收测试被定义为由业务方和开发方共同编写的测试,旨在确认需求已经满足。
7.2.1 “完成”的定义
开发人员自称任务已完成,可能是指功能可部署、是准备好进行 QA 测试,或者是代码已编写并运行但未测试
专业开发人员认为“完成”的标准是明确的:所有代码编写完成,测试全部通过,并经过 QA 和需求方的认可。要在不影响迭代速度的情况下达到这种完成程度,应编写完整的自动化测试,确保所有测试通过即表明所有需求已满足。
专业开发人员根据自动化的验收测试来明确需求,与业务方和 QA 合作,确保自动化测试覆盖全部完成指标。
7.2.2 沟通
验收测试旨在促进沟通、澄清需求和精确化理解。通过验收测试,开发方、业务方和测试方可以达成一致,清楚系统的预期行为。各方都应记录这种明确的共识。专业开发人员认为,与业务方和测试方协作,确保所有人都理解待实现的内容,是他们的责任。
7.2.3 自动化
验收测试应始终自动化进行。虽然软件开发过程中有时需进行手动测试,但验收测试不应如此,因为手动测试成本过高,而自动化测试成本相对较低。专业程序员会避免手工执行测试脚本,并认为实现验收测试的自动化是他们的责任。
7.2.4 额外工作
不要把自动化测试看作额外的工作,而应当看成节省时间和金钱的办法。这些测试可以避免你的开发误入歧途,也可以帮你确认自己已经完工。
7.2.5 验收测试什么时候写,由谁来写
在理想情况下,业务方和 QA 协作编写验收测试,程序员检查测试冲突。然而,实际由于业务方时间有限,通常测试由业务分析员、QA 或开发人员编写。若开发人员编写测试,应与开发功能的人员分开。业务分析员关注“正确路径”,QA 测试“错误路径”和异常情况。
根据“推迟精细化”原则,验收测试应在功能完成前数天进行。在敏捷项目中,测试在确定迭代或冲刺功能后编写。迭代首日应准备初步验收测试,逐日完成,至中点完成所有测试。
7.2.6 开发人员的角色
代码功能的实现应在验收测试通过后开始。开发者负责将验收测试与系统关联并确保测试通过。工具用于识别和解析测试,调用系统功能,简化过程并允许重复使用测试场景和设备。关键在于开发者需确保测试与系统关联并通过。
7.2.7 测试的协商与被动推进
测试人员可能犯错,因此开发人员在实现功能时可能遇到不合理或错误的测试。开发者应积极与测试人员协商以改进测试,而不是被动接受要求。作为专业开发者,责任在于与团队合作,关注并纠正错误,共同开发优质软件。
7.2.8 验收测试和单元测试
验收测试与单元测试有着不同的目的和作用。单元测试是程序员为程序员撰写的设计文档,关注底层代码结构和行为,关心结果的是程序员。验收测试则是业务需求文档,体现业务方对系统运行的预期,关心结果的是业务方和程序员。尽管两者可能测试同一对象,但它们的执行机制和路径完全不同:单元测试深入系统内部,而验收测试在系统外部进行。
区分这两种测试不是重复劳动,因为它们的主要功能不是测试,而是文档化系统的设计、结构和行为。测试是其附属功能,其真正价值在于准确记录和描述,而非仅仅验证指标。
7.2.9 图形界面及其他复杂因素
详细指定图形用户界面(GUI)是困难且容易变化的,因其美观主观且用户倾向于不断调整。编写 GUI 的验收测试很复杂,但可以通过将 GUI 视作 API 来简化,关注其背后稳定的功能而非组件本身。根据单一责任原则,应将容易变化的 GUI 元素与稳定的功能分离。
理想的测试方式是通过 API 而非 GUI 进行,直接调用 GUI 背后的 API,以确保测试的稳定性和一致性。GUI 若需频繁改动,依赖它的测试将难以维护,不妨减少此类测试或使用测试桩代替业务逻辑。总之,尽可能减少 GUI 上的测试,将关注点放在相对稳定和核心的功能上。
7.2.10 持续集成
确保在持续集成系统中,单元测试和验收测试每天多次运行。系统应由源代码管理系统触发,一旦代码提交,立即构建并运行所有测试,并将结果通过电子邮件发送给团队成员。
持续集成应始终保持运行,不应失败。若失败,团队需立即处理,视其为紧急事件。“立刻中止”所有工作以解决问题。
7.3 结论
在开发方与业务方的沟通中,交流细节常引发误解。双方可能在沟通后各自理解不同,要解决这个问题,编写自动化验收测试是唯一有效的方法。这些测试因其正式性和权威性而避免模糊,确保对系统真实需求的准确反映。自动化验收测试不仅是需求文档,更是对需求的精确验证工具,减少了沟通过程中的误解。
八、测试策略
专业开发人员会测试自己的代码。但是,测试并不就是写一些单元测试或验收测试那么简单。编写这些测试只是万里长征的第一步。每个专业的开发团队都需要一套好的测试策略。
8.1 QA 应该找不到任何错误
尽管公司可能设有独立的 QA 小组进行软件测试,但开发团队应以“QA 找不到任何错误”为目标。尽管这是个高标准,但对 QA 发现的问题,开发团队应认真对待,反思并措施改进。
8 .1.1 QA 也是团队的一部分
QA 和开发人员并非敌对关系,相反,QA 和开发人员应该紧密协作,携手保障系统的质量。
8.1.2 需求规约定义者
作为需求规约定义者,QA 与业务人员一起创建自动化验收测试,作为系统真正的需求规约文档。每轮迭代中,他们将业务需求转化为描述系统行为的测试。通常,业务人员负责正常路径测试,而 QA 负责极端情况、边界状态和异常路径的测试。
8.1.3 特性描述者
作为特性描述者,QA 通过探索式测试反馈系统实际运行情况给开发和业务团队。QA 的任务不是解析需求,而是鉴别系统的真实状态,帮助识别和改进系统的运行,这有助于提升系统的整体质量。
8.2 自动化测试金字塔
专业开发人员通过测试驱动开发创建单元测试,并利用验收测试定义系统需求。持续集成用于提升质量,这些测试构成了全局测试体系。为了确保 QA 无法发现错误,除了单元和验收测试,还需要更高层次的测试。
自动化测试金字塔,直观显示所需的测试种类。
8.2.1 单元测试
在测试金字塔的底部是单元测试,由程序员使用开发语言编写,目的是在最低层次上定义系统功能。开发人员通过“先写测试,再写代码”的方法确保代码符合预期。这些测试作为持续集成的一部分运行,以保证代码意图。单元测试可实现接近 100%的覆盖率,理想情况下应保持在 90%以上。这要求真实的覆盖率,而不仅仅是通过的测试。
8.2.2 组件测试
组件测试是验收测试的一种。通常,它们是针对系统的各个组件而编写的。系统的组件封装了业务规则,因此,对这些组件的测试便是对其中业务规则的验收测试。
组件测试围绕组件而写。它向组件中传入数据,然后收集输出数据。它会测试实际输出是否符合预期的输出。组件测试由 QA 和业务人员编写,开发人员提供辅助。
组件测试差不多可以覆盖系统的一半。它们更主要测试的是成功路径的情况,以及一些明显的极端情况、边界状态和可选路径。大多数的异常路径是由单元测试来覆盖测试的。在组件测试层次,对异常路径进行测试并无意义。
8.2.3 集成测试
这些测试对大型、组件众多的系统尤为重要。它们将组件组合成组,测试相互通信是否正常,并使用模拟对象和测试辅助工具与其他组件解耦。集成测试关注组件之间的协调,而非业务规则,确保组件正确连接、通信畅通。
通常由系统架构师或主设计师编写,以确认系统架构的正确性。此阶段也可能进行性能和吞吐率测试。集成测试多使用与组件测试相同的语言和环境编写,因其运行时间较长,通常不纳入持续集成。但根据需要,可设定周期性运行,如每天或每周一次。
8.2.4 系统测试
这些测试针对完全集成的系统进行,属于最终的集成测试,旨在验证系统的整体组装和各组件间的正确交互,而非直接测试业务规则。此层次的测试应包含吞吐率和性能测试。
系统测试通常由系统架构师和技术负责人编写,使用与 UI 集成测试相同的语言和环境。测试频率根据运行时间而定,尽量提高频率。系统测试约占全部测试的 10%,其目标是确保系统构造正确,而系统行为的正确性由较低层次的测试验证。
8.2.5 人工探索式测试
这种测试需要人工操作,通过键盘输入和观察屏幕进行,既不是自动化也不是脚本化的测试。目的是在验证系统预期行为的同时,发现意料之外的行为。这类测试依靠人类智慧和创新能力,对系统进行深入探索,预先编写测试计划可能会降低效果。
8.3 结论
TDD 很强大,验收测试是表达和强化需求的有效方式。但它们都只是整体测试策略的一部分。为了更好地做到“QA 应该找不到任何错误”,开发团队要和 QA 紧密协作,创建由单元测试、组件测试、集成测试、系统测试和探索式测试构成的测试体系。应该尽可能频繁地运行这些测试,提供尽可能多的反馈,确保系统始终整洁。
九、时间管理
若想要在每天固定的时间内做更多的事,更高效的工作。就要对时间进行管理和规划。
9.1 会议
会议是必须的,会议的成本是很高的,会花费大量的时间。
如果会议没有现实且显著的成效,专业人士会主动拒绝。
9.1.1 拒绝
不需要参加所有受邀会议,过多参与反而显得不够专业。合理安排时间,谨慎选择参加的会议并礼貌拒绝不必要的邀请。负责管理时间的是你自己,确保参会对当前工作有实质帮助。如果会议没有必要参与,就要判断是否能承担时间成本,并以自己项目为优先。对于非必要的命令性会议,可以与同事和主管商量。优秀的领导应支持和帮助下属选择性地拒绝不必要的会议。
9.1.2 离席
会议并不总是如计划般进行。有时会议会变得冗长无聊,这时你可以礼貌地离开,而不是浪费时间。管理好自己的时间是你的责任。若发现会议没有意义,应选择合适时机礼貌退场,或建议加快讨论。继续参与无意义的会议是不专业的,因为时间和精力需要合理分配。
9.1.3 确定议程与目标
我们愿意承担开会的高成本,是因为有时需要所有参与者共同完成目标。为合理利用时间,会议必须有清晰的议程、明确的目标和时间分配。在接到会议邀请时,需确认议题、时长和预期成果,不明确可礼貌拒绝。若会议偏离议程,应要求明确新的议题和计划,否则适时礼貌离席。
9.1.4 立会
敏捷开发中的“立会”要求所有参会者站立并依次回答三个问题:昨天做了什么、今天计划做什么、遇到哪些问题。每人回答时间不超过 1 分钟,确保即使是 10 人的小组,会议也不超过 10 分钟。
9.1.5 迭代计划会议
迭代计划会议在敏捷开发中难度较高,但技巧可学,避免浪费时间。此会议用于选择下轮迭代任务,需先评估任务的开发时间和业务价值,最好完成验收测试或有概略方案。会议应简明快速,每个任务讨论5到10分钟,如需深入讨论则另行安排。会议时间不应超过每轮迭代的5%,如一周迭代则会议应控制在2小时内。
9.1.6 迭代回顾和 DEMO 展示
这类会议在迭代末尾召开,团队成员回顾迭代成败,业务方观看最新成果演示。为避免浪费时间,可在最后一天工作前 45 分钟召开,20 分钟回顾,25 分钟演示。涉及内容仅限于最近一两周的工作,讨论内容应简洁。
9.1.7 争论/反对
无法在 5 分钟内解决的争论不能通过辩论解决,因其依据信念而非事实。在技术争论中,没有数据支持的观点难以达成一致,需用数据说话。有时个人试图凭声势赢得争论,但长远看,强势无法解决问题,最终需依赖数据。有的人在争论中表现被动,表面同意却不积极参与,这是极不专业的行为,如同意则需行动。
为解决争论,需要实验、模仿或建模获取数据,某些情况下甚至可用投币决定。如果结果有效则选择正确,否则调整选择。明智的做法是设定评估时间或标准,以便必要时调整策略。
要警惕只为发泄情绪或站队的会议,如果内容片面则应避免参加。若必须解决争论,应在会议中要求各方在 5 分钟内阐明问题并投票,整个过程不超 15 分钟。
9.2 注意力点数
编程需要持续的注意力,这是一种稀缺的“注意力点数”。耗尽后,需要花一个小时或更多时间做不需专注的事来恢复。注意力点数影响专注度,职业开发人员会合理安排时间,充分利用这些点数。当注意力充足时编程,匮乏时做其他事。注意力点数会随时间流逝而减少,会议、忧虑和分心都会耗尽它们,导致编程时缺乏专注。
9.2.1 睡眠
睡眠至关重要,充分休息后注意力最充足。睡足 7 小时能支持 8 小时高效工作。专业开发人员通过良好睡眠安排,确保早晨以充沛的注意力投入工作。
9.2.2 咖啡因
适量咖啡因能提升注意力,但过量会导致注意力分散。咖啡因的效果因人而异。咖啡、可乐、茶。
9.2.3 恢复
注意力点数在放松时能慢慢恢复,如漫步、聊天或看窗外。一些人会选择冥想、小睡、听播客或看杂志。注意力耗尽时,虽然还能写代码,但质量堪忧,可能需要重写。因此,花 30 到 60 分钟转换心情是更明智的选择。
9.2.4 肌肉注意力
搏击、太极、瑜伽、撸铁等体力活动需要肌肉注意力,区别于编程需要的心智注意力。肌肉注意力有助于提高心智注意力的上限。
9.2.5 输入与输出
关于注意力,关键在于平衡输入和输出。编程需要创造力,接触他人的创意思维可以激发自己的创造力。可以通过阅读科幻小说等来激发软件开发的灵感。
9.3 时间拆分和番茄工作法
番茄工作法是众所周知的,这一方法简单而有效:设置一个 25 分钟的计时器,全神贯注于工作,处理干扰推迟到计时结束。25 分钟后,处理其他事情,休息 5 分钟。每 4 个工作周期休息 30 分钟。这个方法将时间分为高效的番茄时间和处理杂事的非番茄时间,帮助提高生产率。通过记录和分析番茄时间段,可以了解每日的有效工作时间。番茄工作法的好处在于可在 25 分钟内专心工作,并有理由拒绝干扰。
9.4 要避免的行为
工作时分心可能源自恐慌、厌烦或不喜欢工作,从而导致优先级错乱。我们为逃避紧急任务,提升其他任务优先级,实属自欺欺人。这种行为是为他人找借口,非专业表现。真正专业的开发人员会客观评估优先级,按实际紧急程度执行任务。
9.5 死胡同
软件开发者常会遇到走入技术死胡同的情况。坚持错误选择浪费时间,尤其当将其视为专业声誉问题时,更难回头。慎重态度和经验可避免部分死胡同,但并非全部。关键在于及时识别死胡同并勇于回头。专业开发人员应保持开放心态,接受不同意见,即便走到尽头也有其他选择。
9.6 泥潭
泥潭比死胡同更棘手,它影响速度但不完全阻止前进,让人误以为坚持比回头路更短。泥潭对团队效率的负面影响深远且持久,而像死胡同一样不可避免。慎重态度和经验虽能避开部分泥潭,但无法完全避免。
当问题复杂化,程序无法跟上需求变化时,是个转折点。尽管回头修正设计代价高,但通常是最简单的方法。继续前进可能令系统陷入泥潭,无法摆脱。专业开发者对泥潭的恐惧大于死胡同,会尽早脱身。固执于泥潭是严重的优先级错乱,是自我欺骗和误导,最终导致更大麻烦。
9.7 结论
专业开发人员注重时间和注意力管理,抵制优先级错乱,珍视声誉。保持开放心态,接受多种方案,避免执拗于单一解决方案。警惕泥潭,及时避开。最糟糕的是,开发人员徒劳努力却陷入泥潭。
十、预估
预估是软件开发中最简单却又最可怕的任务之一,涉及巨大商业价值和声誉,常导致苦恼和挫折。它是业务和开发人员之间的主要障碍,引发不信任。
10.1 什么是预估
问题在于,不同的人对预估有不同的看法。业务方觉得预估就是承诺。开发方认为预估就是猜测。两者相差迥异。
10.1.1 承诺
承诺必须兑现。专业开发人员只有在确信能够完成时才会承诺。如果要求你做不确定的事,坚决拒绝。承诺意味着确定性,兑现承诺影响他人的计划和你的声誉,未兑现即为欺骗。
10.1.2 预估
预估是猜测,无需承诺或约定,错误不会影响声誉。预估是因为无法确定所需时间。大多数开发人员不擅长预估,不是缺乏诀窍,而是对预估本质的理解不足。
10.1.3 暗示性承诺
专业开发人员能够清楚区分预估和承诺。只有在确切知道可以完成的前提下,他们才会给出承诺。此外,他们也会小心避免给出暗示性的承诺。他们会尽可能清楚地说明预估的概率分布,这样主管就可以做出合适的计划。
10.2 PERT
PERT(Program Evaluation and Review Technique)计划评审技术
10.3 预估任务
在预估时,最重要的资源是你周围的人。他们可以看到你看不到的东西。相比自己单干,他们可以帮你更精确地预估任务。
德尔菲法
一组人集合起来,讨论某项任务,预估完成时间,然后重复 “谈论 - 预估” 的过程,直到意见统一。
- 亮手指方法:参与者针对任务复杂性伸出 0 到 5 根手指,若手指数一致,讨论下一任务;否则,继续讨论直到趋于一致。统一并非绝对,只需接近即可。通过亮手指确定估算单位,避免受他人影响。
- 这是一种用于任务评估的工具。参与者根据任务将牌面向下,每人选择一张牌亮出,达成共识即认可估计,否则继续讨论。通常使用0, 1, 3, 5, 10这五个点数进行评估。
- 关联预估是一种德尔菲法的特殊形式,任务写在卡片上,无预估信息。参与者围坐,将卡片按任务所需时间排序。可随时移动卡片,若过多次需讨论。讨论后按时间单位给任务归类,常用斐波那契数列(1, 2, 3, 5, 8)。
- 三元预估中需进行三种预估来得出概率分布,无论使用何种形式,都能快速得到任务的乐观与悲观预估值。例如,用规划扑克时,可要求依据悲观预估亮出纸牌,选出点数最大者;乐观预估选点数最小者。
大数定律
预估是非常容易出错的,所以才叫预估。控制错误的办法之一是使用大数定律。该定律的意思是:把大任务分成许多小任务,分开预估再加总,结果会比单独评估大任务要准确很多。这样做之所以能提高准确度,是因为小任务的预估错误几乎可以忽略,不会对总的结果产生明显影响。
坦率地说,这也是比较乐观的想法。预估中的错误通常会被低估而不是高估,所以拆分再加总很难做到完美。不过,把大任务拆分成小任务分开预估,仍然是个好办法。有些错误会被忽略,而且拆分成小任务也更利于理解任务本身及其它意外因素。
10.5 结论
专业开发人员只在有把握的情况下承诺,会与团队合作以确保估计准确。一般通过概率预测完成时间。
十一、压力
即使有压力,专业开发人员也会冷静果断。尽管压力不断增大,他仍然会坚守所受的训练和纪律,他知道这些是他赖以战胜由最后期限和承诺所带来的压力感的最好方法。
11.1 避免压力
在压力下保持冷静的最好方式,便是规避会导致压力的处境。规避的方式也许无法完全减除压力,但是可以大大降低压力并缩短高压力期的时间。
11.1.1 承诺
为了避免无法实现承诺,需明确风险并做好准备。未经咨询的承诺不能接受,因为可能阻碍目标实现,影响公司和个人利益。专业人士要帮助业务方实现目标,但不一定得代为承诺。承诺未实现时,应由当时作出承诺的人负责。压力产生于无法完成承诺导致失败,但如果在职期间展现出色表现,至少在寻找新工作时会更有信心。
11.1.2 保持整洁
保持整洁是确保按期推进的关键。专业人士不会为赶进度而导致混乱,因为混乱只会拖慢进程。保持系统、代码和设计整洁,可避免压力和延误,因此应力求输出整洁成果。
11.1.3 危机中的纪律
选择在危机中坚持的纪律,反映了你真正的信念。即使在困境中也要保持原则,将有助于提高效率,避免陷入更大的麻烦。
11.2 应对压力
尽管努力预防,压力仍可能不可避免地到来,比如计划延迟、设计错误、团队或客户损失,或无法兑现承诺。该如何应对?
11.2.1 不要惊慌失措
正确应对压力,避免冲动。放松,深思熟虑,寻找最佳解决方案,稳步前进。
11.2.2 沟通
告知团队和主管你的困境,请求支持,避免意外状况,控制压力。
11.2.3 依靠你的纪律原则
当遇到困难时,坚持你的纪律原则。纪律在高压期尤为重要。不要失去方向,依靠自己的原则帮助走出困境。遵循 TDD,多重构代码,保持函数小,遵守已知有效的纪律。
11.2.4 寻求帮助
当头脑发热时,找到愿意结对编程的伙伴。这样能更快进步,更少出错。结对伙伴能帮助你坚守原则,提供建议,帮助你保持专注。
此外,当看到别人有压力时,也可以给予帮助,结对合作,助他们脱困。
11.3 结论
应对压力的诀窍在于,能回避压力时尽可能地回避,当无法回避时则勇敢直面压力。可以通过慎重承诺、遵循自己的纪律原则、保持整洁等来回避压力。直面压力时,则要保持冷静,与别人多多沟通,坚守自己的原则纪律,并寻求他人的帮助。
十二、协作
大多数软件都是由团队开发出来的。当团队成员能够十分专业地互相协作时,整个团队是最为高效的。单打独斗与游离于团队之外都是不专业的表现。
12.1 程序员与人
程序员通常不是因为喜欢与人共事而选择编程的。编程相对整洁可预测,而人际关系复杂不定。若能独自在房间里专注于有趣的问题,那会是最开心的时光。当然,也有程序员喜欢合作并享受挑战,但总体而言,程序员更喜欢独立思考,沉浸在问题中。
12.1.1 程序员与雇主
专业程序员应专注于满足雇主需求,理解企业业务价值,与团队紧密合作。程序员需懂业务、解决问题,确保公司长期发展。他们会花时间理解业务,与用户、销售、经理沟通,明确目标。简而言之,他们将注意力放在与业务共同进步上。
12.1.2 程序员与程序员
程序员之间通常很难密切合作,这就会带来一些不小的问题。
-
代码个体所有 不正常团队中,程序员只负责自己的代码,不让他人触碰,形成“最低秘密”壁垒。类似打印机的复杂结构,程序员各司其职,不共享技术信息。技术孤立性导致了模块混乱。我试图改变这种方式,但困难重重,因为维护自己的代码对于程序员来说至关重要。
-
协作性的代码共有权 团队共享代码权限,打破隔断,提升效率。每位成员可修改任何模块,代码属于团队而非个人。专业开发人员通过合作和互动学习,不会阻碍他人修改代码。
-
结对 许多程序员不喜欢“结对编程”,但在紧急情况下大多数人愿意结对。这是因为结对是解决问题的有效方法。即使有研究支持,我也不推荐在多大程度上应用结对。专业人士结对工作是为了分享知识和学习系统的不同部分。他们在紧急情况下能替代他人的工作。结对也是复查代码最好的方式,最终实现高效代码复查的方法就是相互协作完成代码编写。
12.2 小脑
专业人士需要合作,面对面交流能理解彼此。独立工作有时有效,尤其在需要深思或任务琐碎时,但大多数情况下,合作能提高效率。
12.3 结论
编程意味着协作,我们需要与同事合作。如果我们被关在一个科技设备齐全的房间,那看似理想,但其实不够。若想通过编程改善生活,就要学会与人沟通交流。
十三、团队与项目
小项目该如何实施?如何给程序员分派?大项目又该如何实施?
13.1 只是简单混合吗
一个项目只有一到两名程序员,工作几周即可,有时仅需一名项目经理、业务分析师和测试人员,但他们都管理和参与多个项目,每个人都按 50%至 25%的时间分配。
这种分配方式让项目之间复杂交织,没有团队协作,只是机械地组合在一起。
13.1.1 有凝聚力的团队
形成有凝聚力的团队需要时间,成员需相互了解和协作,最终形成能够创造奇迹的团队。理想情况下,团队由大约 12 人组成,包括程序员、测试人员、分析师和项目经理。建议配置为 7 名程序员、2 名测试人员、2 名分析师和 1 名项目经理。
分析师和测试人员都编写自动化验收测试,但关注点不同:分析师关注业务价值,编写成功路径场景;测试人员关注正确性,编写失败和边界场景。
项目经理负责跟踪进度,确保团队理解时间表和优先级。部分团队成员可能兼任团队教练或 Master 角色,确保进展顺利,并在团队面临压力时提供支持。
-
发酵期 成员需时间克服差异形成真正团队凝聚力,可能需要6个月至1年。一旦形成,团队将共同计划、解决问题。不要因项目结束解散团队,避免浪费可笑。最好的方法是不断给予新项目。
-
团队和项目,何者为先 银行和保险公司用项目构建团队,效果不佳。短期停留无法形成凝聚力,成员难以有效配合。专业组织应将项目分配给已有凝聚力的团队,团队可承接多项目,依成员特长分工,确保完成。
13.1.2 如何管理有凝聚力的团队
每个团队都有自己的速度,即在一定时间内能完成的工作量,通常用“点数”来衡量。速度是一个统计性的度量,例如,一个团队某周完成 38 个点,下一周 42 个,再后一周 25 个,逐渐得出平均值。管理人员可以为团队项目设定目标,并根据项目需要合理分配团队精力,比如将 50 个点分配为 15、15、20。
这种方法的优势在于灵活性,允许在紧急情况下重新分配资源。如果某项目 B 紧急,则可集中团队精力优先处理。然而,对某些临时改变优先级的团队而言,快速适应可能有难度。
13.1.3 项目承包人的困境
有人反对我的方法,认为这样会让项目承包人失去安全感和权力。他们担心团队组建和解散成本高,公司不会轻易调整。
另一方面,多任务时,项目优先级可能变更,影响承包人的安全感,资源也可能抽走。
我认同后者。组建解散团队是管理问题,公司不应受困。若优先级改变,应迅速重配资源,让承包人明确项目价值,获得支持。
13.2 结论
团队比项目更难构建。因此,组建稳定团队并让其在项目中共同工作是较好的做法。团队可同时承接多项目,需给足时间形成凝聚力,持续合作,成为项目交付的动力。
十四、辅导、学徒期与技艺
学校能够传授的是计算机编程的理论。但是学校并不会也无法传授作为一名编程匠者所需掌握的原则、实践和技能。这些东西只有经由师徒个体间多年的细心监督和辅导才能获得。软件行业中像我们这样的一批人必须要面对这一事实,即指引下一代软件开发人员成熟起来的重任无法寄希望于大学教育,现在这个重任已经落到了我们肩上。建立一种包含学徒期、实习期和长期指引的机制已是迫在眉睫。