敏捷实践:代码审查-审查失败或引发问题?


53

在为期2周的冲刺结束时,一个任务进行了代码审查,在审查中我们发现了一个功能正常,可读性强的功能,但它相当长且有一些代码异味。轻松的重构工作。

否则,该任务将符合完成的定义。

我们有两个选择。

  • 代码审查失败,因此该票证不会在此Sprint中关闭,并且我们在士气上受到了一些打击,因为我们无法通过该票证。
  • 重构只是一小部分工作,并且将在下一个冲刺(甚至在开始之前)中完成,只是一个很小的半点故事。

我的问题是:从审阅的背后提出票证而不是通过失败有任何内在的问题或考虑吗?

我可以找到并已阅读详细代码评论的资源通常为100%或什么都没有,但是我发现这通常是不现实的。


23
因此,如果您不能因此而使代码审查失败,审查的目的是什么?现在,对您来说似乎是在看是否有效果,但是,这肯定是测试或测试人员的工作,而不是代码审查。
VLAZ

21
我认为大多数答案都遗漏了您的问题中的一个重要要点:否则该任务符合完成的定义。您提到的问题是团队认为“未完成”任务的一部分吗?还是应该在“完成”任务中不考虑这些问题?如果您对“完成”的定义包括“没有代码气味”,那么该任务就不会完成。
乔什(Josh Part)

1
@ErdrikIronrose,所以听起来更改不符合标准,并且可能(不那么容易)可维护。尽管您的其他评论似乎表明更改不属于请求,但在这种情况下,更改不应属于代码审查。如果有人在现有的丑陋骇客旁边编写了正确且符合标准的代码,则可以随意提出修复丑陋骇客的票证并通过当前代码审查。如果有人编写了正确但不符合标准的代码(如您的问题所示),则在正确完成代码之前,请不要完成代码检查。
VLAZ

9
@ErdrikIronrose:啊,所以在编写正在审查的故事时没有创建代码气味的方式,但是已经存在了吗?这是一个重要的区别-考虑编辑问题。
sleske

1
@vlaz,您应该从自己的评论中回答
Ister

Answers:


67

从审阅的背后提出票证而不是通过失败有任何内在的问题或考虑吗?

不是天生的。例如,当前更改的实施可能会发现一个已经存在的问题,但是直到现在才是未知的/显而易见的。失败的票证将是不公平的,因为您会因与实际描述的任务无关的事情而使票证失败。

在评论中我们发现了一个功能

但是,我推测这里的功能是当前更改所添加的。在这种情况下,票证应该失败,因为代码未通过气味测试。

您会在哪里画线,如果没有,您会在哪里画线?您显然认为此代码不够干净,无法以当前形式保留在代码库中。那你为什么还要考虑给门票通票呢?

代码审查失败,因此该票证不会在此Sprint中关闭,并且我们在士气上受到了一些打击,因为我们无法通过该票证。

在我看来,您似乎是在间接地争论要给这张票传递通行证,以提高团队士气,而不是提高代码库的质量。

如果是这样,那么您的工作重点就混了。不应仅仅因为使团队更快乐而更改干净代码的标准。代码的正确性和整洁度并不取决于团队的情绪。

重构只是一小部分工作,并且将在下一个冲刺(甚至在开始之前)中完成,只是一个很小的半点故事。

如果原始票证的实施引起代码异味,则应在原始票证中解决。仅在代码气味不能直接归因于原始票证的情况下,才应该创建新票证(例如,“打碎骆驼背的稻草”方案)。

我可以找到并已阅读详细代码评论的资源通常为100%或什么都没有,但是我发现这通常是不现实的。

通过/失败本质上是一个二进制状态,本质上是全部或没有。

我认为,这是指您将代码审查解释为需要完美的代码或以其他方式使代码失败,而事实并非如此。

该代码不应是完美无缺的,它仅应符合您的团队/公司所采用的合理的清洁标准。遵守该标准是一个二元选择:坚持(通过)或不(失败)。

根据对问题的描述,很明显,您认为这不符合预期的代码标准,因此不应出于诸如团队士气之类的别有用心而通过。

