最好的软件开发人员知道一个秘密:美的东西比丑的东西创建起来更廉价、也更快捷。美的系统是灵活、易于理解的,构建和维护它们就是一种快乐。
我们需要招聘的不是“经历丰富”的人,而是“有职素养”的人。并不是问题的难度,而是解决问题的方式、步骤以及反思的深度,更能体现一个人的职业素养。
如果只会快速编码,却不关心代码背后的意义,不能迅速判断、解决程序运行中的问题,不能自信满满地为自己交付的程序承担责任,同样是与职业素养绝缘。
本文是 程序员的职业素养 的读书笔记。
1 专业主义
专业不但象征着荣誉与骄傲,而且明确意味着责任与义务。
专业主义的精髓就在于将公司利益视同个人利益,专业就意味着担当责任。
首先,不行损害之事:
- 不要破坏软件功能,不能留下 bug,将测试自动化
- 不要破坏结构,软件要易于修改
为了证明软件易于修改,你应该随时做点简单的实际的修改。这又要求我们有一个覆盖率极高的自动化测试。
职业发展是自己的事儿,雇主没有义务培训你,给你买各种书籍。
你应该计划每周工作 60 小时,前 40 小时是给雇主的,后 20 小时里,你应该看书、练习、学习,或者做其他提升职业能力的事情。
职业道德:
- 了解你的领域:设计模式、设计原则、方法、实践、工件
- 坚持学习,关注博客,参加技术大会,读相关文章,访问用户
- 练习
- 与他人合作,一起编程、一起练习、一期计划
- 传道授业,教学相长
- 了解业务领域,知道为什么设计这样的 spec
- 与雇主和客户保持一致,他们的问题就是你的问题
- 谦逊,切忌自负,承认自己的力不从心。嘲讽别人只会自作自受。
2 说“不”
专业人士敢于说明真相而不屈从于权势,并有勇气对自己的经理说”不“。
要做出艰难决定的时候,存在对抗角色间的冲突于此是最为有利的。你的经理指望你能像他一样捍卫自己的目标,这样你们才能达到可能的最好结果。
要有团队精神,意味着当其他队员遭遇困境时你要施予援手。
“好的,我试试看”是最差的回应。许诺“尝试",意味着你承认之前未尽全力。而且,只要你许诺”尝试“,你其实在许诺你会确保成功。这样压力就让自己来扛了。
委曲求全,并非问题的解决之道,还可能制造出更多的麻烦。
3 说“是”
作出承诺,包含3个步骤:
- 口头上说
- 心里认真
- 付诸行动
在承诺做某事时,留意自己的用词,它们透露了我们对待承诺的认真程度,要避免:
- 需要/应当
- 希望/但愿
- 让我们(而不是让我)
真正的承诺听起来像“我将在 。。。之前。。。”:
- 对事情有清晰的事实陈述
- 明确说明了 deadline
如果因为以下原因没有“言必信,行必果”,你应该有些应对方法
- 因为寄望于某某去做这件事:你只能承诺自己完成的事情
- 因为不太确信是否真能完成的了:努力缩短与目标之间的距离
- 因为无能为力:要立刻调整别人对你的预期,越快越好!
专业人士对自己的能力极限了如指掌,十分清楚自己还能保持效率加班多就,也明白要付出的代价。
4 编码
编码要更加聚精会神,因为你必须平衡相互牵制的多种因素:
- 代码必须能够正常工作。为此要理解要解决的是什么问题
- 代码必须解决用户提出的问题,与客户交流并定义问题
- 代码必须与现有系统结合地天衣无缝
- 其他程序员能读懂你的代码
当你心烦意乱或者疲惫时,千万不要编码!奉献精神和职业素养,是指遵守纪律原则而非长时间工作。
在“流态区”,你可能会敲出更多代码,但你没有顾及全局。因此你可能做出一些后来不得不推到的决策。
类似于单核工作法的全局模式
音乐可能把你带入流态区。
中断:被打断无法避免,要维护住上下文的做法有:结对编程和 TDD。
当你死活写不出代码时,试着结对编程,或者来点创造性输入(科幻小说)。
要想避免难熬的调试时间,最好的办法就是测试驱动开发。
软件开发是一场马拉松,要保存体力和维持稳定节奏来取胜:
- 疲劳时就停下来吧
- 从问题中脱离出来,可能会获得意想不到的创意:开车时,洗澡时
管理拖延的诀窍是,早期检测和保持透明。
- 根据目标定期衡量进度
- 及时将进度送达利益相关者
当经理极力要求你赶上 deadline,你应该维持你的估算。
明确定义“完成”来避免交付失误。最好的办法是创建一个自动化的验收测试。
帮助他人:要清楚团队伙伴的状态,随时准备提供帮助,全情投入的帮助。反过来,自己受阻时,也要即使请求别人的帮助。
5 测试驱动开发
TDD 的 3 项原则:
- 在编好失败单元测试之前,不要编写任何产品代码
- 只要有一个单元测试失败了,就不要再写测试代码;无法编译也是一种失败情况
- 产品代码恰好能够让当前失败的单元测试成功通过即可,不要多写。
遵循这 3 个法则,大概 30s 就要运行以一次代码。
单元测试不是一开始把 case 全部写完,而是 case -> 代码 -> case -> 代码 这种小步前进。
TDD 拥有一套值得信赖的测试,完全可以大小对修改代码的恐惧。
单元测试即文档:遵循 3 项法则,那么对系统中的每个对象,单元测试都可以清楚描述对象的创建方法;对于系统中的每个函数,单元测试都可以描述各种有有意义的调用。
测试先行的时候,迫使你思考什么是好的设计!
6 练习
速度快不见得是好事,更好的做法是慢下来,仔细思考。有些时候,尽可能快地编译和测试,也可以带来很高的生产率。
练习的意义:真正做出反应的是你的身体,大脑则是在更高级的层次上思考。而身体的反应,都来自练习!
常见的练习:
- 学习热键和导航操作,以及测试驱动开发、持续集成之类的方法
- 一个人写单元测试,一个写程序通过测试。然后交换角色
- 解决一个实际的问题,多人参与
程序员容易受到一种限制,即所解决问题的种类比较单一,怎么拓展自身的经验呢?
- 为开源项目贡献
- 用自己的时间练习
7 验收测试
职业程序员会充实与团队及业务部门的沟通,并确保这种沟通准确、顺畅。
做业务的和写程序的人都容易陷入“过早精细化”的陷阱:
- 业务方看到真正的运行情况,就会调整自己对系统的看法。
- 开发人员应该明白,评估可以,而且必须基于不那么精确的需求。评估只是评估
迟来的模糊性:需求文档的模糊之处,都应对着业务方的一点分歧。有时,业务方会想当然以为看文档的人懂了自己的意思。模糊性的问题只能从“验收测试”来解决。
不同团队对“完成”的定义各不相同。完成应该是代码写完了、测试通过、需求方已经认可。要达到这种程度而不影响迭代速度,要有一整套的自动化测试。
验收测试的目的是沟通、澄清、精确化。开发、业务、测试对验收测试达成共识,大家明白系统的行为应该是怎样。
专业开发人员认为,实现验收测试的自动化是自己的责任。
写测试不是什么额外的工作,而是确保系统的各项指标符合要求,这样程序员才能确知“完成”。自动化测试是节省时间和金钱的方法,也可以避免开发人员误入歧途。
通常,业务员测试“正确路径”,QA 测试“错误路径”、边界条件、例外情况。
迭代开始的第一天,就应该准备好最初的几项验收测试。然后每天都应当完成一些测试。到迭代的中间点,所有的测试都应该准备完毕。
身为专业开发人员,不能被动地接受测试,要与编写测试的人协商并改进测试。
验收测试,不是单元测试。
- 单元测试是程序员写给程序员的,是正式的设计文档,描述了底层结构以及代码的行文。验收测试是业务方写给业务方的,是正式的需求文档,描述了系统该如何运行。
- 单元测试深入系统内部进行,调用特定类的方法;验收测试是在系统外部,通常在 API 或 UI 级别进行。
单元测试和验收测试,首先是文档,其次才是测试。
GUI 测试
要把 GUI 看做 API 那样处理,验收测试就简单多了。编写 GUI 测试时,必须使用 GUI 背后相对稳定的抽象元素(比如通过 id 来指定 button)。
持续集成
确保每次提交代码,持续系统系统就开始构建、并运行所有的测试,并将结果通知团队的所有人。
CI 失败,所有人应该立刻停下手里的活,这是紧急情况。
8 测试策略
目标:QA 找不到任何错误。
QA 也是团队的一部分,QA 和开发人员应该紧密协作,共同保证协同的质量。QA 扮演的角色是:
- 需求规约的定义者。QA 的任务是与业务人员一起构建自动化验收测试,作为系统真正的需求规约文档。
- 特性描述者。QA 应当描述系统运行中的真实情况,并反馈给开发和业务人员。
自动化测试金字塔
- 单元测试(100%覆盖率):先写测试、在写产品代码。覆盖大多数的异常路径。
- 组件测试:验收测试的一种。测试中需要使用 mocking 或 test-doubling 技术,解开与其他组件的耦合。组件测试差不多可以覆盖系统的 50%,主要测试成功路径的情况,以及一些明显的极端情况、边界状态、可选路径。
- 集成测试:测试组件之间能否正常通信。
- 系统测试:最终的集成测试,不直接测试业务规则,而是测试系统是否正确组装完毕,以及各部件能否正确交互。应该包括吞吐量测试和性能测试。
- 人工探索式测试,非脚本化的测试,需要人类智慧的介入。可以搞一个抓 bug 活动。
9 时间管理
会议
会议是必须的,但会浪费大量的时间:
- 谨慎选择要参加的会议
- 如果会议让人厌烦,就离席
- 确定议程与目标,否则可以礼貌拒绝这个会。
- 立会:昨天干了什么?今天打算干什么?我遇到了什么问题?
- 迭代计划会议:评估可选择任务的开发时间、确定这些任务的业务价值。
- 迭代回顾或 DEMO:只牵扯到最近一两周的工作,不有太多内容
凡是不能再 5 分钟解决的争论,都不能靠争辩来决绝。最后的出路就是用数据说话。
注意力
- 睡眠。再怎么强调也不为过
- 恢复。注意力不集中的时候,可以通过沉思、反省、小睡、听播客、翻翻书来恢复
- 肌肉注意力。肌肉注意力有注意改善心智注意力
- 输入输出。多接触其他人的创造性思维,比如阅读科幻小说
时间拆分与番茄工作法
番茄工作法来记录和跟踪自己的时间,在 25min 内全身心投入,拒绝任何干扰。
要避免的行为
优先级错乱是自我麻痹的谎言,因为不能面对真正要做的事情。
专业人员会评估每个工作的优先级,排除个人的喜好和与澳,按照真是紧急程度来执行。
死胡同
慎重的态度和积累的经验可以让你避免某些死胡同,但如果不小心走进去,要有足够的勇气回头
专业人员要保持开放的头脑,虚心听取其他意见。
泥潭
走回头路(重新修正设计)是最简单的办法。
10 预估
预估是一种概率分布。
业务方觉得预估就是承诺,开发方认为预估就是猜测。
专业人员不会随便承诺,除非他们确切知道可以完成。同时,专业人员也应该避免给出暗示性的承诺,会清楚说明预估的概率分布,这样主管就可以做出合适的计划。
专业人士一旦做出承诺,就会提供确定的数字,按时兑现。但多数情况下,他们只会给出概率预估。
预估的三元分析法:
- 乐观预估
- 标称预估:概率最大的数字
- 悲观预估:考虑到各种意外
预估是非常容易出错的,控制错误的方法就是分解任务,预估小任务的时间,再加和。
11 压力
即使有压力,专业开发人员也会冷静果断。尽管压力不断增大,他仍会坚守所受的训练和纪律。这是战胜压力感的最好办法。
在压力下保持冷静的最好方式,就是规避可能导致压力的处境:
- 承诺。避免对没有把握的事情作出承诺。
- 保持整洁。不会为了赶进度而乱来,保持系统、代码和设计及可能整洁。
- 坚守纪律
应对压力:
- 不要惊慌,切记鲁莽仓促,对问题要深思熟虑
- 沟通,让别人知道你正深陷困境, 请求支援
- 坚守你的纪律原则
12 协作
与雇主的关系
对做的事情充满激情是好的,但是最好把注意力集中在付我们薪水的老板所追求的目标上。
专业程序员的首要职责是满足雇主的要求,协作沟通,深刻理解业务目标。这不是说你要成为业务方面的老学究,而是理解手上正在编写的代码的业务价值,了解雇你的企业将如何从你的工作中获得回报。
与同事的关系
代码为整个团队所有,而不是个人。专业人员不会阻止他人修改代码,而是尽可能地相互协作。
专业人士会结对编程,这是他们分享知识的最好途径。结对也是 code review 的最好形式。
专业人士会共同工作,当你带着耳机坐在角落,是无法参与合作的。面对面工作,你能感受他人的恐惧担忧,能够听到别人的牢骚,要有口头和肢体上的下意识的沟通交流。
13 团队与项目
形成团队是需要时间的,团队成员首先要建立关系。他们需要学习如何相互协作,了解彼此的癖好、强项、弱项,最终凝聚成团队。这个时间称为发酵期,可能需要6个月,甚至1年。
有凝聚力的团队大约12名成员,最多20人,最少3人。这个团队配有
- 7名程序员
- 2名测试:编写自动化验收测试(关注正确性,即可能出错的地方)
- 2名分析师:开发需求,为需求编写自动化验收测试(关注业务价值)
- 1名项目经理:追踪项目进度,确保团队理解优先级和时间表
其中一名成员可能会充当 Master 的角色,负责确保项目进展,监督成员遵守纪律。
围绕项目构建团队,是愚蠢的做法。这样,团队永远不可能形成凝聚力。专业的开发组织会把项目分配给已形成凝聚力的团队。
团队可以使用每周点数来衡量自己的速度。他们对每个项目的特性进行分解,使用点数来估算。
14 辅导、学徒期与技艺
即使是最好的CS教学计划,也不足以帮助毕业生充分准备好接受工作的挑战。
如果有一名 mentor 能够一起工作,那再好不过。如果没有,也能通过观察他们的工作,快速学习。
程序员的几个阶段:
- 大师。他们在多个系统上工作过,经验丰富,懂得如何领导和协调多个团队,同时也是熟练的设计师和架构师。
- 熟练工。他们还在受训期,但能胜任工作,正在学习如何在团队中卓越工作和成为团队的领导者。但是,他们对很多系统都缺乏经验。(熟练工在大师的督导下进行工作,代码会被仔细审查)
- 学徒,需要在熟练工的督导下工作,学习纪律并强化各项实践。
BUT 大多数公司没有技术督导这回事,程序员能否提升和晋升,全看程序员自己的表现。
技艺是工匠所持的精神状态,技艺的meme饱含着价值观、原则、技术、态度和正见。
专业人士首先自己会成为表率,让技艺可以被他人察觉、传染。
工具
源代码控制
git 分支工作
IDE
vi/Emacs/Eclipse/IntelliJ
问题追踪
wiki/jira
持续构建
只要 commit 代码,就需要自动构建,并将结果呈给团队。
Jenkins
单元测试工具
单元测试工具必须满足:
- 快速方便地进行测试
- 测试结果有清楚的视觉提示
- 对测试进度有清楚的视觉提示
- 应该避免测试用例之间想相互通信
- 编写测试用例应该非常简单
MDA (模型驱动架构)
以图形代替代码来消除大量的细节。