最好的软件开发人员知道一个秘密:美的东西比丑的东西创建起来更廉价、也更快捷。美的系统是灵活、易于理解的,构建和维护它们就是一种快乐。

我们需要招聘的不是“经历丰富”的人,而是“有职素养”的人。并不是问题的难度,而是解决问题的方式、步骤以及反思的深度,更能体现一个人的职业素养。

如果只会快速编码,却不关心代码背后的意义,不能迅速判断、解决程序运行中的问题,不能自信满满地为自己交付的程序承担责任,同样是与职业素养绝缘。

本文是 程序员的职业素养 的读书笔记。

1 专业主义

专业不但象征着荣誉与骄傲,而且明确意味着责任与义务。

专业主义的精髓就在于将公司利益视同个人利益,专业就意味着担当责任。

首先,不行损害之事:

  • 不要破坏软件功能,不能留下 bug,将测试自动化
  • 不要破坏结构,软件要易于修改

为了证明软件易于修改,你应该随时做点简单的实际的修改。这又要求我们有一个覆盖率极高的自动化测试。

职业发展是自己的事儿,雇主没有义务培训你,给你买各种书籍。

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

职业道德:

  • 了解你的领域:设计模式、设计原则、方法、实践、工件
  • 坚持学习,关注博客,参加技术大会,读相关文章,访问用户
  • 练习
  • 与他人合作,一起编程、一起练习、一期计划
  • 传道授业,教学相长
  • 了解业务领域,知道为什么设计这样的 spec
  • 与雇主和客户保持一致,他们的问题就是你的问题
  • 谦逊,切忌自负,承认自己的力不从心。嘲讽别人只会自作自受。

2 说“不”

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

要做出艰难决定的时候,存在对抗角色间的冲突于此是最为有利的。你的经理指望你能像他一样捍卫自己的目标,这样你们才能达到可能的最好结果。

要有团队精神,意味着当其他队员遭遇困境时你要施予援手。

“好的,我试试看”是最差的回应。许诺“尝试",意味着你承认之前未尽全力。而且,只要你许诺”尝试“,你其实在许诺你会确保成功。这样压力就让自己来扛了。

委曲求全,并非问题的解决之道,还可能制造出更多的麻烦。

3 说“是”

作出承诺,包含3个步骤:

  1. 口头上说
  2. 心里认真
  3. 付诸行动

在承诺做某事时,留意自己的用词,它们透露了我们对待承诺的认真程度,要避免:

  • 需要/应当
  • 希望/但愿
  • 让我们(而不是让我)

真正的承诺听起来像“我将在 。。。之前。。。”:

  1. 对事情有清晰的事实陈述
  2. 明确说明了 deadline

如果因为以下原因没有“言必信,行必果”,你应该有些应对方法

  1. 因为寄望于某某去做这件事:你只能承诺自己完成的事情
  2. 因为不太确信是否真能完成的了:努力缩短与目标之间的距离
  3. 因为无能为力:要立刻调整别人对你的预期,越快越好!

专业人士对自己的能力极限了如指掌,十分清楚自己还能保持效率加班多就,也明白要付出的代价。

4 编码

编码要更加聚精会神,因为你必须平衡相互牵制的多种因素:

  1. 代码必须能够正常工作。为此要理解要解决的是什么问题
  2. 代码必须解决用户提出的问题,与客户交流并定义问题
  3. 代码必须与现有系统结合地天衣无缝
  4. 其他程序员能读懂你的代码

当你心烦意乱或者疲惫时,千万不要编码!奉献精神和职业素养,是指遵守纪律原则而非长时间工作。

在“流态区”,你可能会敲出更多代码,但你没有顾及全局。因此你可能做出一些后来不得不推到的决策。

类似于单核工作法的全局模式

音乐可能把你带入流态区。

中断:被打断无法避免,要维护住上下文的做法有:结对编程和 TDD。

当你死活写不出代码时,试着结对编程,或者来点创造性输入(科幻小说)。

要想避免难熬的调试时间,最好的办法就是测试驱动开发。

软件开发是一场马拉松,要保存体力和维持稳定节奏来取胜:

  1. 疲劳时就停下来吧
  2. 从问题中脱离出来,可能会获得意想不到的创意:开车时,洗澡时