否则,该任务将符合完成的定义。

如果“完成任务”是代码质量的最佳基准,那么我们就不必发明干净代码和良好实践的原则了-编译器和单元测试已经是我们的自动审查过程,并且您不需要代码审查或样式参数。


26
“代码的正确性和整洁性并不取决于团队的情绪。” 仅此一项就可以+1,但是,整个答案的唯一警告是迫在眉睫。如果未能通过此代码审查,则意味着一个备受期待的功能将无法纳入下一版本,则必须在代码清洁度与客户需求之间取得平衡。但是请记住,今天不能满足客户要求的不正确代码明天将成为生产问题。
格雷格·伯格哈特

11
好的答案-坚定但不粗鲁。一个切线点可能是:我们如何在sprint中这么晚进行代码审查,以致在不导致整个sprint失败的情况下无法进行简单的重构?
丹尼尔(Daniel)

@Daniel:开发人员可能会参与其中,或者可能是计划问题。完成任务和完成sprint之间的时间通常最短,因为(在理想情况下)人们会在sprint的关闭时间前后完成最后的sprint任务。您不能花很长时间来审核/修复;或者,开发人员可能根本就不在其余的sprint中。
平坦

8
+1程序员在编写良好的代码后会感觉良好。超越质量控制并不是提高士气的答案。无论如何,偶尔拒绝次要问题都不会使士气受损。如果您的士气因经常未能通过质量控制而遭受打击,那么答案就是要对所有时间都在通过的质量控制不合格采取措施,而不是降低标准。
jpmc26

1
@GregBurghardt:因为我已经看到很多公司都滥用了截止日期的论点,所以我倾向于只同意在且仅当为第一个发布后的sprint创建并计划立即重构任务的情况下,才通过差评。所增加的时间成本为规避代码质量增加了有意义的进入障碍。
平坦

38

在两周的冲刺结束时,一个任务进行了代码审查,从而简化了重构工作。

为什么在冲刺结束时弹出?您认为代码完成后(甚至更早),应立即进行代码审查。您应该检查完成的每个故事的完成定义。

如果您发现自己在演示/冲刺审阅之前不久就完成了故事,以至于您无法完成“微小的”任务,那么您需要在评估工作方面变得更好。是的,这个故事还没有结束。不是因为代码审查,而是因为您不打算合并代码审查中的更改。这就像估计“测试”要花费零时间一样,因为“如果编程正确,它就可以工作,对吗?”。那不是它的工作方式。测试将发现错误,而代码审查将发现需要更改的地方。如果不这样做,那将是浪费大量时间。

综上所述:是的,国防部是二进制的。通过或失败。代码审查不是二进制的,它应该更像是正在进行的任务。你不能失败。这是一个过程,最后完成了。但是,如果您没有适当地计划,您将无法及时到达“完成”阶段,并且在冲刺结束时会陷入“未完成”的境地。这对士气不利,但是您需要在计划中考虑这一点。


5
这正是我想到的答案。如果每个故事都通过自己的分支实现,则不要将审阅和合并分支推迟到sprint结束。相反,应在分支准备就绪后立即创建拉取请求,并继续对该分支进行迭代,直到该分支实际完成,批准并合并为止。如果该过程在冲刺结束时尚未完成,那么故事就不会完成。
Daniel Pryden

20

简单:您查看更改。否则,您无需检查程序的状态。如果我在3,000行函数中修复了一个错误,那么您检查我的更改是否可以修复该错误,仅此而已。如果我的更改解决了该错误,则表示您接受更改。

如果您认为该功能太长,则可以在接受我的更改提出更改请求以使该功能更短或拆分,然后可以根据其重要性对更改请求进行优先级排序。如果确定团队还有其他重要工作要做,则稍后再处理。

如果您可以在代码审查期间确定开发优先级,那将是荒谬的,因此,拒绝我的更改将是尝试确定开发优先级。

总而言之,接受代码更改并根据您在查看更改时所看到的情况立即举票是绝对可以接受的。在某些情况下,如果更改本身引起了问题,您甚至会这样做:如果现在进行更改比解决问题更重要。例如,如果其他人被阻止,等待更改,那么您想取消阻止它们,同时可以改进代码。


