我们知道Git协作中最重要的一个步骤是Pull Requests(简称PR,Gitlab中也叫Merge requests,MR,本文中统一叫PR),在之前的文章"理清基本的git(github)"流程中,虫虫介绍过基于Fork-PR的开发协作流程。拥有有效的git协作流程,编写良好的PR可以极大的提高团队的工作效率,最大限度地减少挫败感。本文中虫虫给大家介绍良好协作的PR实践,虽然PR传统上被认为是开发人员工作流程的最后一点,但这些最佳实践涵盖整个开发过程。
本文主要涵盖良好的用户故事,代码测试,代码的可读性,编写良好的修订控制commits以及编写良好的pull请求消息的。
良好Pull Requests的重要性
在团队中编写好的PR的流程可以大大提高开发生产力。如果一个协作团队中提交的PR小而精悍、频繁迭代、易于查看和测试,则能加速PR、加速版本合并,快速实现开发迭代和版本上线。
如果在开发人员,审阅者,测试人员和PM之间来回反复发送的不常见的大PR可能会显著影响进度,开发人员需要浪费大量时间处理冲突和合并。繁琐的PR也大大增加了引入大bug的风险,由于变化太大,很难仔细检查所有变化并且回归测试。
所以,项目PR的质量是团队开发实践效率的一个很好的宏观指标。如果一个团队已经在编写快速合并的PR,则说明他们的协作流程比较健康优良。
用户故事
用户故事(User Story)是对需要完成的工作单元的简短描述,描述对用户有价值的功能。它通常从用户的角度来完成解释,因此得名。用户故事通常的格式为:作为一个<角色>, 我想要<功能>, 以便于<商业价值>。
一个良好的PR应该从编写精良的用户故事开始。
细化用户故事
用户故事通常从一个通用的用例开始,随着项目的进展被分解为更小,更具体的故事。例如:
"作为客户,我需要登录管理系统。"
虽然这看起来像是一个合理的用户故事,但应该进一步细分,因为太范。开发人员可能需要一天多才能创建一个完全可操作的登录系统。它的细分程度取决于团队,但理想情况下,用户故事应该需要一天或更少的开发工作。以下是该故事的一部分可能如下所示的示例:
"作为客户,我打开表单页面,输入我的用户名和密码,然后点击提交。"
从这个跟踪的其他故事可以涵盖当用户提供无效输入时发生的事情(异常处理),或者点击提交后发生的事情(登陆跳转)。许多团队在sprint规划时候提出并细分用户故事,因此团队中的每个人都可以了解即将开展的工作。
垂直划分
还有一件事值得一提的是分解许多团队细化用户故事时候常犯的错误。在细分时,应该垂直划分,而不是水平划分。
垂直划分故事通常需要在整个工作流程上:从视图到服务/类,再到数据库。水平划分的故事通常只涉及其中一个。引用上面的例子,一个水平细分:
"作为用户,我可以将我的详细信息存储到数据库中"
这首先作为一个用户故事,很不合规,但团队通常基于流程构建任务,而不是某些操作。因此,需要一次构建很多个故事的用例,然后查看其面向客户的价值。
通过创建垂直划分的故事,团队可以清楚地了解需要完成哪些特定任务才能为最终用户增加价值,并且任务更加离散且更容易跟踪。
用户故事依赖
理想情况下,用户故事应该不依赖于其他另一个正在完成用户故事。这样,所有工作都可以并行完成。但实际上,用户故事经常相互依赖。搞清故事依赖非常重要。
当处理一系列相互依赖的故事时,最常见的构成一系列PR"塔"型请求,划分到时,最终会得到一系列嵌套的功能分支。最简单的解决方案是在完成其他故事之前合并上一依赖的故事。
我们考虑一个具有三级嵌套的示例分支。
master <分支1 <分支2 <分支3
如果在创建分支3之后,对分支1中进行了更改,则必须在分支2上rebase 分支1,然后分支2 rebase 分支2。万一分支1 rebase了master分支则分支冲突和合并灾难也会发生。
理想情况下,要从底部到顶部来解决这个问题。将分支3合并到分支2中,然后将分支2合并到分支1中,最后合并到master中。但是,通常的合并是从旧到新来的,其中分支1将首先会被合并,然后分支2 rebase到master。
为了避免出现这种情况,尽可能避免依赖关系,但是无法避免时,就要快速合并PR,然后在进一步工作。如果团队会立即处理PR,这样是OK的。但如果不是这的话,最好通知团队优先处理依赖性的额PR。
为GitHub/Gitlab活动设置一个特殊Slack通道会很有帮助,可以通过使用GitHub的Slack的集成来实现。该通道将会非常有用,可以突出显示团队中有关不能等待的PR。
准确评估故事
许多企业都在努力解决这个问题,但这对规划至关重要。上面我们提到了改善这个问题的一个关键因素,将故事分解为可在一天或更短时间内完成的事情。但带来一个问题:如何知道故事是否需要一天或更短的时间?
有一个值得推广的好方法是做"计划扑克"。在sprint计划之前或期间,让每个开发人员独立的投票决定一个故事需要多长时间。投票结果的中位数应该能够合理准确地估计故事的持续时间。如果故事超过一天,划分该故事,重新投票。
要熟练使用这方法,需要一些反复试验和实践。可以让开发人员使用时间跟踪来详细说明每个故事所花费的时间,这也是衡量每个故事所花费的实际时间和验证估算的好方法。进行一次计划扑克之前,可以查看之前估算的值,并将其用作调整未来估算的参考。一些项目管理软件,如Jira,内置时间跟踪功能,可以用来做评估和参考。
良好的测试
编写好的测试用例有很有帮助。一方面可以用这些用例确保代码实现它应该做的事情。如果有好的测试结果,则很可能代码有效。但是,在将代码提交给PR之前,不能用它作为线上运行的版本。在QA环境和运维试运行环境中运行验证代码可以发现由环境不同或未经测试的错误导致的错误。
测试即文档
经验丰富的开发人员会首先查看用户故事,并为其编写验收测试,用来作为开发的开始。这也是倡导良好用户故事的原因,因此开发人员可以编写好的测试并最终产生良好的PR。
当另一位开发人员开始审查某人编写的代码时,如果他们可以查看测试,查看此PR涵盖的内容,会很有帮助。许多测试框架,例如RSpec,也允许测试打印出预期的期望,以说明正在发生的事情。下面是一个RSpec验收测试示例。
审查代码的开发人员将立即看到此PR包含允许用户创建窗口小部件的变化。运行此测试的输出如下所示。失败的测试将显示为红色。
当审查该PR时,这也允许开发人员审查代码以仔细检查是否已满足和测试用户故事的所有需求,或者此PR的范围是否超出原始用户故事。
高可读性代码
编写高可读性的代码将产生更好的PR。如果代码易于阅读,则意味着它也很容易查看。高可读的代码可缩短审查时间,并降低审查人员和开发人反复沟通的可能性。
代码注释
通常代码需要额外的帮助来解释我们为什么这样写。可以通过编写描述性方法/函数和变量名来解决,以使代码更口语化。另一种流行的解决方案是使用代码注释来解释代码正在做什么。
代码注释有拆分视图。有些团队鼓励自由使用代码注释,而其他团队则倡导纯代码,消除过多的注释。当考虑是否要添加注释来时,可以考虑使代码更人性化,自具说明。必要的情况下,再添加评论可,以帮助其他阅读代码的人。
比如上图中golang序列化的示例代码,去掉注释,函数功能也是一目了然的。
代码注释标签
使用代码注释标记(如TODO或FIXME)来解释注释的性质以及为什么将它放在首位可能更具描述性,如果使用注释来解释代码,则在注释前加上TODO来改进代码,可以审阅人及其他提供改进的人来解释意图。
良好的commits
编写好的git commit消息允许其他人浏览分支的历史记录并了解所执行的操作。做到这点,需要小而频繁的提交。理想情况下,提交应包含完整的,独立的更改。开发人员可以按需cherry pick或回滚commits。对于大型提交或许多"开发进行时" 的不完全commits,很难实现此类操作。
在编写commit消息时,一定要坚持团队的约定高于其他任何内容。要编写有意义的commit消息,好的实践是,在消息前添加动词,例如"增加","删除"或"修复",然后是所做的内容。力求简洁和简单,不要害怕在信息中提供更多信息。下面是一些好的实例:
添加管理面板的登录页面
更改路由以指向新的登录页面
删除旧的登录页面
有些团队喜欢在合并之前将分支squash为单个提交。关于这一点的好处是冲突的合并会更容易处理,并且commit历史更加简洁。但是这样一来,会丢失掉大量的commit信息。如果一个团队擅长做一些小的,频繁的PR,这是良好的实践。
Pull request流程
大多数团队都有某种PR标准流程来组织PR并确保记录所有内容。一个实践流程如下:
1.手动测试运行代码以确保其无错有效。
2.确保测试所有变化。
3.给PR添加标签。
4.更新用户故事的状态并链接到PR。
5.在PR中添加对用户故事的链接。
6.如果不能自动实现,手动更新变化日志。
7.提交PR,请求审阅者进行审查。
根据团队的工作流程,该步骤可能会很长。一些也可以实现自动化,加快流程完成。对于必须手动的执行的,提供参考流程清单会很有帮助。
GitHub/Gitlab 的PR描述支持交互式清单和模板,这次以前的文章中虫虫写过,关注虫虫,可以浏览。还可以在团队的项目管理软件中该流程建模,这样可以和其他项目/开发管理(比如JIRA,禅道等)的审批、消息流程集成。
有疑问加站长微信联系(非本文作者)