管理拖延的诀窍是,早期检测和保持透明。

  1. 根据目标定期衡量进度
  2. 及时将进度送达利益相关者

当经理极力要求你赶上 deadline,你应该维持你的估算。

明确定义“完成”来避免交付失误。最好的办法是创建一个自动化的验收测试。

帮助他人:要清楚团队伙伴的状态,随时准备提供帮助,全情投入的帮助。反过来,自己受阻时,也要即使请求别人的帮助。

5 测试驱动开发

TDD 的 3 项原则:

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

遵循这 3 个法则,大概 30s 就要运行以一次代码。

单元测试不是一开始把 case 全部写完,而是 case -> 代码 -> case -> 代码 这种小步前进。

TDD 拥有一套值得信赖的测试,完全可以大小对修改代码的恐惧。

单元测试即文档:遵循 3 项法则,那么对系统中的每个对象,单元测试都可以清楚描述对象的创建方法;对于系统中的每个函数,单元测试都可以描述各种有有意义的调用。

测试先行的时候,迫使你思考什么是好的设计!

6 练习

速度快不见得是好事,更好的做法是慢下来,仔细思考。有些时候,尽可能快地编译和测试,也可以带来很高的生产率。

练习的意义:真正做出反应的是你的身体,大脑则是在更高级的层次上思考。而身体的反应,都来自练习!

常见的练习:

  1. 学习热键和导航操作,以及测试驱动开发、持续集成之类的方法
  2. 一个人写单元测试,一个写程序通过测试。然后交换角色
  3. 解决一个实际的问题,多人参与

程序员容易受到一种限制,即所解决问题的种类比较单一,怎么拓展自身的经验呢?

  1. 为开源项目贡献
  2. 用自己的时间练习

7 验收测试

职业程序员会充实与团队及业务部门的沟通,并确保这种沟通准确、顺畅。

做业务的和写程序的人都容易陷入“过早精细化”的陷阱:

  • 业务方看到真正的运行情况,就会调整自己对系统的看法。
  • 开发人员应该明白,评估可以,而且必须基于不那么精确的需求。评估只是评估

迟来的模糊性:需求文档的模糊之处,都应对着业务方的一点分歧。有时,业务方会想当然以为看文档的人懂了自己的意思。模糊性的问题只能从“验收测试”来解决。

不同团队对“完成”的定义各不相同。完成应该是代码写完了、测试通过、需求方已经认可。要达到这种程度而不影响迭代速度,要有一整套的自动化测试。

验收测试的目的是沟通、澄清、精确化。开发、业务、测试对验收测试达成共识,大家明白系统的行为应该是怎样。

专业开发人员认为,实现验收测试的自动化是自己的责任。

写测试不是什么额外的工作,而是确保系统的各项指标符合要求,这样程序员才能确知“完成”。自动化测试是节省时间和金钱的方法,也可以避免开发人员误入歧途。

通常,业务员测试“正确路径”,QA 测试“错误路径”、边界条件、例外情况。

迭代开始的第一天,就应该准备好最初的几项验收测试。然后每天都应当完成一些测试。到迭代的中间点,所有的测试都应该准备完毕。

身为专业开发人员,不能被动地接受测试,要与编写测试的人协商并改进测试。

验收测试,不是单元测试。

  • 单元测试是程序员写给程序员的,是正式的设计文档,描述了底层结构以及代码的行文。验收测试是业务方写给业务方的,是正式的需求文档,描述了系统该如何运行。
  • 单元测试深入系统内部进行,调用特定类的方法;验收测试是在系统外部,通常在 API 或 UI 级别进行。

单元测试和验收测试,首先是文档,其次才是测试。

GUI 测试

要把 GUI 看做 API 那样处理,验收测试就简单多了。编写 GUI 测试时,必须使用 GUI 背后相对稳定的抽象元素(比如通过 id 来指定 button)。

持续集成

确保每次提交代码,持续系统系统就开始构建、并运行所有的测试,并将结果通知团队的所有人。

CI 失败,所有人应该立刻停下手里的活,这是紧急情况。

8 测试策略

目标:QA 找不到任何错误。

