注:本文纯属个人观点,不代表我的公司。谨将本文献给我的导师 Marcel May。
我并不是故意要把 POC 和Scrum与低质量的软件联系在一起,它们的流行确实令人生畏,但我们应该客观地考虑构建高质量软件涉及的所有因素。准确地说,POC 和 Scrum 并没有本质上的缺陷会导致它们成为低质量软件的促成因素,但经常会产生导致低质量软件的副作用。
虽然它们不是唯一的因素,但正是因为它们本身并没有缺陷,当我们意识到它们的副作用时,发现它们是最容易和最值得解决的因素之一,所以我们现在关注它们是有意义的。我们将在以后的文章中讨论其他因素。
本文分为两个主要部分:在第一部分中,我们将仔细研究什么是质量以及如何量化它。如果你对定义不感兴趣,可以直接跳到第二部分。在第二部分中,我们将讨论 POC 和 Scrum 如何影响质量以及如何解决这些问题。
在本文的其余部分,为了简洁起见,我将把“高质量”称为“质量(Quality)”,用了大写的 Q。
第一部分:量化质量
质量不是对完美的无止尽追求。如果我们有量化质量的方法,就有可能实现我们的质量目标。在我看来,质量包含了三个层次,我们需要分别量化每一层。
质量是可靠性的度量
人们普遍认为质量是“产品的可靠性”。正如古典科学所认为的,"可靠性"是指在相同的条件下持续得到相同结果的属性。在这个经典的观点中,构建质量解决方案意味着我们需要构建出永不失败的产品。具有讽刺意味的是,以这种方式理解可靠性会损害质量,而不是提升质量。以构建永不失败的产品为目标只会导致极其难以维护的复杂系统,导致质量随着时间的推移而下降。经典的可靠性的问题在于错误地假设了我们可以控制所有的条件,而实际上我们并没有(硬件故障、网络延迟、外部服务节流,等等)。我们需要对可靠性的含义进行扩展,以适应条件不一致的情况:质量不仅是软件产品启动和运行时的可靠性度量,也是软件产品发生故障时的可靠性度量。
要实现这一层次的质量,我们需要考虑两种情况:对于解决方案的每个组件/服务,它们好的场景和最坏的场景是什么。
要量化这一层次的质量,我们需要确定我们想要的可用性、恢复时间目标(Recovery Time Objective,RTO)和恢复点目标(Recovery Point Objective,RPO),这样我们就能清楚地知道我们的系统能够满足的可接受范围。
简而言之:高可用性设计、优雅地失败以及在可接受的范围内实现第一层次的质量。
质量是简单性(优雅)的度量
可靠性是质量的核心,但靠可靠性还不足以体现整个质量的概念。试想一下:如果你有两台机器,它们具有完全相同的功能和可靠性指标,那么机器越简单质量就越好。
质量和性能指标可以一致,但它们不是同一个东西。随着时间的推移,更简单的产品更容易开发、操作和维护。
第二个层次的质量可以通过解决方案组件是否有可以由需求直观解释的目标来量化。在本文的第二部分中,我们将讨论复杂性,并将为你提供如何让你的项目变得更简单的实用建议。我们先来完成质量的量化定义,我们还剩下一个层次要讨论。
质量是 UI 如何表现产品功能的度量
UI 可能会有一个装饰层(颜色、字体等),视觉上的吸引力是很重要的,但我在这里从一个更全面的角度来谈论 UI——把 UI 看作是向用户呈现产品功能的良好程度。你的产品可能是一组 API,那么 UI 可能就是一个门户,用户可以在其中获得示例请求/响应并测试端点。UI 也可以是一个命令行界面,或者被嵌入到用户的系统中(如 Teams/Slack 等等),或者所有这些方法的组合。创建一个高质量的后端是一回事,以一种让用户能够简单有效使用功能的方式来表示后端功能是另一回事。优质的产品以用户体验为中心。
要在这一层构建高质量的产品,你要不断训练你的思维,从用户的角度思考解决方案。记住,技术最终是为人类服务的。
你可以通过执行用户测试来量化这一层,聚合并分析客户满意度驱动因素,并根据这些有形的结果衡量你的解决方案。
第二部分:POC 和 Scrum——复杂性的出现和加剧
随着时间的推移,复杂性会呈指数级增长,所以越早缓解它越好。如果仔细观察一下,你会发现复杂性始于过早地将 POC 转为开发管道。Scrum 的短 Sprint 通常会创造一种环境,有助于对不断变化的需求(这些需求让开发人员很难在项目过程中确定优先级并实现质量目标)做出反应。正如我在这篇文章的引言中提到的,这并不是 POC 和 Scrum 固有的,一旦意识到了,我们就可以解决这些问题。
POC——允许混乱
POC(概念验证)的主要目的是证明一个新想法在概念上是可能的。POC 与实现细节无关。一个人可以骑马去办公室,以此来证明它比开车节省时间,但即使这种 POC 是成功的,也并不意味着骑马去办公室是最好的方式或唯一的方式(更不用说验证简单性、用户体验或边缘情况)。不过,这种 POC 的价值在于“提供了一种比开车更快到达办公室的方法”,所以我们可以把这个概念提升到一个新的层次,寻找替代方案,并比较结果。
需要注意的是,将“寻找解决方案”的过程与“寻找最简单的解决方案”的过程结合起来是违反直觉的。通常情况下,我们解决问题的第一次尝试并不是解决问题的最简单方法。看看你以前的 POC,你很可能会发现有“额外的连接”可以被移除。一个成功的 POC 并不意味着可以马上开始开发,我们需要尝试替代方案,在交付其中一个 POC 并进入下一阶段之前尝试多个 POC。
不过,证明“新颖的想法是可行的”并不是我们进行 POC 的唯一方法。POC 是学习/感受新技术的一种方法,其中的风险是我们可能在新技术上浪费了时间,然后在清理这些混乱之前进入开发阶段。这就是经常出现“技术债务”的地方。
解决这个问题的办法不是少一些混乱,实际上恰恰相反,我们需要更多混乱!很多时候,我们无法清理这些混乱,因为我们通常没有足够的时间去完全掌握新技术。基于开源代码库部署现有的解决方案,创建一个沙盒,这是一种很好的方式,但你应该继续在沙盒中,直到能够映射所有的结果和它们的原因,当你离开沙盒环境时,就可以移除任何不需要的部分。
“考虑替代方案和清理/简化”阶段有时是由开发人员(作为 POC 工作的一部分)完成的,但在快速交付的压力下,这一阶段往往被压缩或跳过。考虑到当今大多数解决方案的复杂性,这个重要的阶段变得势在必行,因此是时候给它专门的时间,并将其作为项目生命周期的一部分。POC 必须经历一个 PONC 阶段,然后才能变成开发管道。
PONC——保证收拾残局
PONC(无复杂性验证)是让简单性/质量走上正轨的护栏。在这个阶段,你希望从 POC 转向开发管道之前简化并消除 POC 中的复杂性。
我在之前的一篇文章中已经写过关于复杂性和简单性的文章,当每个组件的目的很容易在与需求的联系中识别出来时,就实现了简单性。当实现相对于需求来说太过复杂,复杂性(“非直观的复杂性”)就出现了。PONC 旨在解决这两个问题。
将 PONC 付诸实践。
指定你的需求。把它们写下来,与你的团队分享,不要基于模糊的需求来解决问题,因为这可能会导致你解决不存在的问题。
考虑前面讨论过的替代方案。比较结果,写下你选择这个实现而不是其他实现的原因*。实现一个解决方案可能都会有几种方法,特别是当涉及到云的时候,前期的技术选择是构建高质量软件的最重要因素。
从 POC 中移除与需求不直接相关的内容。如果你只有基本需求(因为你还处于项目的开始阶段),那就不要害怕移除所有与这些需求无关的内容,即使只剩下很少的内容。只要坚持好的原则(SOLID/12-factor),然后去掉其他的东西——你可以在需求变得清晰的时候再添加。
使用托管服务来减少基础设施的责任。只要有可能,就向右移动,从 IaaS 到 PaaS,到无服务器,再到 SaaS。如果你做不到这一点,那就把阻碍你的技术障碍记录下来。科技在不断发展,这些障碍可能在未来会被消除,所以了解到底是什么阻碍了你会很有用。“这是我们习惯的做法”并不是坚持 IaaS 的好理由。
如果你的解决方案可以从拆分服务中受益,那么现在是时候这么做了(例如,一些部分可以迁移成无服务器架构,一些可以迁移到 PaaS,另一些可以保留为 IaaS……等等)。
如果你正在重新实现一个现有的系统,请格外注意不要陷入“第二系统效应”(过度工程,因为你想要旧系统中拥有的所有功能。第二系统效应一词在 Fred Brooks 的《人月神话》一书中首次出现)。清楚你想要实现什么,这样你就可以在一段时间内保持简单性(像上面描述的那样,指定需求总是很关键的)。
(*当我提到你应该“写下来”或“记录”替代方案/结果时,我并不是说你应该写三页纸的文档——一行重点突出的句子就足够了,通常比一份很长的文档更好。例如,你可以这样写:“我们选择了一个 Web 应用程序,因为 Azure 函数不支持 Ruby”——如果将来你回头来看你的解决方案,向看看早期的技术决策,并在当初的理由不再成立时需要做出修改,这些信息已经足够了。)
即使在你认为你的 POC 不需要简化的情况下,你仍然可以从与团队进行“如果”这样做而不是那样做的 PONC 讨论中受益。当你能够清楚地说明为什么一个解决方案不能进一步简化时,它会让你相信自己正在正确的轨道上。
Scrum——一个没有设计者的伟大设计
让我们来看看积极的方面——Scrum 将整个项目定义为“用户项目”,这很好,因为它可以帮助我们实现第三个层次的质量。“用户故事”不断提醒我们要从用户的角度来看待产品。
但消极面如下所述。
1. “无第零个 Sprint”的方式通常会导致开发者将他们混乱的 POC 变成 MVP。
教科书对第零个 Sprint 的描述似乎各不相同。一些 Scrum 教科书/Scrum Master 反对第零个 Sprint,因为它违背了 Scrum 的基本前提,即“每个 Sprint 都应该产生一个价值”。其他的则采取更务实的方法,并接受第零个 Sprint(只要你将它称为“启动 Sprint”)。关于你是否可以/应该有第零个 Sprint,网上有一个很长的讨论,但无论你的立场是什么,在你清楚需求和了解所有潜在的解决方案之前,请不要急于着手解决问题。
事实上,我相信我们可以通过扩展我们对“价值”的看法(这里的“价值”指的是 Scrum 规则中“每个 Sprint 都应该产生的价值”)来实现“第零个 Sprint”和“无第零个 Sprint”两个目标。
你会惊讶地发现,通过向产品负责人展示你的早期工作成果能给Scrum团队带来多大的好处。尽早获取他们的反馈不仅能让你更接近他们的愿景,还能让开发团队使用正确的业务术语,而不是每个人都使用自己的术语,因为这些术语对不同的开发人员来说可能意味着不同的东西。
将“价值”的范围降到一个 MVP 或一件功能性产品,这对质量是有害的,因为这将迫使开发人员在有机会掌握所有需求之前过早地做出技术决策。当需求在随后的 Sprint 中变得清晰时,改变这些早期的技术决策通常是非常困难的,这导致开发人员添加越来越多的变通方法来满足需求,而不是通过使用正确的技术来解决它们。一开始缺少 MVP,然后在 Sprint 中进行修补,这样只会导致糟糕的产品质量。如果质量是一个人,他/她会对在第一个 Sprint 就把一个混乱的 POC 变成最终部署到生产中的 MVP 感到惊恐。
(我想借此机会邀请 Scrum 指南的创建者在他们的 Scrum 指南中阐明“价值”,加入可视化原型和架构构件,这些构件可以分享给产品所有者并收集他们的反馈。这将让想要正确实现 Scrum 的开发人员变得轻松一些。)
最后,这里有一些东西可能可以帮你驾驭“第零个 Sprint”,具体取决于你的项目/团队的成熟度和项目交付的时间线。
在某些情况下,设计Sprint可以给你带来很大的好处。虽然它们的结果不一定是可发布的(如 Scrum 所期望的),但在某些情况下,它们可以提供很多价值。
如果你在完成架构讨论/POC/PONC 之前必须创建一个 MVP,可以考虑创建一个(完全或部分)带有模拟后端(例如使用Azure APIM)的 MVP,这样你就可以在后续改变后端技术,同时给产品所有者/测试人员机会接触到具体的东西。
2. 第三层对 Scrum 的关注给第一层和第二层带来了违背质量的风险。
第一层和第二层的质量(想想灾难恢复、保持简单性,等等)就像其他非功能性需求(NFR)一样,在紧急交付项目的压力下,通常会被剥夺优先级。将 NFR 放在产品待办事项列表中是一个很好的实践(如果可能的话,还可以将其中一些放到你的“完成定义”中),这肯定会带来透明度,并将极大地促进 Scrum 团队对这些事项的讨论。但你仍然面临一个挑战,你需要优先考虑实现这些事项。这说起来容易做起来难,尤其是在 Scrum 这样的敏捷环境中。
敏捷的主要目标是将你从阻止你对变更做出响应的僵化的项目计划中解放出来,这非常好,因为它保证你不会偏离利益相关者对其产品的设想。然而,变化是不可避免的,如果你不响应这些频繁发生的变化,敏捷性的主要目标就会被削弱。其副作用是你将经常优先考虑这些变更,而不是质量相关的工作。当可能不会从每个单独的 Sprint 中注意到它的反应性,但如果你把视野放大到整个项目,你会发现你处在一个反应性的环境中,在这个环境中,NFR 的优先级需要特别的关注才能被发现。没有两个 Scrum 团队是相同的,所以我不知道是否有关于如何在各个层次上优先考虑质量的通用规则,但既然你已经意识到这一点,我相信你是在项目中优先考虑质量的最佳人选。
结论
在本文中,我们讨论了质量的三个层次以及如何量化每一层。较差的质量始于早期,在把 POC 变成开发管道之前,应该先进行 PONC。Scrum 帮助我们在第三层实现质量,但我们需要有一个策略,并在第一层和第二层特别关注质量的优先级。
作者简介:
Alaa Tadmori 是微软云解决方案架构师。他是云计算模型的早期采用者和爱好者。Alaa 相信我们今天构建的解决方案正在塑造我们的世界,而这也将是孩子们的世界,所以我们每天都有责任让我们的世界变得更美好。不工作的时候,Alaa 喜欢和家人呆在一起,阅读非小说类书籍。
原文链接:
POCs, Scrum, and the Poor Quality of Software Solutions
相关阅读:
我同情所有的 Scrum 团队,因为他们经常被指导得死去活来
评论