4
我认为在这种情况下,更改过长的函数-如果您引入了以前没有的3000行函数(或者以前是10行函数)。
user3067860 '18

3
原则上,这个答案是正确的。在实践中.....如果所有开发人员都相信并实践良好的编码实践与工作量之间的平衡,那么您可能不会经常遇到此问题,然后就可以找到答案。但是……似乎总是有一个或两个开发人员为了节省5分钟时间而又忙又忙。而他们忽略了他们将要花上几小时甚至几天甚至几个月的工作。在这些情况下,这个答案只是重新开始并重新设计整个系统的一个滑坡。
Dunk

+1,尽管我认为您应该改写最后一段,以使它突出表明签入有问题的代码应该是绝对的例外。我的意思是,仅仅有人被阻止不够。失败一个冲刺也不是足够的借口,当然也不是可以重复使用的借口。
Frax

@ user3067860如果您已将10行功能转换为3000行功能-那么显然会失败。如果您将3000行功能转换为3010,则可能会通过。但是,如果您将100行功能(通常太大)变成了300行功能(绝对太大)怎么办?
马丁·邦纳

9

代码审查失败,因此该票证不会在此Sprint中关闭,并且我们在士气上受到了一些打击,因为我们无法通过该票证。

这似乎是问题所在。
从理论上讲,您知道应该做的事,但是由于临近截止日期,因此您不想做自己想做的事。

答案很简单:如果在冲刺的第一天就获得了相同的代码进行代码审查,则应做任何事情。如果可以接受,那么现在应该接受。如果不会,现在就不会。


“亲爱的客户,由于我们的代码有效,您又不能再使用2-3周,但是我们不喜欢它的外观”,...请不要与我们的竞争对手竞争...或者告诉首席执行官!
RandomUs1r

6
@ RandomUs1r客户不应具有此类信息。没做完是因为没有足够的时间来做,就是这样。客户是否规定应如何编写代码?如果您打电话给电工在家里固定布线,您是否会“仅需更换电缆,而不必费心检查电缆是否正确”?还是您告诉您的医生“我病了-给我一些药但不要先诊断我”?代码审查应该是工作的固有部分,而不是客户要求的内容。
VLAZ

1
@ RandomUs1r:““亲爱的开发人员,为什么功能未完成?” -答案应该是“因为我们没有足够的时间将其构建到可接受的质量水平”,也许之后是“我们可以给予它如果您愿意在质量上做出让步”
Bryan Oakley,

1
@ RandomUs1r,所以基本上您现在想牺牲代码质量,这可能会使以后实现功能变得困难得多。现在进行2天的修复可以很好地为您节省4周的修复。因此就是“亲爱的客户,您不能再使用2-3周,因为现在实施次要功能需要花费很长时间”。还是冲刺结束还是主要的截止日期?如果这是一个主要的截止日期,我现在可以看到合并,在接下来的两天内写一个修复程序,并在截止日期之后立即提高PR。
xyious

5
我要说的是,如果您的标准在冲刺的第一天和最后一天是不同的,那么您就没有标准,并且质量将不可避免地下降。
xyious

5

在此过程中,很大一部分是决定要做什么然后坚持到底。这也意味着不要过度投入并及时进行同行评审,以使测试确保工作也可以完成。

当涉及到代码审查问题时,有几种方法可以处理它,而正确的选择取决于一些因素。

  • 您可以自己清理代码,然后让此人知道您做了什么。提供一些指导机会,但这应该是相当简单的事情,可以在几分钟内完成。
  • 你可以踢回与评论,什么是错的。如果错误处理不善,或者开发人员不断重复同样的错误,则可以保证。
  • 您可以创建票证并招致技术债务。那里有票,以确保您以后付清。可能是您时间紧迫,在检查变更的过程中,您看到了一个与变更没有直接关系的更大问题。

最重要的是,当您完成工作时,就需要完成它。如果问题超出了开发人员的工作范围,请升起旗帜并继续前进。但是您不应该在冲刺结束前几个小时就处于一个位置,而您现在正要进行同行评审。闻起来像是过度使用您的资源,或拖延了同行的评论。(过程气味)。


