第 5 章 项目管理与审查
以团队形式进行开发时,应该遵循什么样的流程呢?首先,要分配作业任务,让每个成员负责一部分开发工作。每天或每周报告进度,如果工作过程中发现问题则必须马上共享。要想更好地管理这一系列流程,使开发工作更加顺利,我们要用到 Redmine、Trac 这类问题跟踪系统。
本章首先在 5.1 节就问题跟踪系统中的项目管理与问题的区分使用进行说明。另外,会在 5.2 节通过实际的示例讲解如何统一项目内容。
然后再进一步,将问题跟踪系统中的问题与版本管理系统中的分支相结合,为各位介绍问题驱动开发的相关知识(5.3 节)。
团队开发还有另外一项优势,那就是在分配任务和实现任务之余,还可以验证当前任务或实现是否正确地完成了,也就是所谓的审查。5.4 节将不惜大量篇幅,为各位讲解审查方和被审查方各自的注意事项,以及一些行之有效的审查方法。
5.1 项目管理与问题的区分使用
进行项目管理时,应该以什么为单位创建问题,每个问题中应该写什么,这都是整个团队必须统一的事。本节将以 Redmine 为例,学习进行团队项目管理时应注意的一些点。
5.1.1 项目管理的前置准备工作
在 Redmine 上创建项目、开始项目管理之前,有些设置需要先做好。我们可以在项目的“配置”标签页进行各类设置(图 5.1)。
图 5.1 项目的配置界面
◉ 添加成员
首先添加参与项目的成员。成员按职责不同分为 3 种,具体区分如下。
管理人员:可更改项目的设置。一般情况下所有项目成员都可以设置为管理人员
开发人员:无法对配置界面进行操作,其余操作则不受限制。通常是公司外项目成员等
报告人员:测试负责人,用来对那些负责添加问题的成员进行设置
◉ 添加版本
一个项目必然存在确定需求、实现完毕、测试完毕等阶段点(里程碑)。我们要把各个里程碑的信息添加到版本标签页,将其视为一个个版本。如果版本发布日期已确定,则可以给相应版本设置日期。
◉ 添加问题类别
如果项目具备一定规模,那么最好以功能为单位添加问题类别。添加类别时,请记得设置该类别的负责人。在没有相应负责人的情况下,可以直接将项目组长设为负责人。这样一来可以避免出现没人负责的问题。
5.1.2 创建问题
现在我们已经做好了给项目创建问题的准备,可以实际动手创建问题了。点击“新建问题”标签会出现如图 5.2 所示的界面。
图 5.2 新建问题界面
创建问题时,各项目按如下内容输入、设置。
项目 | 内容 |
---|---|
跟踪 | 指定问题的种类。区分如下所示1。 • 功能:用于实现新功能的问题 • 错误:用于报告 Bug • 任务:实际工作、操作等 • 需求讨论:用于讨论需求的问题。需求确定后新建功能问题并与之关联 |
主题 | 简洁明了地描述问题的内容 |
描述 | 描述这个问题想要实现的内容。用问题模板(Issue Template)统一描述内容后可有效防止遗漏 |
状态 | 设置问题的状态。比如开始工作后选择“进行中”,工作完成需要别人确认时选择“反馈”等 |
优先级 | 如果描述的内容比较重要,可以将优先级提升为“高”或其他 |
指派给 | 选择负责这个问题的人。如果不清楚该由谁负责,可以指定项目组长,再让项目组长更改为适当人选 |
类别 | 指定问题的类别,标明问题属于哪个部分、与什么功能相关 |
目标版本 | 如果项目中存在里程碑,则选择“目标版本”这一项 |
跟踪者 | 如果除负责人之外还有其他人员与此问题有关,则选择此项 |
文件 | 在此添加相关图片或文件 |
父任务 | 要与父任务相关联时,在此输入父任务的编号 |
开始日期 | 添加着手处理该问题的日期。可直接使用默认日期(创建日期) |
计划完成日期 | 希望在哪一天之前完成此问题里的内容 |
预期时间 | 如果预估了工作时间,可写入此栏 |
% 完成 | 直接用 0% 即可 |
1新建项目默认跟踪标签只有“功能”“错误”“支持”3 项,不过用户可以通过管理界面里的“跟踪标签”页面增加、删除和修改选项。这里的“任务”和“需求讨论”应该是作者自己创建的。——编者注
5.1.3 整理问题
在问题中描述工作内容,根据内容进行操作和实现,将完工的问题的状态改为“已关闭”。当这一系列周期顺风顺水时,我们会发现问题一个个减少,有一种项目向着成功迈进的感觉。
然而,因需求变更导致问题的内容与最新需求产生偏差,或是负责人设置不当等问题经常会导致开发无法进行下去,结果就是无人问津的问题越积越多。一旦问题堆积起来,我们将不知道自己还剩多少工作要做,自然也就看不到项目的终点。
为防止无人问津的问题越来越多,我们需要定期整理问题。问题的整理方法如下。
① 问题分类
按更新顺序重新排列未结束的问题。更新日期较早的问题大多已经停滞,所以可以根据日期从旧到新进行查看。
② 查看问题
查看问题里写的工作内容。确认工作内容与实施目标是否一致,以及负责人是否合适。
③ 结束、驳回问题
如果发现内容与其他问题重复,则注明对应问题编号并结束当前问题。如果工作内容与当前需求出现很大偏差,则写明理由并驳回该问题。
④ 修正问题
如果工作内容不明确或有错误,则要将内容修正至能正确进行为止。如果工作内容涉及面太广,同时出现多名负责人,则可以按照 5.1.4 节说明的方法进行分割。
⑤ 分配问题
将问题指派给合适的负责人。有些工作内容会涉及多名负责人,此时要根据工作优先级挑选出一名主要负责人。
要是等问题堆积起来再去整理,那我们很可能要在这上面耗上一整天,结果耽误本来该干的工作。建议各位平时就抽出一丁点时间来整理它们。
5.1.4 分割问题
当既有问题久久无法完成时,就需要我们重新审视该问题的目标了。因为我们在创建问题的时候,很可能没注意到其中暗藏的大量工作。一旦遇到这种情况,就应该把这些暗藏的任务分割到其他问题中。
这样一来,我们就可以把暂时不需要处理的内容向后放,或者将分割出来的问题交给其他负责人。另外,把每个项目都细分成问题还有助于管理。Redmine 能够给问题添加关联,比如设置父子关系,或者将几个问题设置为“相关问题”等,这对管理的帮助很大。
当然,我们也可以在最开始就把工作细分成较小的问题。这个分配工作既可以在着手该项目时进行,也可以等到必要时再做。重要的是,要根据工作量以及工作内容的复杂度合理分配任务,然后整理成问题。
5.2 问题模板
我们在 5.1 节了解到,新建问题时要在“描述”一栏描述这个问题想要实现的内容。不过真到新建问题时,我们往往不知道该写些什么。另外,一旦问题的描述不够充分,我们就要花费很多时间在留言交流上。
因此,为了防止问题的描述出现遗漏,团队应该事先统一好需要描述的项目。Redmine 有一个插件叫问题模板,它能生成统一的格式(模板)并把该格式反映到问题中。
这里我们将学习问题模板插件的使用方法,然后给各位看一个实际的例子。
5.2.1 安装插件
要想给 Redmine 的问题设置模板,需要安装 Issue Template Plugin2。
2http://www.r-labs.org/projects/issue-template/
安装插件的流程如 LIST 5.1 所示。
LIST 5.1 安装Issue Template Plugin
$ wget https://bitbucket.org/akiko_pusu/redmine_issue_templates/downloads/redmine_issue_templates-0.0.9.zip
$ sudo mkdir usrshare/redmine/plugins
$ unzip redmine_issue_templates-0.0.9.zip -d usrshare/redmine/plugins/
$ cd usrshare/redmine
$ sudo chown -R www-data:www-data plugins
$ sudo cp -pr plugins/redmine_issue_templates/assets public/plugin_assets/redmine_issue_templates
$ sudo rake redmine:plugins:migrate RAILS_ENV=production
$ sudo bundle install
$ sudo service apache2 reload
如果插件安装正确,插件管理界面会显示 Redmine Issue Templates plugin,管理菜单中将多一条 Global Issue Templates(全局问题模板)(图 5.3)。
图 5.3 Redmine 的插件管理界面
5.2.2 问题模板的使用方法
下面我们来学习问题的设置和使用方法。
◉ 启用问题模板功能
打开项目的“配置”→“模块”界面,勾选“问题模板”,保存后即可对该项目开启问题模板功能。开启后,配置界面上将多出“问题模板”标签页。
另外,我们在这里更改全部设置,以便今后创建的所有项目都可以直接使用问题模板功能。打开管理界面的“配置”→“项目”界面,在“新建项目默认启用的模块”中勾选“问题模板”并保存(图 5.4)。
图 5.4 管理界面的项目配置
◉ 创建模板
在配置界面选中“问题模板”选项卡,点击“新建”进入模板添加界面(图 5.5)。
图 5.5 添加问题模板的界面
请按照下表设置各输入项目。
项目 | 说明 |
---|---|
模板名称 | 在这里指定问题模板的名称。我们规范为“用于〇〇的模板” |
跟踪标签 | 在这里指定应用模板的跟踪标签的种类(功能、错误、支持等) |
问题标题 | 在这里指定标题的模板 |
模板内容 | 在这里输入问题内容模板。推荐使用标题或逐条列记,这种模板更直观易用 |
批注 | 不需要填写 |
默认值 | 勾选之后,选择该类问题的跟踪标签时会自动套用当前模板 |
启用 | 勾选之后,该模板才会生效 |
Enabled sharing with project tree. | 勾选之后,子项目也可以使用该模板 |
◉ 套用模板
问题模板的使用方法很简单,只要在新建问题界面中选择模板就行了。
选择跟踪标签(问题类别)后,界面上会列表显示该跟踪标签下有效的问题模板。此处只要选择了任意一个模板,“主题”“描述”就会被自动填充。随后我们只需要按照通常创建问题的步骤,对内容作一些修改即可(图 5.6)。
图 5.6 选择问题模板
5.2.3 Global Issue Templates
问题模板很方便,但当我们有一个所有项目通用的模板时,如果要一个项目一个项目地去设置这个模板,那可不是一般的麻烦。这种时候,我们可以使用 Global Issue Templates 功能,创建一个所有项目通用的模板。
创建 Global Issue Templates 时,先选择 Redmine 管理界面中的 Global Issue Templates 菜单,然后点击“新建模板”打开如图 5.7 所示的界面。这里的基本输入项目与问题模板相同,不同点则是下面多出了“项目”区域。这里会显示 Redmine 上的所有项目,我们在哪个项目前面打上勾并保存,该模板就会对哪个项目生效。
图 5.7 添加 Global Issue Templates 的界面
另外,如果我们新建了项目,Global Issue Templates 并不会自动对该项目生效。所以当我们创建了新项目时,需要打开 Global Issue Templates 的编辑界面,勾选新项目并重新保存。
5.2.4 问题模板示例
这里,我们为每个跟踪标签各准备了一个问题模板的范例。
我们用的格式是 reStructuredText,如果各位使用的是 Redmine 默认的 Textile,请自行作相应替换。
◉ 功能
与添加功能相关的问题(LIST 5.2)。描述要实现何种功能。
LIST 5.2 功能问题的模板
目的
目的
- 实现该功能后成品能完成什么工作,将大致预想写在这里
输入输出
- 记录输入的值和输出的结果
相关功能、影响范围
- 需要进行回归测试的地方
- 生成该功能所需数据的功能(可添加与对象问题的关联)
- 使用该功能所生成数据的功能(可添加与对象问题的关联)
安全
- 如有安全相关问题(权限等),则将相应内容写在这里
演示方法
- 记录 URL 或其他能轻松查看该功能的操作流程
◉ 错误
用于报告 Bug 的问题(LIST 5.3)。将试验等过程中发现的 Bug 制作成问题,分配给相应负责人。
LIST 5.3 错误问题的模板
现象
现象
- 描述该Bug 的现象
流程
- 描述出现该Bug 的操作流程
预期结果
- 描述原本应该出现的结果
环境
- 描述试验环境
示例
- OS :Mac OS X 10.10
- 浏览器:IE11
- 执行用户:admin
- 出现Bug 的URL :https://staging.example.com/very-critical-feature
相关信息
- 描述错误日志、消息Sentry 的URL 等
◉ 任务
关于各种作业的问题(LIST 5.4)。
LIST 5.4 任务问题的模板
目的
目的
- 执行该任务的目的
内容
- 具体要实施的内容
◉ 需求讨论
用来讨论需求的问题。确定需求后关闭这个问题,然后新建功能问题,并与之相关联。
LIST 5.5 需求讨论问题的模板(LIST 5.5)
目的
====- 当前讨论的需求要达成何种预期。
需求方案
======
- 记录已有的需求方案,有多种时可记录多个方案
- 同时记录优点和缺点
总结
======
- 推荐的方案以及推荐理由
演示方法
========
- 记录URL 或其他能轻松查看该方案的操作流程
5.3 问题驱动开发
接下来我们来学习问题驱动的团队开发。
5.3.1 别急着敲代码,先建问题
下面我们开始学习团队开发的一系列流程。所谓开发,既可以是开发新应用,也可以是修改已有系统。重要的是,我们在打开编辑器敲代码之前,要先把即将着手处理的项目添加到问题跟踪系统之中。
在 Redmine 界面中填入标题和任务概述并完成添加后,我们将得到一个带有编号的问题(这里是“#2”)(图 5.8)。
图 5.8 Redmine 的问题界面
5.3.2 创建与问题编号同名的分支
获得问题跟踪系统分配的问题编号之后,我们就可以在 Mercurial 上创建一个与之同名的分支了。举个例子,如果问题的编号为“#100”,那么我们的分支名就对应为“t100”。
这样一来,我们负责的项目和源码的修改内容就一一对应起来了。等到我们完成了这部分工作,只要将 t100 分支合并到 default 分支,就能轻松完成发布工作,十分方便。另外,将“一个问题对应创建一个分支”用作项目方针来规范团队,可以有效减少恼人的多头现象(Multiple Head,即最新端变更集分成多个版本的现象)。而且,由于问题的编号与分支名一一对应,所以当我们想查看项目 #100 所修改的内容时,只需查看分支 t100 即可,非常方便。
◉ 主题分支与问题驱动开发
这种为单一问题(主题)而创建的分支称为主题分支。
一个项目拥有的主题分支数往往很惊人。我们没必要给每个主题分支都起一个有意义的名字。花大量时间在起名字上,有时反而会起出一些让人摸不清头脑的名字,倒不如机械式地起名来得方便,而且不会出现歧义。问题跟踪系统的问题 ID 一般都很独特,再加上它具备我们前面提过的那些优势,所以用它肯定万无一失。
像这样基于问题跟踪系统的问题来进行开发的模式就称为问题驱动开发。
专栏 明确区分分支名与版本修订号
主题分支的英文是 Topic Branch,所以名字前面都加了t。在 Mercurial 中,这还起到了区分分支名与版本修订号的重要作用。仅由数值组成的分支名很容易与版本修订号搞混,弄不好就会出现意外情况,所以应当尽量避免使用仅由数值组成的分支名。
5.3.3 让发布与分支相对应
从项目管理的观点讲,我们提倡设置里程碑并制定发布计划。而采用敏捷开发的项目还会基于迭代来制定发布计划。
在系统开发中,发布计划的重要性不言而喻。前面我们讲了问题与分支一一对应的好处,同样道理,将发布与版本管理系统的分支一一对应也能方便管理。
使用 Mercurial 时,default 分支是一个既定的分支,必然存在。因此我们规定“default =面向正式运行的分支”。然后假设现在有一个发布,我们从 default 创建一个新分支与它相对应(LIST 5.6)。
LIST 5.6 从 default 创建发布分支
$ hg update default
$ hg branch m1231
然后,基于新创建的发布分支派生出各个主题分支(LIST 5.7)。
LIST 5.7 创建主题分支
$ hg branch
m1231
$ hg branch t100
用作里程碑的分支并没有严格的命名法则,但为了表示其“里程碑”之意,我们用字母 m 打头。
m 后面可以是发布日期(上例中的 1231 表示 12 月 31 日),也可以是明确表示发布的特殊词汇(比如智能手机版的发布命名为 m_smartphone 等)。由于问题跟踪系统不会专门给里程碑分配特殊 ID,而且创建里程碑并不像创建主题分支那样频繁,所以我们完全可以给每个里程碑设置个性化的分支名。
5.3.4 分支的合并
刚刚我们通过创建分支将开发成果分割开了。各个项目都有对应的分支,分支之间的开发和提交相互独立。因此,当项目完成后,还需要将它合并回来。合并的规则很简单,保证只合并有父子关系的分支就行了(图 5.9、图 5.10)。
① 只在父分支和子分支之间进行合并。
② 子分支之间不合并。
③ 从子分支派生出来的孙分支不能直接合并到父分支。
上述规则必须严格遵守。
图 5.9 父分支与子分支之间的合并
图 5.10 一些应当避免的合并
如果合并了没有父子关系的分支,我们好不容易分割清楚的项目会再度乱成一团。这可能会导致问题里没提到的修改内容在不知不觉间混入,或者导致源码产生冲突,等等。想在其他分支中反映某分支的成果时也要遵守这一规则,先与它们共同的父分支合并,再反映到对象子分支里。
虽然这样做很麻烦,但在父分支中分享一些本不该出现的半成品会导致很多问题,所以请各位务必遵守。
只要按照我们这里学习的方法创建分支,一般不会遇到子分支之间合并或者孙分支向父分支合并的情况。如果无论如何都得进行这类操作,那就可以认定是分割项目与设置父子关系的阶段出现了问题,需要我们修改问题的内容或选择其他方法应对。
另外,在将子分支合并到父分支之前,一定要先在子分支中反映父分支的修改内容。因为在我们修改子分支的过程中很可能有其他人对父分支作了修改,所以要先将这部分修改吸收到我们的子分支中,保证“父分支和子分支之间的差别 = 我们对子分支作的修改”。
这样一来,即便子分支与父分支之间出现了矛盾,我们只要回到自己的任务分支中就能解决该问题。
5.4 审查
5.4.1 为什么需要审查
审查的意义大致有两个。
一个是修正错误。通过审查人员的指正,我们能及早发现单人作业时忽略的问题以及一些错误的认识。
另一个是知识共享。审查能帮助团队成员分享在工作过程中学到的新知识,同时也是不同特长的成员相互交流窍门和经验的好机会。
这里我们了解一下“受审查者在接受审查时”和“审查者在进行审查时”的注意事项。另外,本节将审查分为分“源码的审查”和“发布作业的审查”,分别称为代码审查和作业审查。
5.4.2 审查委托:代码审查篇
◉ 准备资料
接受审查前首先要准备资料。将自己写代码时参考的规格文档、需求定义文档、界面图等资料提供给审查者。
同时要让审查者清楚自己修改了哪部分源码。我们可以列出源码修改前后的差别,让审查者对修改内容一目了然。
◉ 统一编码风格
我们在第 1 章中了解到,PEP8 这种编码风格如今被人们广泛采用。所以我们可以先借助 flake8 统一编码风格,然后再请人来审查。flake8 的使用方法以及在编辑器中的设置方法请参考 1.3 节。
如果某个项目想采用与 PEP8 稍有不同的编码风格,我们可以准备出一个设置文件(setup.cfg)来自定义 flake8 的检测内容。
比方说我们想将一行的最大限制从 80 个字符提高到 120 个字符,那么要在项目根目录下的 setup.cfg 中作如下描述(LIST 5.8)。
LIST 5.8 setup.cfg 示例
[flake8]
max-line-length = 120
flake8 Configuration
这样一来,flake8 就不会对 120 个字符以内的行发出警告了。变更项目的编码风格时,记得要先取得项目成员们的一致同意。
把希望审查员确认的事项整理出来
接下来思考一下审查时的步骤。漫无目的地检查全部代码既耗时又没效果,所以要圈出重点。
那么应该如何圈重点呢?请参考以下 2 个原则。
① 自己不放心的地方
处理比较复杂或者涉及陌生模块的地方等。总之要将自己不放心的地方交给审查者确认。
② 费心设计的地方
回顾一下实现过程中思虑再三的地方以及设计上比较费心的地方,将它们总结并整理出来。这些需要花时间思考的部分很可能是需求或设计上的重要部分,应该积极地交给审查者检查。另外,将编码或测试中的难点与其他团队成员共享也是一件有利无弊的事。
◉ 反映审查结果
接受审查时要虚心听取指正并记录下来。另外,根据指正进行修改后还要请审查者再确认一遍。
5.4.3 审查委托:作业审查篇
◉ 准备资料
接受作业审查时也需要准备相关资料。尽量把需要负责人严格把关的事故易发点共享出来,比如服务器的环境设置、网络构架等。
◉ 制作作业流程文档
将作业总结成“作业流程文档”。制作该文档时需要注意以下几点。
① 目的和概述
写出该作业的目的或概述。比如像下面这样的原因或问题经过、非正常作业流程等就要写出来,让审查者一目了然。
由于编号为 #123 的问题中的问题导致接单表数据不一致,为修正此问题进行数据维护
发布面向营业的接单数据报告功能。为进行发布作业而更改数据库定义以及修改 Apache 的设置
② 作业场所
写明是在哪里进行的作业。在正式环境由多台服务器共同组成时(比如主从式数据库)要格外注意这一点。
③ 所需时间与时间规划
各项作业的预计所需时间以及作业整体的时间规划能帮助我们制定当天的计划。另外,当外部相关人员加入时,我们还可以根据它来修改日程。
④ 负责人
如果事先知道当天的作业负责人,最好把负责人信息记下来。同时要写明进行该作业的是团队内部人员还是承包方负责人。
⑤ 执行用户与权限
写明该作业由 OS 的哪个用户执行。这样就可以避免到了实际作业时才发现没有 root 权限而慌了手脚。这对写流程文档的人来说或许意义不大,但当天保不准会由其他人来代替我们进行该项作业,如果对方搞不清执行用户,将很容易引起麻烦。所以这一条还是记上为妙。
⑥ 执行命令
在命令行执行某条命令时,需要将该命令记录下来。只要保证作业内容固定,那么任何人执行该作业都能得到相同结果,造成问题的风险也就相对较小。另外,执行命令之后要检查命令是否正常执行完毕,这个检查的方法也最好写在这里。
⑦ 作业失败时的对策以及补救方法
对于一些有着复杂流程的作业,或者一旦失败就难以还原的作业,应该事先写明失败时的还原方法。事先准备好应对方法能避免正式作业中慌手慌脚。
⑧ 试运行的记录
如果在本地的 VM 上进行了试运行,那么应该把结果记录添加在这里。试运行的结果不但能让审查者对正式作业有一个更清晰的印象,还能帮助审查者发觉正式作业时需要注意的点。
审查者要根据这些资料判断受审查者的开发成果是否妥当。
5.4.4 实施审查:代码审查篇
◉ 形式上的检查没有意义
如果把有限的时间花费在检查“一行有多少个字符”“类或函数的定义之间空了几行”等问题上,那完全是在浪费审查时间。这些事项用 PEP8 或 pyflakes 等 lint 工具就能轻松搞定。
审查时要把时间花在以下几个项目的检查上。
① 程序是否满足需求
② 是否有遗漏需求
③ 设计或实现的效率是否够高
◉ 发现疑问就询问理由或缘由
在实际写代码的过程中,设计与实现上的很多判断都要基于各种背景或缘由。
受审查者在接受审查时不一定能把这些情况全都说清楚,而审查者又不知道对方漏说了哪些知识,所以审查者在看代码时常常会觉得有些不对劲。作为一名审查者,我们必须考虑到这种情况。对于有疑问的部分,要向受审查者了解背景或缘由,然后努力去理解。如果我们有更好的解决方法,要当场提出。
◉ 整理注释
关于源码上是否应该留有注释,众说纷纭。不过,我们建议将修改程序时的背景或缘由简单地注释下来。日后再有人接触这部分源码时,很可能会对该项处理抱有疑问,而注释能帮助他们理解实现者的意图。
但这并不是说所有地方都要留注释。只要把实现过程中需要注意的难点顾及到了就行。另外,如果注释文的篇幅实在太长,最好改用文档或其他形式保存。
反过来,我们还需要检查该删除的注释是否都删除了。因需求变更而修改源码后,有一部分注释会与事实不符,这些注释就需要删去。
◉ 判断是否需要修正时,要考虑重要性和日程之间的平衡
即便我们断定受审查者写的代码需要修改,也要考虑修改内容的重要性以及日程。很多时候,代码修改后确实能提高可读性,但当涉及的代码规模很小时,我们完全可以不对本次代码作修改,只敦促对方下次注意即可。相对地,如果可读性差或效率低的代码已经被大量复制和粘贴,那就很可能对今后的修改造成影响,这时就必须督促对方修改。
不过,是否立刻修改仍有商量余地。有时考虑到日程所限,可以将修改程序的作业创建成问题并延后处理。
◉ 将编码能力不足和问题意识分开评价
有些时候,虽然受审查者的编码能力还欠火候,但需求和设计方面的问题意识可圈可点,一看就是认真考虑过的。受审查者自然希望获得肯定,所以这种时候我们要充分肯定对方的问题意识,只针对编码方面作出指正。
专栏 rietveld 的功能介绍
平日里,我们用 Mercurial 的 diff 进行简单审查,同时会还配合使用 Google 开发的代码审查工具 rietveld。这个专栏将对 rietveld 的功能作简单介绍。rietveld 是在 Google App Engine 上运行的 Web 应用式代码审查工具。
委托别人进行审查时,要将审查对象 diff 提交给 rietveld。提交内容将汇总为 patch set 形式,生成审查专用页面。根据 diff 的内容,该工具会生成审查对象文件一览表以及各文件的审查页面(图5.11)。
图 5.11 审查对象文件一览表
各文件的审查页面有两种阅览形式(unified 形式和 side-by-side 形式)(图 5.12、图 5.13)。不管是哪种形式,在任意一行上双击都可以打开留言框,提交审查留言(图 5.14)。
图 5.12 unified 形式的审查页面
图 5.13 side-by-side 形式的审查页面
图 5.14 可以直接在代码中添加留言
rietveld 这类工具虽然会增加审查的前置准备工作,但它可以高效审查涉及多个文件的大幅度更改,而且不必审查者和被审查者同时在场。根据审查对象的不同,我们可以选择用工具进行审查,或者用 diff 甚至是打印出来的源码进行审查,从而提高审查效率。
5.4.5 实施审查:作业审查篇
◉ 能否跟踪作业内容和实施历史
不论事前事后,我们都希望能够客观掌握负责人的作业情况,所以要督促对方将作业内容以流程文档等形式保留下来。在服务器或数据库上作业时则要记录已执行的命令或 SQL 语句。另外,命令的执行结果也要记录下来。这样一来,即便事后才发现作业失误或者作业内容不周全,我们也可以根据记录查找原因,制定对策进行弥补。
◉ 能否还原,是否有备份
作业失败时能进行还原是十分重要的。
比如这个作业要进行数据库的数据修正,如果我们发现事务忘记了 BEGIN,直接发出了 UPDATE 语句,那么必须指正出来。同样地,数据库备份的重要性也是不言而喻的。
◉ 是否受外部因素的影响
即便受审查者在写流程文档时制定了妥当的作业流程,实际作业仍可能受到一些与作业内容没有直接关系的外部因素影响而失败。
比如以下这些情况。
发布对象程序需要调用外部 API 时,API 方有访问限制,但程序没有申请解除限制
发布服务时,没有将维护消息通知给用户。或者维护时间过紧,每次都是勉强完成维护
忘记停止正式服务器的 cron 中设置的定时任务,在作业中误执行了批处理,从而引起问题
审查者要注意这类外部因素的有无。
5.5 小结
本章先讲解了使用问题跟踪系统时的注意点。
接下来对把项目管理与版本管理结合起来的问题驱动开发进行了介绍。问题与分支相互配合管理有助于开发顺利进行。另外,这样做还能有效减少源码的矛盾,让我们在团队开发时不必为此费心。
至于审查,我们从被审查者和审查者两个角度出发,分别了解了其需要注意的地方。我们前面也说过,能够进行审查是团队开发的一大优势。但是,如果审查时抓不住重点,那就审查很可能沦为形式上的检查。希望各位能熟记本章讲述的这些注意点,去实践一些更有意义的审查。