代码整洁之道 – 程序员的职业素养
最好的软件开发人员都知道一个秘密: 美的东西比丑的东西创造起来更廉价, 也更快捷. 构建、维护一个美的系统所花费的时间、金钱都要少于丑的系统.
美的系统是灵活、易于理解的, 构建、维护它们就是一种快乐.
优秀的程序员都因为爱好, 孜孜不倦的写了好多根本没可能去实际执行, 为人类创造价值的程序.
专业主义
“专业主义” 有很深的含义, 它不但象征着荣誉与骄傲, 而且明确意味着责任与义务. 这两者密切相关, 因为从你无法负责的事情上不可能获得荣誉与骄傲.
专业主义的精髓就在于将公司利益视同个人利益. 专业主义就意味着担当责任.
开发的软件有 bug 会损害软件的功能. 因此, 要做得专业, 就不能就下 bug.
当然, 软件开发太复杂了, 怎么可能会没 bug 呢? 的确, 但最重要的是, 要对自己的不完美负责. 代码中难免会出现 bug, 但这并不意味着你不用对它们负责; 没人能写出完美的软件, 但这并不表示你不用对不完美负责.
所谓专业人士, 就是能对自己犯下的错误负责的人, 哪怕那些错误实际上在所难免. 职业经验多了之后, 失误率应该快速减少, 甚至渐进于零. 失误率永远不可能等于零, 但你有责任让它无限接近零.
成熟的专业开发人员知道, 聪明人不会为了发布新功能而破坏结构. 结构良好的代码灵活. 以牺牲结构为代价, 得不偿失, 将来必追悔莫及.
如果你希望自己的软件灵活可变, 那就应该时常修改它
你应该每周工作60小时. 前40小时是给雇主的, 后20小时是给自己的. 在这剩余的20小时里, 你应该看书、练习、学习, 或者做其他能提升职业能力的事情.
业精于勤. 真正的专业人士往往勤学苦干, 以求得自身技能的纯熟精炼. 只完成日常工作是不足以称为练习的, 那只能算是种执行性质的操作, 而不是练习. 练习, 指的是在日常工作之余专门练习技能, 以期自我提升.
学习的第二个最佳方法是与他人合作. 专业软件开发人员往往会更加努力地尝试与他人一起编程、一起练习、一起设计、一起计划, 这样他们可以从彼此身上学到很多东西, 而且能在更短的时间内更高质量地完成更多工作.
让新人融入团队的最好办法是和他们坐到一起, 向他们传授工作要诀. 专业人士会视辅导新人为己任, 他们不会放任未经辅导的新手肆意妄为.
每位专业软件人员都有义务了解自己开发的解决方案对应的领域. 开始一个新领域的项目时, 应当读一两本该邻域相关的书, 要就该领域的基础架构与基本知识做客户和用户访谈, 还应当花时间和业内专家交流, 了解他们的原则与价值观念.
雇主的问题就是你的问题. 你必须弄明白这些问题, 并寻求最佳的解决方案. 每次开发系统, 都应站在雇主的角度来思考, 确保开发的功能能真正能满足雇主的需要.
专业人士知道自己自负, 不会故作谦逊. 他们熟知自己的工作, 并引以为荣; 他们对自己的能力充满自信, 并因此用于承担由吧务的风险. 专业人士不是胆小鬼.
需要尽可能做到的事情
- 让 QA 找不出任何问题
- 要确信代码正常运行
- 自动化 QA
- 不会为了发布新功能破坏结构
- 每次读代码, 都别忘了进行点滴的改善
说”不”
专业人士敢于说明真相而不屈从于权势。专业人士有勇气对他们的经理说“不”。
你怎么能对自己的老板说”不“呢?毕竟,他们可是你的老板啊!难道不该照你老板说的去做吗?
不应该照做。只要你是一名专业人士,那就不应该照做。
奴隶没有权利说”不“。劳工或许也对说”不“有所顾忌。但是专业人士应该懂得说”不“。事实上,优秀的经理人对于敢于说”不“的人,总是求贤若渴。因为只有敢于说”不“,才能真正做成一些事情。
你的经理要求你在明天之前完成登录页面,这就是他在追求和捍卫的一个目标,那就是尽他的工作职责。如果你明知第二天之前不可能完成登录页面,嘴上却说”好的,我会试试的“,那么便是你失职了。这时候,尽职的唯一选择是说”不,这不可能“。
最要说”不“的是那些高风险的关键时刻。越是关键时刻,”不“字就越具价值。
这一点应该不证自明。当公司存亡成败皆系于此时,你必须尽己所能,把最好的信息传递给你的经理。这往往意味着要说”不“。
我们都听说过具备”团队精神“是多么重要。具备团队精神,意味着恪尽职守,意味着当其他队员遭遇困境时你要出手相助。有团队精神的人会频繁与大家交流,会关心队友,会竭力做到尽职尽责。
专业人士常常会成为英雄人物,但这样的荣誉并非是他们所刻意追求的。他们之所以成为英雄人物,是因为他们出色地完成了任务,不但按时,而且符合预算。
说”是“
做出承诺,包含三个步骤。
- 口头上说自己将会去做
- 心里认真对待做出的承诺
- 真正付诸行动
但是,我们是不是常常碰到这种情况,其他人(当然不会是我们自己!)在作出承诺时,其实并没有包含这三个步骤?
很少有人会认真对待自己说的话,并且说到做到。有些人在说话时是认真的,但他从来不会说到做到。而更多的人在做出承诺后,几乎从不会认真去履行诺言。
你只能承诺自己能完全掌握的事。比如,如果你的目标是完成一个还依赖与另一团队的模块,那么你无法承诺自己能够完成这个模块,还能实现该模块与其他团队所负责模块的无缝集成。但你能承诺自己会采取一些具体的行动来达到最终目标。
即使目标无法完成,你仍能全力前进,离目标更近些。而弄清楚目标能否达成这件事,便是你可以采取的努力行动之一。
这样的事情时有发生。有些事情先前你可能没预料到,这很现实。但是如果你仍然希望自己能够不负众望,那就赶紧去调整别人对你的预期,越快越好!
如果你无法兑现承诺,那么最重要的就是尽早向你的承诺对象发出预警,越快越好,越早越好。
你越早向各利益相关方发出预警信号,整个团队就越有可能抓住机会,中止并重新评估当前的活动,并决定是否采取些措施或做出些改变(比如调整优先级等)。这么一来,你仍有可能达成之前的承诺,或者,用另一个承诺来代替先前的承诺。
在此,有一点相当重要:如果你不尽早告诉他人可能的问题,就错失了让他们帮助你达成目标、兑现承诺的机会。
今天的程序员肯定得去面对诸如估算、确定最后期限以及面对面交流等沟通活动。做出承诺或许听来令人有些害怕,但他能帮助程序员解决在沟通中可能发生的不少问题。如果你能够一直信守承诺,大家会以为你“是一名严谨的开发人员”。在我们这行中,这也是最有价值的评价。
如果进度比预期的要慢一些,你可能会冒险放弃原则。如果不写测试,你或许可以更快完成任务。如果不做重构,不运行整套回归测试,或许也可以更快完成任务。
此时,如果是专业开发人员,就不会放弃底线。首先,这种假设本身就是错误的。即使不写测试,不做重构,省掉完整的回归测试,你也无法更快完成 任务。多年经验告诉我们,打破这些纪律和原则,必然会拖慢进度。
其次,身为专业开发人员,你有责任根据标准规范自身工作。代码必须经过测试,代码必须要有对应的测试代码。要确保代码清晰整洁,而且必须确保没有影响到系统的其他部分。
作为专业开发人员,你已经承诺会遵守和捍卫这些标准。你做出的其他所有承诺,都应该和这份承诺保持一致。
专业人士不需要对所有请求都回答“是”。不过,他们应该努力寻找创新的方法,尽可能做到有求必应。当专业人士给出肯定回答时,他们会使用正式的承诺,以确保各方面明白无误的理解承诺的内容。
编码
能够感知到错误非常重要。说明你已经能够非常迅速的获得反馈,能够更为快速地从错误中学习。要熟练掌握每项技艺,关键是要具备“信心”和“出错感知“能力。
编码是一项颇具挑战也十分累人的智力活动。相比其他类型的活动,编码更加要求聚精会神。因为在编码时你必须平衡互相牵制的多种因素:
- 首先,代码必须能够正常工作
- 代码必须能够帮你解决客户提出的问题
- 代码必须要能和现有系统结合的天衣无缝
- 其他程序员必须能读懂你的代码
而且,编码时无可避免地会受到各种干扰。当你无法全神贯注地编码时,所写代码就有可能出错。
如果感到疲劳或者心烦意乱,千万不要编码。强而为之,最终只能回头返工。相反,要找到一种方法来消除干扰,让心绪平静下来。
软件开发是一场马拉松,而不是短跑冲刺。你无法全程一直以最快的速度赢得比赛,只有通过保持体力和维持稳定节奏来取胜。无论是赛前或者赛中,马拉松选手都会调整好自己的状态。专业程序员也会同样仔细地保存好自己的精力和创造力。
流态区
关于高效率状态,大家已经写了很多,这种状态通常称为”流态“。在这种状态下,程序员会感到效率极高;在这种状态下,会感到“绝无错误”。因此他们 一直苦苦追求进入这种状态,并经常以能在那种状态下维持多久来衡量自我价值。
一些曾经进入这种状态但最终摆脱出来的人给出了一点忠告:“避免进入流态区”。这种意识状态并非真的高效,也绝非毫无错误。这其实是一种”浅层冥想“状态,在这种状态中,为了追求所谓的速度,理性思考的能力会下降。
在流态区,你可能能够敲出更多的代码。你会收获一种愉悦感或者征服感。问题在于,在流态区状态下,你其实放弃了顾及全局,因此,你可能会做一些后来不得不推倒重来的决策。在流态区写代码可能会快些,但是后面你将不得不更多地回头审视这些代码。
帮助
编程并非易事。越年轻的程序员对此可能越没有什么感觉。但是随着经验增长,必须小心谨慎地将系统分解为易于理解的小单元,同时使这些单元直接的关系越少越好,这并非易事。
编程很难,事实上,仅凭一己之力无法写出优秀的代码。即使你的技能格外高超,也肯定能从另外一名程序员的思考与想法中中获益。
因此,互相帮助也是每个程序员的职责所在。将自己封闭在格子间或者办公室里与世隔绝,有悖于专业的职业精神。你的工作不可能重要到你不能花一丁点时间来帮助别人。事实上,作为专业人士,要以随时能够帮助别人为荣。
沟通
专业开发人员既要做好开发,也要做好沟通。“输入糟糕,输出也会糟糕”对程序与同样通用,所以职业程序员会重视与团队及业务部门的沟通,确保这种沟通的准确、流畅。
开发方与业务方之间最常见的沟通是关于需求的。业务方描述他们认为自己需要的东西,程序员按照自己理解的业务方表达的需求来开发。至少从理论上来说,应该是这样。但在现实里,关于需求的沟通是及其困难的,其中会表现出各种问题。
做业务的人和写程序的人都容易陷入一个陷阱,即过早进行精细化。业务方还没有启动项目,就要精确知道最后能得到什么;开发方还没有评估整个项目,就希望精确知道要交付什么。双方都贪求不现实的精确性,而且经常愿意花大价钱来追求这种精确。
在工作中,有一种现象叫做观察者效应,或者不确定原则。每次你向业务方展示一项功能,他们就获得了比之前更多的信息,这些新信息反过来又会影响他们对整个程序的看法。
最终结果就是,需求完成的越精细,就越容易被忽视,系统因此也谈不上完工。
首先,即使拥有全面准确的信息,评估也通常会存在巨大的变数。其次,因为不确定原则的存在,不可能通过反复推敲实现早起的精确性。需求是一定会变化的,所以追求那种精确性是徒劳的。
避免过早精细化的方法是尽可能的推迟精细化。专业人员直到到手的开发的前一刻才会把需求具体化。但是,这可能造成另一个问题:迟来的模糊性。
业务方常常会提出不同意见。这时候他们会发现,相比去解决分歧,更好的办法是换一种说法,所以会寻找各方都同意的关于需求的表述,而不是去解决争端。
需求文档的每一点模糊之处,都对应着业务方的一点分歧。当然,模糊不只来自于分歧或争论。有时候,业务方会想当然地认为看文档的人懂得自己的意思。
验收的目的是沟通、澄清、精确化。开发方、业务方、测试方对验收测试达成共识,大家都能够明白系统的行为将会是怎样。各方都应当记录这种准确的共识。在专业开发人员看来,与业务方、测试方协同工作,确保大家都明白要做什么,是自己的责任。
验收测试都应当自动进行。在软件开发的周期中,确实有时候需要手动测试,但是验收测试不应当手工进行,原因很简单:要考虑成本。
专业人员会避免手动测试。相比手动测试,自动化测试的成本非常低,让人手工执行测试脚本不划算。专业开发人员认为,实现验收测试的自动化是自己的责任。
请务必确保在持续集成系统中,单元测试和验收测试每天都能运行好几次。整套持续集成系统应该由源代码管理系统来触发。只要有人提交了代码,持续集成系统就会开始构建,并运行所有的测试,测试结果会用电子邮件发给团队的所有人。
保持持续集成系统的时刻运行是非常重要的。持续集成不应该失败,如果失败了,团队里的所有人都应该停下手里的活,看看如何让测试通过。在持续集成系统里,失败的集成应该视为紧急情况,也就是“立刻终止”型事件。
测试
尽管公司可能设有独立的 QA 小组专门测试软件,但是开发小组仍然要把“QA 应该找不到任何错误”作为努力的目标。
当然,这个目标定得有点高。毕竟,如果有一群聪明人联合起来绞尽脑汁找出产品中所有的瑕疵和不足,他们肯定是能够找出一些问题的。对 QA 找到的每一个问题,开发团队都应该高度重视、认真对待。应该反思为什么会出现这种错误,并采取措施避免今后重犯。
会议
会议的成本非常高。如果你去算算会议的成本,你会很吃惊。
关于会议,有两条真理:
- 会议是必须的
- 会议浪费了大量时间
受到邀请的会议没有必要全部参加。参加的会议太多,其实只能证明你不够专业。你应该理智的使用时间,所以必须谨慎选择,应当参加哪些会议,礼貌拒绝哪些会议。
邀请你参加会议的人并不负责管理你的时间,为时间负责的只有你。所以,如果你收到会议邀请,务必确保出席会议可以给自己目前的工作带来切实且显著的成效,否则不必参与。
还有些时候,有职权的人(比如其他项目的高级工程师或者主管)命令你必须参加某些会议。这时候应当问问自己,他们的职权是否比自己的工作计划更重要。同样,自己团队的同事和领导也可以帮忙决策。
领导的最重要责任之一,就是帮你从某些会议脱身。好的领导一定会主动维护你拒绝出席的决定,因为他和你一样关心你的时间。
如果会议让人厌烦,就离席。仔细管理自己的时间是你的责任。如果你发现参加某个会议是在浪费时间,就应当想个礼貌的办法退出来。
重要的是,你应当明白,继续待在会议室里是浪费时间;继续参加对你没有太多意义的会议,是不专业的行为。
优先级
专业人员评估每个任务的优先级,排除个人的喜好和需要,按照真实的紧急任务来执行任务。
专业开发人员会用心管理自己的时间和注意力。他们知道优先级错乱的诱惑,他们也珍视自己的声誉,所以会抵制优先级错乱。他们永远有多种选择,永远敞开心扉听取其他解决方案,他们从来不会执拗于某个无法放弃的解决方案。他们也时刻警惕着正在暴露的泥潭,一旦看清楚,就会避开。最糟糕的事情,莫过于看到一群开发人员在徒劳地拼力工作,结果却陷入越来越深的泥潭。
预估
预估是软件开发人员面对的最简单,也是最可怕的活动之一了,预估影响到的商业价值巨大,关乎声誉,也给我们带来了很多的苦恼和挫折。预估是业务人员和开发人员之间最主要的障碍,横亘在双方之间的种种不信任,几乎由它引发。
什么是预估
问题在于,不同的人对预估有不同的看法。业务方觉得预估就是承诺。开发方认为预估是猜测,两者相差迥异。
专业开发人员不随便承诺,除非他们确切知道可以完成。道理就是这么简单。如果你被要求承诺做自己不确定的事情,那么就应当坚决拒绝。如果要求你承诺在某天完成,但是需要每天加班,周末加班,取消休假,那么最后的决定取决于你;不过,不要违背自己的意愿去勉强。
承诺是关于确定性的。其他人会把你的承诺当真,据此拟定计划。如果不能兑现承诺,他们的损失,以及你的声誉受到的影响,都是巨大的。不能兑现的承诺也是一种欺骗,只不过比明目张胆的欺骗好一点。
预估是一种猜测。它不包含任何承诺的色彩。他们不需要做任何约定。预估错误无关声誉。我们之所以要预估,是因为不知道到底要花多少时间。
不幸的是,大多数软件开发人员都很不擅长预估。这不是因为他们没有掌握关于预估的诀窍——根本没有这样的诀窍。预估的偏差总是很大,原因在于我们并不理解预估的实质。预估不是个定数,预估的结果是一种概率分布。
专业开发人员能够区分预估和承诺。只有在确切知道可以完成的前提下,他们才会给出承诺。此外,他们也会小心避免给出暗示性的承诺。他们会尽可能地说明预估的概率分布,这样主管就可以做出合适的计划。
压力
即使有压力,专业开发人员也会冷静果断。尽管压力不断增大,他仍然会坚守所受的训练和纪律,他知道这些是他赖以战胜最后期限和承诺所带来压力的最好方法。
在压力下保持冷静是最好的方式,便是会规避导致压力的处境。规避的方式也许无法完全减除压力,但是可以大大降低压力并缩短高压力期的时间。
业务人员可能在没有事先咨询我们的情况下就向客户做出了承诺。发生这种事情,出于责任感我们必须主动帮助业务方找到方法来兑现这些承诺,但是一定不能接受这些承诺。
专业人士总会千方百计地帮助业务方找到达成目标的方法,但并不一定要接受业务方代为做出的承诺。最终,如果我们没有兑现业务方所作出的承诺,那么该由当时做出承诺的人来承担责任。
如果因为没能兑现承诺而导致业务失败了,你也将无法按时拿到薪水,这种情况下可能能感受不到压力。但是,如果此前你已经表现得十分专业,那么至少在找新工作时可以昂首挺胸问心无愧。
快速前进确保最后期限的方法,便是保持整洁。专业人士不会为了快点前进而乱来。他们明白“快而脏”是自相矛盾的说法。脏乱只会导致缓慢!
协作
大多数软件都是由团队开发出来的。当团队成员能够十分专业地互相协作时,整个团队是最为高效的。单打独斗与游离与团队之外都是不专业的表现。
专业程序员的首要职责是满足雇主的需求。这意味着你要和你的经理们、业务分析师们、测试工程师们和其他团队成员很好地协作,深刻理解业务目标。这并不是说你必须要成为业务方面的专家,而是说你需要理解手头上正在编写的代码的业务价值是什么,了解雇你的企业将如何从你的工作中获得回报。
专业程序员最糟糕的表现是两耳不闻窗外事,只顾一头将自己埋在技术堆里,甚至连公司业务火烧眉毛行将奔溃了也不闻不问。你的工作职责就是要让业务免于陷入困顿,让公司可以长久发展下去。
因此,专业程序员会花时间去理解业务。他们会和用户讨论他们正在使用的软件,会和销售人员与市场人员讨论所遭遇的问题,会和经理们沟通,明确团队的短期目标和长期目标。
简而言之,他们会将注意力放在与业务同舟共济上。
程序员与程序员
程序员直接通常很难密切合作,这就会带来一些不小的问题。
代码个体所有
不正常的团队最糟糕的症状是,每个程序员在自己的代码周边筑起一道高墙,拒绝让其他程序员接触到这些代码。我曾在需要地方看到过,不少程序员甚至不愿让其他程序员看见他们的代码。这是引发灾难的“最有效秘诀”。
协作性的代码所有权
将代码所有权的各种隔断全部打破、由整个团队共同拥有全部代码的做法,相较于此则要好得多。
团队中每位成员都能签出任何模块的代码,做出任何他们认为合适的修改。专业人士期望拥有代码的是整个团队,而非个人。
专业开发人员是不会阻止别人修改代码的。他们不会再代码上构造所有权的潘篱,而是尽可能多地互相合作。他们通过合作来达到学习的目的。
结对
“结对编程”是解决问题最有效的方法。专业人士会结对工作。
专业人士结对工作,还因为这是分享知识的最好途径。专业人士不会仅凭一己之力从零开始创建知识,而是通过互相结对来学习系统的不同部分和业务。他们明白,尽管每位团队人员都有自己的位置,但是在紧要关头,每位团队成员也要能够接替其他人的位置。
专业人士之所以结对,是因为结对是复查代码最好的方式。系统中不应该包含未经其他程序员复查过得代码。代码复查的方法很多,但大多数方法效率都极其低下。最有效率且最有效果的代码复查方法,就是以互相协作的方式完成代码编写。
团队与项目
形成团队是需要时间的。团队成员需要首先建立关系。他们需要学习如何相互协作,需要了解彼此的批号、强项、弱项,最终,才能凝聚成团队。
有凝聚力的团队确实有些神奇之处。他们能一起创造奇迹。他们互为知己,能够替对方着想,互相支持,激励对方拿出自己最好的表现。他们攻无不克。
成员克服个体差异,默契配合,彼此信任, 形成真正有凝聚力的团队,是需要一些时间的,可能需要6个月,甚至1年。但是,凝聚力一旦真正形成,就会产生一种神奇的魔力。团队的成员会一起做计划,一起解决问题,一起面对问题,一起搞定一切。
团队已经有了凝聚力,但却因为项目结束了就解散这样的团队,则是极其荒谬的。最好的做法是不拆散团队,让他们继续合作,只要不断地把新项目分派给他们就行。
专业的开发组织会把项目分配给已形成凝聚力的团队,而不会围绕着项目来组建团队。一个有凝聚力的团队能够同时承接多个项目,根据成员各自的意愿、技能和能力来分配工作,会顺利完成项目。
团队比项目更难构建。因此,组件稳健的团队,让团队在一个又一个项目中整体移动共同工作是较好的做法。并且,团队也可以同时承接多个项目。在组建团队时,要给于团队充足的时间,让他们形成凝聚力,一直共同工作,成为不断交付项目的巨大引擎。