4

将代码审查问题排在优先顺序上没有内在的问题,但是听起来像一个团队,您需要达成共识的主要问题是:

  1. 您的代码审查的目的是什么?
  2. 代码审查的结果如何与工作项目的“完成”定义相关?
  3. 如果代码审查确实适用于门控测试,那么哪些问题被视为“阻止者”?

这一切都取决于团队同意“完成”的定义。如果通过零问题通过代码审阅是对工作项完成的定义,则您不能关闭不满足此要求的项。

就像在单元测试期间单元测试失败一样。如果必须通过单元测试,则可以修复该错误,而不要忽略单元测试。

如果团队不同意代码审查是“完成”的定义,则您的代码审查不是对工作项的门控验收测试。它们是团队活动,是您积压流程的一部分,以查找可能需要的其他工作。在这种情况下,您发现的任何问题都与原始工作项的要求无关,并且是团队要确定优先级的新工作项。

例如,即使团队确实讨厌看到变量名“ myObkect”,也不会影响某些变量名中的拼写错误,因为这不会影响已交付的业务功能,这完全可以接受。


1

投票率较高的答案很好。这个解决了重构角度。

在大多数情况下,重构时的大部分工作是了解现有代码。由于以下两个原因之一,在那之后进行更改通常是工作的较小部分:

  1. 如果只是使代码更清晰和/或简洁,那么必要的更改就显而易见。通常,您可以通过尝试看似更干净的更改并查看它们是否确实有效或者是否错过了更复杂的代码中的一些细微之处来加深对代码的了解。

  2. 您已经在考虑使简化新功能的构建所需的特定设计或结构。在那种情况下,开发该设计的工作就是产生对此需求的故事的一部分。它与您需要进行重构以实现该设计无关。

学习和理解现有代码对非永久性的工作是相当大的工作(从现在开始的一个月,如果他们在那个时间里不继续阅读或使用它,那么人们可能会忘记很多代码),因此除了在导致您出现问题或计划在不久的将来进行更改的代码区域之外,这样做是没有意义的。反过来,由于这是重构的主要工作,因此除非对当前的代码造成问题或计划在不久的将来进行更改,否则不应对代码进行重构。

但是,有一个例外:如果某人当前对代码的理解会随着时间的流逝而消失,那么利用这种理解来使代码更清晰,更迅速地被理解,将是一项不错的投资。这就是刚完成一个故事的人的情况。

重构只是一小部分工作,并且将在下一个冲刺(甚至在开始之前)中完成,只是一个很小的半点故事。

在这种情况下,您考虑制作一个单独的故事进行重构是在几个方面的警告信号:

  1. 您并不是在考虑将重构作为编码的一部分,而是将其视为单独的操作,从而使重构可能会在压力下被丢弃。

  2. 您正在开发的代码将在下一次有人需要使用它时需要更多的工作来理解,这会使故事花费更长的时间。

  3. 您可能会浪费时间和精力进行重构,而从中获益不大。(如果以后发生了更改,无论如何,仍然有人必须重新理解代码;将其与重构工作更有效地结合在一起。如果以后没有发生更改,则重构没有任何作用。的目的,也许是审美目的。)

因此,这里的答案是使项目无法明确说明您的流程中有什么失败(在这种情况下,这是开发人员或团队未分配时间进行审核并实施未审核的更改),并要求开发人员立即继续工作在项目上。

当您要估计下一个迭代时,请重新估计现有故事,因为剩下的工作似乎需要通过审核,然后再将其添加到下一个迭代中,但要保留上一个迭代的估计。在下一次迭代结束时完成故事后,将历史工作总量设置为第一估算和第二估算的总和,这样您就可以知道实际投入了多少估算的工作。这将有助于在您当前流程的当前状态下,对相似的故事进行更准确的估计。(即,不要假定您的明显低估不会再次发生;假定它会再次发生,直到您成功完成了类似的故事,同时减少了工作量。)


1

