作者:阿诺
本文为阅读《代码整洁之道:程序员的职业素养》的学习体会
随着互联网在中国的蓬勃发展,程序员这个群体也在不断扩大。以至于各种程序员速成班满大街都是,号称只要上个一年半载的培训班,出来就可以进大厂拿高薪从此走向人生巅峰。培训班为了招揽学员满嘴跑火车可以理解 ,但对有志于成为程序员的群体来说,坚持专业性才是职业生涯的持久稳定之道。
一个工作了很长时间的程序员一定具有专业性吗?可能也未必,可以看一下我们身边,是不是有这样的程序员:他工作多年,出活儿虽然快但总是很糙;技能水平停留在刚入行时所学会的那些,知识一直没有更新;抱怨自己做了这么多项目,工资不涨职位不升,天妒英才。
我们当然不想在工作多年以后也成为这样的人,追求专业性就是从现在开始要去做的事情。软件工程界的传奇人物Robert C. Martin所著的《代码整洁之道:程序员的职业素养》一书为如何成为一名专业程序员,提出了自己的真知灼见。Robert江湖人称Bob大叔,是码农界的宗师人物。他也是敏捷开发方法论的创始人之一,这本书虽然篇幅不长,但却十分精干,可以说是程序员专业成长之路上的必备指南。
Bob大叔每个职场中人都知道做事情要专业这个基本道理,但很多十几年的职场老油条都未必是真正意义上的专业人士。专业性的第一条便是具备责任心。对于这一点没有人会说自己没有责任心,但回顾一下工作中的表现吧。例如作为程序员来说,是把代码写完编译通过就提交给测试人员,等着反馈了问题再去修改吗?这就是不专业的表现,负责任的做法应该是程序写完之后就要保证功能是完整自测通过的。
有的程序员也许会强调时间紧,任务急,能完成功能就不错了,再花更多时间去测试,那后面堆积的工作就更加完不成了。这种片面强调时间紧张的说法也是站不住脚的,Bob大叔作为一名美国码农,在他早期职业生涯一样要面对苛刻的时间要求。貌似在软件工程领域,时间永远都是不够的,这一点还真是不分国界了。
在做了几个糟糕的项目之后,Bob大叔痛定思痛,决定不能再这样在低水平的层级中无限循环下去了。他决心成为一个专业的程序员,他使用了很多方法论以及工具来保障软件产品质量。例如对于代码的态度,他的看法就是凡是自己写的每一行代码,都要有相应的测试代码。这看起来似乎是一件难以完成的工作,但一开始没有这样的决心,那得到的结果永远都是低水平的重复。
Bob大叔推崇测试驱动TDD开发方法,就是在实现业务逻辑之前先写好单元测试代码。这样做的好处是充分保证业务代码都经过了功能测试,并且后续增加新功能可以快速检验对既有代码的影响程度。而不是加一段代码之后还要花大量时间验证现有功能是不是正常。同时TDD方法可以倒逼程序员思考业务逻辑的设计是否合理,因为耦合得太紧的代码是无法测试的。
负责任的程序员还应当保持旺盛的学习热情。当然,对于中国的码农们来说,都知道学习的重要性,可有时候也会面对职业倦怠的问题。例如每天都有新鲜事物冒出来,层出不穷的技术框架,刚学会用的东西转眼就过时了。
所以对于技术的学习,我觉得可以从道与术两个方面来看。道为上,术次之。道所包含的应该是那些基础的思想与理论,例如计算机体系结构、操作系统理论、经典的算法,以及软件工程理念等。而术所包括的则是编程语言的用法、技术框架的功能、开源软件等,这些拿来就能用的东西。
显然,道的内容是长远重要但不紧急,术的内容是当前重要且紧急。因此我们总是把时间花在当前重要的事情上,而对长远有益的事情却是一再拖延。这样就会陷入术的坑里,疲于应付手头的工作,而无法得到在境界上的提升。
这对我来说也是要不断提醒自己的,绝对不能陷入在技术的细节里,却忽视了技术之道的学习。
众所周知,Bob大叔是敏捷式开发的发起者与践行者。对于如何保证代码质量,他推崇的是测试驱动开发方式(TDD)。
TDD的实践原则说起来也就是三项,而且很好理解:
在编写好失败的单元测试之前,不编写任何业务代码;
只要有一个单元测试执行失败,就不再写测试代码,必须将当前的问题解决掉;
业务代码恰好能让当前失败的单元测试通过即可,不用多写。
TDD的好处很多,首先就是让开发者对代码的质量具备了充分的信心。一个正式商用的系统一般都会包含数量众多的功能模块,百十来行代码还能顾得过来,到了数千行代码的时候,要想加个功能改个参数难免心里打个小鼓。而完备的单元测试可以避免这种担心,不放心跑一遍单元测试用例,有问题就解决,没问题就继续。
其次是单元测试用例本身就是最好的功能说明文档,这对于协作开发或是代码交接都是最省时间的。在传统的软件开发过程中,总会强调完备而详细的文档,但问题在于文档的编写总是滞后于代码的实现。没有人希望自己看到的是一份过时的文档,都想直接从代码入手来了解系统实现。显然,单元测试用例就是从系统的底层描述了最小粒度的功能实现。
再次就是TDD对于良好设计的倒逼作用。可能一位开发人员要经过很长时间的磨炼才能具备良好的代码设计能力,做到松耦合、正确使用设计模式等。如果遵循TDD开发方式,则非常有助于培养良好的设计能力。因为糟糕的代码实现意味着难以编写单元测试,诸多功能耦合成一个无法测试的大块代码区。这就会促使开发人员思考如何进行分解并且使用设计模式。
当然,敏捷方法大都是说起来简单,但做起来不容易的。对于TDD来说也是如此,它需要开发人员的严格自律与高度认同。即发自内心地认可并在任何时候都坚持原则。最常见到的一个场景就是老板催得急,开发者心想:“哪还有时间写测试代码,赶紧直接做业务实现,等有空了我再补上吧”。就此小坑就挖成了大坑,又回到我们熟悉的加班-改bug痛苦轮回中。
要想做到在实际工作中不因为各种理由而违背原则,进行刻意的练习是非常有必要的。借用中国人民解放军常说的一句话就是:平常多流汗,打仗少流血。幸好在网络上还是有很多资料可以使用的,卡塔练习就是从武术中借用的一个术语,意即固定的招式。当然,同样的招式练得好的可以飞花逐叶,踏雪无痕;不肯下苦功夫的只能打个酱油跑个龙套了。
资源链接:
katas.softwarecraftsmanship.org
codekata.pragprog.com
对于无论哪国的程序员来说,总是要面对紧张的工期、变幻莫测的需求,专业的态度则是要求自己要有明确的承诺。也就是对做不到的能够勇敢说不,而对于答应实现的则要求自己按时保质的完成。
在说不的前面我加上了勇敢二字,这的确是一件需要勇气的事情,因为这会承受来自同事甚至是上级的质疑。但说不的前提有两种情况:一是很清楚自己的水平边界,对于超出自己能力的事情不答应;二是不合理的要求,有时候只是上级一厢情愿地往下压而已,这也需要有明确的态度。
在职场中,谁也不愿意承认自己水平不够,也不想跟自己的上级对着干。因此经常出现的一种场景就是对于上述两种情况,程序员报之以模棱两可的回答:“好吧,我试试看吧。”。程序员的立场是“我很有可能无法实现”,而上级的理解是这就算答应下来了,可以给老板交待了。可想而知,到最后熬夜加班、赶进度、延迟发布、带病上线、客户抱怨等灾难都是这么来的了。
所以在勇敢说不的那一刻,起码这就是最糟糕的情况了,而不是心照不宣地一起滑向灾难的深渊。我自己对此是深有体会的,曾经一个项目因为对自己能力估计不足,导致在最后期限前已经明显无法完工了,但我还对上级说“只要加两天班就可以搞定了”。结果是最后一天上级对我大失所望,了解清楚情况后对我说如果我能提前明确告知他不能完成,他完全可以帮我争取工期,协调其他同事来帮忙,而不是被老板给痛骂一顿。
对于说是的情况也是类似,一位专业的程序员交出的成果不仅是一份代码,还应该包括严谨的单元测试、详细的注释、关键的设计文档。以及在测试、运维阶段积极友好地配合以解决产品的各种问题。有的时候程序员会有本能的防御心理,会将测试人员提交的问题看成是来找自己的麻烦,不仅不配合,还找各种理由搪塞、拒绝承认问题。
这种防御心理是要克服的,专业的程序员应该将精力专注于问题之上,快速地解决问题,并提醒自己不能再犯同样的错误。这样程序员的能力在提高,对自己的水平也会越来越自信,对于困难的任务也有说是的底气,从而在职场上独当一面。
这一节要讲的并不是如何正确地写代码,而是关于在进入编码工作之中应该保持怎样的状态。这可以先从一个问题开始:假如你将要坐长途汽车去一个很远的地方,路途中会有险峻的盘山公路,而你刚得知司机大哥开了一宿车,只睡了两个小时就要载着你出发了,你会登上这辆车吗?
敢说会的都是令我无比敬佩的勇士,但理智的乘客都不会把自己的生命系于这样的司机。而程序员群体熬夜是家常便饭之事,但我想客户是不会希望自己的产品是由一群红着眼睛强打精神的程序员们开发出来的。不过这又会回到紧迫的任务进度、不停变化的需求上来。中国的程序员们确实很不容易,各大互联网公司996已经是标配了,忙起来007也是常有的事。
但追求专业的程序员应该心中有数,知道自己在什么时候的生产力最大。为了上级压下来的要求而勉强为之,留下一堆充满bug和隐患的系统并不是专业的表现。这个时候就可以践行“勇敢说不,坚决说是”的原则,拒绝不能完成的工作,将承诺的工作做到最好。同时保持自己的头脑清醒,能够专注于当前的工作,坚决不打疲劳战。
虽然说并不能总是成功,但起码可以让上级知道真实的情况并且想办法协调时间和资源,避免更大的损失。程序员的专业性不仅体现在把自己的代码写好,同样也要关注到公司的利益得失。不能被动地接受一切指令,也不能出了问题之后就推诿塞责,专业性是自己在这个行业中能够发展顺遂的重要保证。
对于已经进入编码工作中时,大家都会有自己的小妙招保持专注。戴着耳机听音乐似乎是一种比较常见的做法。我有一位同事就比较狠,他写代码的时候就找一间空闲的会议室在里面工作,只有处理其他事务时才回到自己的工位。
我们其实都是自己职场之路的驾驶员,谁也不想半路翻车。而要想在这条路上把车开得顺遂快速,保持专业性才是最好的方法。
记得在读研期间,企业导师曾经问过我们一个问题:“测试人员应该在何时进入项目中?”。彼时的我们尚未有实际开发经验,凭想象都知道是开发完成以后。那位在软件行业有着多年开发管理经验的资深专家说道:“同学们,你们一定要记住我今天说的话,测试人员一定要在需求讨论阶段就参与到项目中来。”
当时我们听到之后都有些惊讶,不知道测试人员在如此之早的时候就介入项目中能有多大的效益。及至工作多年,踩过不少的坑之后,再看Bob大叔在书中所提出的验收测试观点,终于明白当年企业导师那一番话的含义。首先是对验收测试的定义,传统看法认为是在软件开发完成之后对既有功能的验证与检测。Bob大叔认为应该将这一概念前置为需求的确认完成。
相信每一位码农都经历过需求变更的折磨,网上更有无数的段子调侃产品与开发的水火不容。其实这个难题并不是无解的,一个好办法就是在开需求讨论会时把测试人员也叫上。这样当产品说“我这里需要一个登录页面”时,测试就会对“这里我具体要测什么”提出疑问,在产品和开发的共同确认下在功能要求和技术实现上达成一致。
当然,这里又会有很多的疑问抛出来说测试人员已经忙得不可开交了,就开发完成的功能都测不完,哪还有时间去参与需求呢?其实这个问题的根源在于测试人员的很大一部分工作是在给开发填坑。之前说过如果开发没有以专业态度要求自己,开发过程中不做单元测试,开发完就扔给测试,这等于是在让测试帮开发去做最基本的功能检验。这显然是一种效率极低的方式,导致测试和开发把宝贵的时间都消耗在频繁的交流上了。
所以要从根本上解决问题则是开发要转变态度,目标不是让测试去找错误,而是要让测试找不出错误来。一个很好的手段就是构建一套金字塔式的自动化测试体系。自塔底向上分别是单元测试、组件测试、集成测试、系统测试、人工探索式测试。
单元测试在前面测试驱动开发章节已经叙述过,是软件系统的最底层实现逻辑。而组件则是在底层之上的独立模块。将多个功能相关模块整合在一起又可以进行集成测试。系统测试则是最终完成的软件功能,包含所有模块的整体测试。以上这些工作都应该是开发人员与架构师完成的工作,这样测试人员就可以解放出来参与需求讨论,专注于软件质量的相关工作了。
对于职场中人来说,什么是最珍贵的?我认为应该是时间。因为自己在单位时间内的产出决定了最终的工作成果。但为什么都是同样多的时间,有的人完成了很多事情,而有的人却一件事也做不好呢?以专业的态度对待时间的利用是很重要的。
要想利用好时间不妨从两个方面来看时间都去哪儿了。首先是向内审视:自己是不是有拖延症;做事的时候注意力很难集中;总要花很长时间才能进入工作状态;长时间工作之后觉得疲惫不堪。如果有以上这些问题,那说明时间的利用率是不高的,幸好办法是有的。
每个人都可以找到适合自己的办法来提高效率,但前提是要以专业精神约束自己,自律是最基本的要求了。对于我来说,实践番茄钟工作法的体验还不错。就是每次集中精力工作半小时,然后不管干什么,立即保存退出,休息十分钟后进入下一个周期。
可能会有疑问说代码写到一半,思路被打断,这样效率会高吗?一开始还真有这样的问题,但这是因为之前习惯了长时间工作的状态,其实是可以将任务分解为半小时之内完成的。这就需要掌握好一个节奏,长时间编码之后的问题是精力涣散,反应迟钝,休息很久也未必能恢复。而番茄钟法可以使人总是维持在一个高效的状态,出活快而且好,避免因低效而导致的无意义加班。
再就是向外观察,是哪些因素导致了时间的浪费。在职场文化中,会议是一个无法避开的话题。要是说八小时工作内最浪费时间的事情,开会估计能得第一。相信谁都会有这样的经历,被群发邮件点名通知参加某个个会议,但去了之后只有一个很小的议题和自己相关,可碍于领导在场,又不能提前退场。这个时候怎么办?
专业性告诉我们要为自己的时间负责,也就是要有拒绝的勇气。当然,办法可以有很多,不一定非得要当场让领导下不来台。可以在事前和领导沟通好,说明自己手头事情的重要性,这样讨论完自己的事,跟领导微笑点个头就可以悄悄地出来了。
如果是自己组织的会议,那就是目标明确,控制好会议进程,不让会议被无关话题带偏。常见的情况是开的是需求讨论会,却在一个技术细节的实现上慷慨激昂地讨论了半小时,这显然没有必要。敏捷开发管理的Scrum方法中有一个立会的形式,就是大家站着讨论,每人一分钟,只说三件事情:昨天做了什么,今天要做什么,昨天遇到什么问题。
通过这样由内而外地分析时间流逝的原因,再寻找解决的办法,相信程序员们一定能在专业性上前进一大步。
如果说程序员最怕听到的是产品经理说“这里有个小地方要改动一下”,那么最不想回答的便是项目经理的问题“开发这个功能需要多久?”。可作为立志要成为专业人士的我们来说,就是要面对困难并且解决它。
估计工时在很多时候就是个拍脑门的活动,其准确性不会比随机抛硬币来得更好。而且现实的情况往往是预估的工时总是太乐观,完工的时间总是会超出。Bob大叔的建议是程序员们在此要区分预估与承诺的区别。
项目经理向程序员询问工时,会把程序员的回答看作承诺,而程序员自己的理解则是预估。这通常会成为双方产生冲突的矛盾点。开个玩笑,如果一个程序员没有跟项目经理拍过桌子瞪过眼睛,那么他的职业生涯是不完整的。专业的做法是程序员明确告知项目经理,预估的时间不代表承诺,仅是完成工作的可能性。
这样,双方如果能达成一致的话,那么就会意识到预估工时实际上是一个完成工作的概率。Bob大叔建议从美国军方借用一个方法,即三段式预估法。由程序员自己按照最乐观、最有可能、最悲观三种情形分别给出三个时间,然后项目经理套用公式据此计算出完工概率最高的数值。
当然,这个方法受到程序员的主观判断影响比较大。如果是经验丰富的程序员,那他的预判可能会比较准确。而一个新手程序员可能要么是过于乐观,或是过于悲观了。不管怎样,这会是一个比拍脑门要好些的办法了。
Bob大叔还提出一个方法就是群策群力式的:由项目组所有人员参与,大家各自把预估工时以保密方式写下来,统一摊牌进行比较。如果大家预估相差不多,那么项目经理可以据此作为参考。而如果有的人差距较大,则要说明原因,然后大家再统一看法。
好了,怎么预估工时的办法有了,接下来无论预估的准确还是离谱,都要面临最后期限来临的泰山压顶。相信每个程序员在找工作时,都会看到招聘广告上会写着“能承受压力”。而不专业的做法是愤怒、无奈、吼叫、跟人动手。在面对压力时程序员如何保持专业?
控制情绪。话说愤怒始于愚昧终于悔恨,生气既然没有用,何必伤心又伤身呢。不如将注意力专注于当前的问题,寻找解决的办法。
坚持原则。平常工作时坚持写单元测试,以TDD方式开发,那么即使面对压力时也要保持一贯做法。始终将产品质量放在首位,因为临时凑乎一下,后面出问题还要修补,压力只会倍增。
积极沟通。当发现不能按正常工期完成任务时要赶紧找到项目负责人沟通协调,而不能抱着侥幸心理说加个班赶回来。我就经常跟项目组成员说“我不怕情况有多坏,而是怕坏到什么程度而我却不知道”。
寻求帮助。不要羞于向同事和领导开口求助,也许一个自己觉得很困难的事情,在别人那里已经有可用的解决方案了。当然,面对别人的求助,也要积极地回应。
软件开发是智力密集型的工作,而每个人的水平高低不见得完全一致,于是程序员们会觉得独自工作强于多人合作。而程序员这个群体给外人的印象多半是工作时戴着降噪耳机,双眼隔着厚厚的眼镜片盯着电脑屏幕,在快速敲击键盘的间隙偶尔抓一下油腻稀疏的头发。这好像也符合一般人的想象,但Bob大叔对此有话要说。
首先是对于协作的意义。秉持专业精神的程序员不应该把眼光只放在和手头工作有关联的事和人上,应该将目光扩展开,了解公司的整体业务与营收方式。说到这里估计会有疑问说这不是咸吃萝卜淡操心吗,赚着打工的钱操着老板的心。我们知道老板在为他自己的公司操着心,但其实我们也是自己的老板。
打工者最普遍的心态应该是“今天我把自己的活干好了,老板就付给我一天的报酬。至于老板能不能赚到钱我管不了,反正也不是我的”。要转变这种思维只需要思考一个问题就好,老板给我们发工资的钱是从哪里来的?只要老板不是钱多的花不完,那必然是要靠经营来获得收入的,公司赚到了钱,我们的收入才是有保障的。
这么说好像有点鸡汤,但如果我们把自己当成一家公司来经营,肯定不会只考虑这个月有收入就行,以后再说。当然是希望为自己找到一个持续稳定而且具有成长性的收入方式,看到这里应该就会明白只做好自己手头那点事是远远不够的了。因为这只是把已经熟悉的工作不断重复而已,并不能拓展自己的边界。
真正意义上的协作精神应该是充分了解公司的业务结构,与产品、商务、运营、法务等各个部门都有交流。从而清楚自己掌握的这部分业务对于公司来说意味着什么,这样可以站在公司的角度去思考并决定行动。例如负责的是核心的后台业务,那么公司需要的是现有用户能稳定使用,就不能频繁升级;如果负责的是新兴业务,那快速推出并让用户试用则最重要,而不能说“等我把这个算法再写得完美一点”。
人多力量大这个道理我们从小就知道,而一个团队肯定比单独的个体能达到的成就要高。现在互联网公司和很多软件企业里流行一种成员不固定的小团队方式,即人跟项目走。一个项目立项之后,再分配若干技术人员进去,但这些技术人员并不是全职投入在这个项目中,而是身处好几个项目中,哪里需要哪里上。
Bob大叔并不赞同这种方式,他认为人与人之间的协作是需要时间来磨合并达到默契的,最终产生齐心协力的化学效应。把人打散在一个个的项目中,只会让人疲于应付,跟谁都无法形成默契,看似节约了人力成本,实则产能并不高。一个好的团队就应该是固定成员,然后让项目跟着团队走。这时会出现多个项目需要并行的压力,那就由管理层决定项目的优先级,让团队在一段时间内只需要处理优先级最高的项目即可。
这让我想起了解放军的“铁打的营盘流水的兵”来。军队的组织与作战方式是最高效的,因为这是生死存亡的必须。我想互联网与软件企业也是可以从中借鉴的,例如王牌部队的战斗力总是最优秀的,但把这支部队的人打散了分配到其他部队里去未必就是王牌了。还有在作战时,部队的作战任务肯定是简单明确的,而不是带着好几个完全不相干的任务上战场。
专业的程序员也应如优秀的战士一样,单兵作战能力要过硬,与战友配合更加重要。清楚自己的当前任务与最终目标,为赢得胜利而作战。
工匠这个词现在可能被“情怀”给玩坏了,但对于程序员来说,应该注重其所包含的专业性。我理解工匠精神包括两个方面,一是技艺,二是态度。在这里我没有用技术,因为术在我看来是基本,连起码的事都做不好,那就不用再说其他了。
技艺一词中的艺则是专业性的根本,如同说把一件事情做到极致就成了艺术。程序员在此要追求的便是我在“做事专业”一章中所说的道。当然,这也是我持之以恒的追求。技艺所要达到的境界是知识体系的完备与融汇贯通,解决问题时不再只依赖于某一种熟悉的语言和工具,而是可以信手拈来,举重若轻。犹如武功练到高明处不再与敌拼力气,四两拨千斤,轻松擒敌。
态度则是匠人本色,对程序员来说便是我们所要坚守的原则与信念。面对跳槽可以带来的短期利益,我们是否能不为所动?面对客户和老板的压力,我们是否能坚持质量优先?面对繁重的工作任务,我们是否还能挤出时间来不断学习?
在成为专业的程序员这条道路上是漫长而崎岖的,但只要不断地磨炼技艺,保持不变的态度,那么就没有什么能阻挡我们成为优秀而专业的程序员。
推荐阅读:
《代码整洁之道:程序员的职业素养》 [美]罗伯特 C. 马丁(Robert C. Martin)本书是编程大师“Bob 大叔”40余年编程生涯的心得体会的总结,讲解要成为真正专业的程序员需要具备什么样的态度,需要遵循什么样的原则,需要采取什么样的行动。作者以自己以及身边的同事走过的弯路、犯过的错误为例,意在为后来者引路,助其职业生涯迈上更高台阶。
下方查看历史文章
你写的每一行代码,都是你的名片
阿里巴巴高级算法专家威视:组建技术团队的一些思考
Mac高效开发之iTerm2、Prezto和Solarized主题
类加载器的双亲委派,看这篇就够了
本号专注于后端技术、JVM问题排查和优化、Java面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。