QA 也是团队的一部分,QA 和开发人员应该紧密协作,共同保证协同的质量。QA 扮演的角色是:

  1. 需求规约的定义者。QA 的任务是与业务人员一起构建自动化验收测试,作为系统真正的需求规约文档。
  2. 特性描述者。QA 应当描述系统运行中的真实情况,并反馈给开发和业务人员。

自动化测试金字塔

  • 单元测试(100%覆盖率):先写测试、在写产品代码。覆盖大多数的异常路径。
  • 组件测试:验收测试的一种。测试中需要使用 mocking 或 test-doubling 技术,解开与其他组件的耦合。组件测试差不多可以覆盖系统的 50%,主要测试成功路径的情况,以及一些明显的极端情况、边界状态、可选路径。
  • 集成测试:测试组件之间能否正常通信。
  • 系统测试:最终的集成测试,不直接测试业务规则,而是测试系统是否正确组装完毕,以及各部件能否正确交互。应该包括吞吐量测试和性能测试。
  • 人工探索式测试,非脚本化的测试,需要人类智慧的介入。可以搞一个抓 bug 活动。

9 时间管理

会议

会议是必须的,但会浪费大量的时间:

  • 谨慎选择要参加的会议
  • 如果会议让人厌烦,就离席
  • 确定议程与目标,否则可以礼貌拒绝这个会。
  • 立会:昨天干了什么?今天打算干什么?我遇到了什么问题?
  • 迭代计划会议:评估可选择任务的开发时间、确定这些任务的业务价值。
  • 迭代回顾或 DEMO:只牵扯到最近一两周的工作,不有太多内容

凡是不能再 5 分钟解决的争论,都不能靠争辩来决绝。最后的出路就是用数据说话。

注意力

  • 睡眠。再怎么强调也不为过
  • 恢复。注意力不集中的时候,可以通过沉思、反省、小睡、听播客、翻翻书来恢复
  • 肌肉注意力。肌肉注意力有注意改善心智注意力
  • 输入输出。多接触其他人的创造性思维,比如阅读科幻小说

时间拆分与番茄工作法

番茄工作法来记录和跟踪自己的时间,在 25min 内全身心投入,拒绝任何干扰。

要避免的行为

优先级错乱是自我麻痹的谎言,因为不能面对真正要做的事情。

专业人员会评估每个工作的优先级,排除个人的喜好和与澳,按照真是紧急程度来执行。

死胡同

慎重的态度和积累的经验可以让你避免某些死胡同,但如果不小心走进去,要有足够的勇气回头

专业人员要保持开放的头脑,虚心听取其他意见。

泥潭

走回头路(重新修正设计)是最简单的办法。

10 预估

预估是一种概率分布。

业务方觉得预估就是承诺,开发方认为预估就是猜测。

专业人员不会随便承诺,除非他们确切知道可以完成。同时,专业人员也应该避免给出暗示性的承诺,会清楚说明预估的概率分布,这样主管就可以做出合适的计划。

专业人士一旦做出承诺,就会提供确定的数字,按时兑现。但多数情况下,他们只会给出概率预估。

预估的三元分析法:

  • 乐观预估
  • 标称预估:概率最大的数字
  • 悲观预估:考虑到各种意外

预估是非常容易出错的,控制错误的方法就是分解任务,预估小任务的时间,再加和。

11 压力

即使有压力,专业开发人员也会冷静果断。尽管压力不断增大,他仍会坚守所受的训练和纪律。这是战胜压力感的最好办法。

在压力下保持冷静的最好方式,就是规避可能导致压力的处境:

  1. 承诺。避免对没有把握的事情作出承诺。
  2. 保持整洁。不会为了赶进度而乱来,保持系统、代码和设计及可能整洁。
  3. 坚守纪律

应对压力:

  1. 不要惊慌,切记鲁莽仓促,对问题要深思熟虑
  2. 沟通,让别人知道你正深陷困境, 请求支援
  3. 坚守你的纪律原则

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

单元测试工具

单元测试工具必须满足:

  1. 快速方便地进行测试
  2. 测试结果有清楚的视觉提示
  3. 对测试进度有清楚的视觉提示
  4. 应该避免测试用例之间想相互通信
  5. 编写测试用例应该非常简单

MDA (模型驱动架构)

以图形代替代码来消除大量的细节。