对于“未通过”代码审查这一概念的答案和评论缺乏回应,我感到惊讶,因为这不是我个人所熟悉的概念。我也不会对这个概念或团队中使用该术语的任何人感到满意。

您的问题明确要求“敏捷实践”,因此让我们重新回顾一下敏捷宣言(强调我的观点):

我们正在探索通过开发软件并帮助他人开发软件的更好方法。通过这项工作,我们开始重视:

  • 个人与流程和工具之间的互动
  • 工作软件超过全面的文档
  • 客户合作而非合同谈判
  • 响应计划变更

也就是说,尽管右侧的项目有价值,但我们更重视左侧的项目。

与您的团队交谈。讨论有问题的代码。评估成本和收益,并作为一个有凝聚力的专家小组,决定是现在,以后还是永远不要重构此代码。

开始合作。停止失败的代码审查。


我全力合作。但是,如果不是“失败”,您将使用什么术语?即使是作为一个小组讨论,一个人也会说“这还不够,需要重构”,这意味着,它没有通过质量检查,对吗?
Erdrik Ironrose

1
@ErdrikIronrose我从未使用过-或需要使用-代码审查失败的术语。有人审查了代码,围绕改进的任何潜在问题进行了讨论,然后决定是否解决这些问题。没有涉及“通过”或“失败”,只有沟通和进步。我不确定为什么需要橡皮图章。
Ant P

0

在评论中,我们发现了一个可以正常工作,可读的函数,但是它很长并且有一些代码味道。

从审阅的背后提出票证而不是通过失败有任何内在的问题或考虑吗?

没问题(在我的团队看来)。我认为该代码符合票证中规定的接受标准(即有效)。创建一个待办事项以解决长度和所有代码气味,并像其他票证一样对它进行优先级排序。如果它真的很小,那么就为下一个冲刺优先。

我们的名言之一是“选择逐渐完善而不是推迟完善”。

我们的流程非常流畅,并建立了相当多的“概念验证”功能(每个sprint 1或2个),这些功能可以通过开发和测试来实现,但绝不能通过内部利益相关者的审查(嗯,我们可以这样做吗?) ?),alpha或beta ...有些可以生存,有些则不能。

在当前项目中,我忘记了我们已经构建了某项功能多少次,并将其交到了利益相关者的手中,而一两次冲刺之后,由于产品方向发生了变化或由于需求而导致了全部移除,有关如何实现该功能的完整重铸。删除的功能的所有剩余“优化”任务,或不符合新要求的任务都会被删除,以及积压整理的一部分。


0

我认为有两种方法可以解决此问题:

  1. 学术方式
  2. 现实世界的方式

从学术上讲,当不满足代码质量标准时,大多数代码检查过程都会使PBI(产品积压项目)的部署失败。

但是,现实世界中没有人会跟随T敏捷,因为(出于多种原因)不同的行业有不同的要求。因此,应根据具体情况立即确定代码或承担技术债务(您很有可能创建新的PBI)。如果要损害冲刺或发布,或引入不合理的风险,则业务利益相关者应参与决策。


2
现实世界中没有人会遵循T的敏捷性 -如果我们的规则太严格,它将不再“敏捷”,对吗?
圣保罗Ebermann

@PaŭloEbermann我曾与一家公司进行过一次有趣的谈话。他们声称他们的过程不是敏捷的,因为它不是敏捷的教科书示例。即使他们所做的一切都是本着敏捷的精神。我向他们指出了这一点,但只是(基本上)遇到了“不,即使我们大量借用概念,我们也没有遵循既定的敏捷程序。因此,我们并不敏捷”。真是奇怪。
VLAZ

正如其他审阅者指出的那样,在这种情况下,可能会从代码未能真正通过审阅中吸取教训。在我看来,好像这个项目的人们真的不太了解,a)您需要留出时间回顾和修复每个故事,并且b)留下必要的干净代码是重构的重要部分故事。在这种情况下,最好的办法是使故事失败,以使这些事情确实不是可有可无的。
Curt J. Sampson

