如今,我们应该批判性地思考我们如何在编程中做事。我们需要将工程方法应用于我们的流程。作为软件工程师,我们对抽象类和纯函数的讨论充满信心。另一方面,当需要讨论“管理”的事情时,我们会逃跑。
我最喜欢的具有大量缺陷的广泛传播的编程过程是代码审查。这个故事将从不同的角度来看待它并提出改进意见。
我读到的关于软件检查的第一个重要事实是在 Robert Glass 的“软件工程的事实和谬误”一书中。它声称以下内容:
在运行第一个测试用例之前,严格的检查可以消除软件产品中高达 90% 的错误。
我无法确定这些话是否仅与代码审查有关。我将它们视为不同类型的检查,稍后我将对其进行描述。
鲍勃·马丁叔叔帮助我发现了我们现代评论的根源。 Michael Fagan 于 1976 年在他的文章“设计和代码检查以减少程序开发中的错误”中提出了检查的概念。
我在那里发现了以下三种检查:
Fagan 的工作并没有提出一种新的编码方法,而是记录了已经存在的现象并为其辩护。然而,这篇文章是我发现的最早的书面检查标志。
代码检查看起来像我们现代的代码审查。为什么我们今天会错过其他类型的检查?
今天代码检查的流行和其他类型的检查几乎不存在来自我们的工具。 GitHub、BitBucket 或 GitLab 都有内置的代码审查工具,它们自然适合 Git 流程、GitHub 流程和其他方法。
您在设计活动中使用什么工具?这与 UI/UX 无关。它是关于您将构建的代码的结构。您可能听说过 CASE 或 UML 工具。我还没有看到它们在我工作的任何公司中被认真使用过,我已经工作了七次。
在 HackerNoon 上,“ UML ”搜索查询只产生两个相关结果。所以在没有切实的解决方案设计流程的情况下,没有地方介绍设计检查。在我领导的团队中,我们使用 Miro 进行界面设计。这个过程可能会更令人满意:与其他图表工具一样,您很快就会开始绘图而不是解决您的问题。我们可以独立于我们的工具。这是“投资无限”一书中的一小段引述来支持这一点:
...如果我们只做工具可以做的事情——那么我们将永远受限于我们工具的功能。
让我们从不同的角度来看看其经典形式的过程。它们中的每一个都显示出严重的问题。
BPMN 是一种业务流程建模符号。它用动作、事件、逻辑网关、消息和其他方式描述流程。如果您想开发算法,我什至建议使用它,因为它比流程图更具描述性。让我们用这种符号描述单个审阅者的代码审查过程并对其进行分析。我使用了基于文本的工具来生成即将发布的图表。有很多回信,上面有一个小故障。
这一切都从公关创作开始,这里没有什么值得注意的。下一步是通知审阅者,这是所有不同方式的简化,可以说:“嘿,我的 PR 正在等你!👋”这里重要的是从当前活动中解放出来。 PR 在这里等待一个未知的持续时间。然后审阅者潜入任务并进行审阅。 PR 有可能立即被合并。但是,可能会发生相反的情况:会出现一些需要修复的评论。
该代码的作者可能已经在下一个任务中,并且还有一个未知长度的等待时间。此外,回来需要上下文恢复、评论的解释和修复。
下一步是审稿人的通知。
我们不是已经去过了吗?是的你是对的。我们刚刚完成了潜在无限循环的第一次迭代。是的,无限。总是有机会出现新的评论。或者其中一个等待期将永远持续下去。
我们是否希望将潜在的无限循环作为我们日常操作的一部分?我不确定,因为更快的交付通常是可取的。
有时,我们的团队中有高级开发人员或架构师作为审阅者。他们希望有一个一致的代码库,坚持一些方法和模式,避免其他的。他们有远见。开发人员也有他们的想法。通常,他们不知道他们的长辈的意图。它需要一致的广播或辅助环境,这很少发生。让我们看看下面的图片。
您可以看到代码审查参与者的两种愿景有何不同。第一次迭代后,它们开始对齐,但仍有一段路要走。审稿人调整自己的视野,代码作者根据提案的解释行动。
我们可以使用“想象你要房子然后惊喜!这不是你所期望的”比喻。或者我们可以看看它的核心。想象一下,你已经要求一个人完成某事。然后你回来看看结果,但你对成就者所做的一系列决定感到惊讶。不要感到惊讶,因为您不需要特定的决策框架。
图像不言自明。你会要求你的工程师同事在花费了很多天后解决设计问题吗?想象一下,你的 sprint 结束了,而罚单是站立时紧张的原因。您将帮助您的同事尽快将其合并。另一方面,可能会有一名高级工程师审查初级的代码。他可以要求他走很长的路来解决一开始做出的决定。然而,现在是给出这样建议的合适时机吗?如此多的决定已经基于错误的选择。
精益制造尚未影响编程。但是,我们已经有了 Mary Poppendieck 和 Tom Poppendieck 的“精益软件开发”一书中的工具。这个工具是软件开发的七大浪费:
部分完成的工作,
额外的功能,
重新学习,
交接,
延误,
任务切换,
缺陷。
经典代码审查赢得 7 分中的 6 分!🎉
我们已经从多方面讨论了代码审查的问题。我们可以做些什么来修复这个过程?
在本节中,我们将涵盖与上一节相同的主题,但从修复的角度来看。
在这里,我们有一位代码作者正在等待审核完成,并且在审核者的概述之后进行了结对编程会议。
在提议的过程中,我们可以看到与前一个相比有几个显着的变化:
不再有潜在的无限审查循环;
单次等待时间未知,我们也会处理;
无需为代码作者恢复上下文或解释反馈。评论现在可以作为对这对的提醒。
这个过程发生的核心先决条件是什么?现在一个团队需要一个额外的角色。它意味着有人做辅助活动,例如处理技术债务,或修复较低优先级的错误。当代码审查出现在雷达上时,此人立即放弃当前任务并跳入到达的 PR。如果您曾经想知道为什么您的工作结构需要一些松弛,这就是一个例子。如果每个人都忙于最重要的功能,您将在稍后交付它们。
我们在代码审查中讨论什么?在开发过程中做出的决定。我们一个小时前做了一些,一周前做了一些。我们的选择相互叠加,并不独立。
您有机会质疑接下来数百人所依据的第一个决定,您有多开心?你可能对这个机会不满意。
讨论设计决策的最合适时间是它们出现的时间。我们需要另一种类型的审查:设计审查。有时当问题具有挑战性时,最好花一些时间制定计划并与知识渊博的同事讨论。
有一句著名的军事格言:
没有任何作战计划能在与敌人的接触中幸存下来。
如果我们将它翻译成系统的语言,我们会得到类似:“当第一个反馈到达时,系统肯定需要调整它的行为”。在编程的情况下,反馈将是我们在尝试将设计实施到我们的系统中时面临的问题。有些决定需要修改。我们需要改变它们,并再次与审稿人产生分歧。
Adam Thornhill 在其压倒性的“软件设计 X 射线”一书中提出了一种方法:
这就是为什么我建议您更早地完成初始代码演练。与其等待功能完成,不如在完成三分之一时展示和讨论每个实现。少关注细节,多关注整体结构、依赖关系以及设计与问题域的匹配程度。当然,完成三分之一是主观的,但应该是基本结构到位、问题很好理解、初始测试套件存在的点。在这个早期阶段,设计的返工仍然是一个可行的选择,在这里发现潜在的问题有很大的回报。
我为我的团队创造了一个术语来使用一种方便的语言:骨架审查。我希望它有助于反映活动背后的想法。
提议的过程消除或显着解决了已发现的废物。
我们已经审查了单个作者和单个审阅者的代码审查方法。当更多的审阅者出现时,问题就会成倍增加。因此,不幸的是,如果您最近增加人员来查看您所做的所有决定,并非所有错误都会变得浅薄。相反,您稍后会合并您的代码。
在尝试引入提议的流程时,我遇到的两个最具挑战性的问题如下:
开发人员将审查阶段视为已完成的工作。当提议在日常工作中引入冗余时,经理们会感到震惊。他们不管理消防队员的队伍真是太好了。
第一个问题的解决方法是重复和速度。
第二个问题更复杂。在这里,我想引用 Daniel Vacanti 的“Actionable Agile Metrics for Predictability”一书:
联邦快递采用了很多策略,但最重要的一个可能是联邦快递在任何特定时间都有空飞机。是的,我说的是空飞机。这样,如果某个位置不堪重负,或者如果由于定期安排的飞机满员而导致包裹被遗漏,那么一架空飞机将被重定向(应该说是及时)到问题点。在任何特定时间,联邦快递都有“空余”!
如果您是经理,请在下次规划利用率最大化时考虑这一点。
我们对更新满意吗?是的,它看起来比我们现在拥有的要好。
我们能做得更好吗?我们可以。
目标是在我们可以保证所有必要的质量已经在代码中的时候消除代码审查。为了实现这一点,我们需要构建一个辅助决策框架来帮助开发人员应用被视为好的做法并避免坏的做法。我从未听说过这样的框架,但在 IDE 中的 linter 是朝着它迈出的一步。
感谢您的阅读!如果您想讨论所描述的想法,请写评论。