@Curt从开发人员的角度来看,我的观点可能并不受欢迎(我也是一名开发人员),但业务确实应该放在首位,他们签署薪水,这值得一提。就离开时间而言,我将再次挑战您对现实世界的理解,您需要意识到这并非总是可能的,并且许多sprint都很紧张,因为开发人员也需要在sprint结束时要做的事情。这并不是因为代码的SOLID,部门可以每2周提高1/10天的工作量,却什么也不做,这在短期内可能很棒,但长期而言却并不可行。
RandomUs1r

@ RandomUs1r我也在现实世界中工作,我一直都在走捷径,而且我总是把业务放在首位,所以我认为我在这里并不缺乏理解。但是,OP的描述不是“我们通常总是正确地做到这一点,而这仅仅是一个标准的小打”,否则他就不会发布这个问题。正如我在回答中所解释的那样,它看起来像一个过程问题,您可以通过放松之前练习正确地执行过程来解决该问题。
Curt J. Sampson

-2

都没有。如果未通过代码审查,则该任务不会完成。但是您不能使基于个人意见的代码审查失败。代码通过;继续下一个任务。

这应该是一个轻松的调用,并且事实并非如此,这表明您没有足够清晰的书面规则来进行代码审查。

  1. “功能很长”。写下:函数的长度必须少于X行(我建议有关函数长度的规则是一件好事)。

  2. “有一些代码气味”。写下来:公共功能必须对功能和性能进行单元测试,CPU和内存使用率都必须在x和y范围内。

如果您无法量化通过代码审查的规则,那么您将得到这些基本上是“您不喜欢的代码”的案例。

您是否应该失败“不喜欢的代码”?我会说不。您自然会基于非代码方面开始通过/失败:您喜欢这个人吗?他们是为自己的案子有力地争论还是按照他们的指示去做?他们在审核您时是否通过了您的代码?

另外,您在估算过程中添加了无法量化的步骤。我根据我认为应该如何编程来估算任务,但是最后我必须更改编码风格。

这将增加多长时间?相同的审阅者将进行后续代码审阅并与第一审阅者达成一致,还是会发现其他更改?如果我不同意变更并在寻求第二意见或争论该案件时推迟这样做,该怎么办?

如果要快速完成任务,则需要使其尽可能具体。添加模糊的质量门不会帮助您提高生产力。

回复:不可能写下规则!

其实并不难。您的意思是“我无法表达“好的”代码的意思”。一旦认识到这一点,如果您开始说某人的工作还没完成,就可以看出这显然是人力资源问题,但您不能说为什么。

写下您可以使用的规则,并就如何使代码“良好”进行讨论。


6
不,您遗漏了“拥有一个完美且普遍适用的标准且无歧义”并不是进行代码审查的现实前提。总是会有您尚未解决的新型问题,因此您需要能够在未知领域做出决定。当然,您应该随后将该决定记录在案,以使其不再是未知领域,但是您的答案基于这样一个假设:只要您在审查之前起草了完善的规则,就可以以某种方式保证没有未知领域。你把马车放在马的前面。
扁平的

5
诸如“函数必须小于x行长”之类的绝对也不是答案
Blrfl

2
与Blrfl达成协议。函数(通常)不应超过20行。但是将其作为绝对规则是一个错误。特定情况总是胜过一般规则:如果您有充分的理由使函数超过20行,则可以这样做。
马特·梅瑟史密斯

1
您不需要为遵循法律规范编写的代码制定规则……您可以有一些准则,也可以假设您都是试图实现相同最终目标的成年人(工作,可读,可维护的代码)。让所有团队成员真正地投入到团队中并愿意一起工作对于Scrum至关重要,因此,如果您没有这样做,那么Scrum也许无论如何都不适合您的团队。
user3067860

2
@Ewan当然。我的意思是,OP具有一定的功能准则,而不是规则。无论在何处设置阈值,它都会提供建议以帮助人们发现难以维护的代码,但这绝不是绝对的规则。如果(如OP所述)它实际上是完全可读的,并且审阅者同意它是完全可读的,并且没有适当地测试它的问题,那么该函数的定义是正确的大小。该评论可能会得到一行提示,说“是的,比建议的要长,但我们同意这是可以的”,然后完成工作。在那之后进行重构就是镀金。
格雷厄姆
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.