本系列文章旨在为人们经常讨论但难以捉摸的软件体系结构和设计概念提供新的视角。 通过具体的示例,尼尔·福特为您提供了进化架构和紧急设计的敏捷实践的坚实基础。 通过将重要的架构和设计决策推迟到最后一个负责任的时刻,可以防止不必要的复杂性破坏您的软件项目。
正如我的一些读者所知道的那样,本系列文章与我在会议上发表的同名演讲是共生的。 演讲问答部分出现的一个常见主题涉及将敏捷设计融入尚未完全吞噬敏捷红色药丸的生态系统中。 关于这个主题的许多问题使我意识到它的重要性。
本系列的前几篇文章涵盖了紧急设计的技术考虑,但是软件设计并不是凭空发生的。 非技术方面的考虑因素也会对设计产生影响。 有关紧急设计的最常见问题之一是“您如何估算?” 新兴设计中最常见的混淆器之一是技术债务 -由于某些外部因素(例如进度压力)而对您的设计造成的折衷。 本部分讨论如何估算紧急设计,以及如何提出令人信服的偿还技术债务的案例。
马丁·福勒(Martin Fowler)在今年早些时候的一次联合主题演讲中提出了一个很好的类比,他和我在今年早些时候就敏捷实践为何起作用的问题做了演讲。 考虑一下水果蛋糕和酒店淋浴。 福勒显然有一个致命的水果蛋糕食谱。 他已经煮了很多遍,并且他有信心,如果他能正确地测量配料并在指定的时间内在指定的温度下烹饪,那么他每次都会得到相同的水果蛋糕。
对比为酒店淋浴选择正确温度的经验。 将其设置为与您在家中使用的设置相同并跳入水中是很危险的。相反,您可以将其打开并对水温进行采样直到温度变高,然后使用快速反馈循环进行调整,测量,重复直到感觉正确为止。
这两种类型的过程之间的差异说明了规定项目和反馈驱动项目之间的广泛差异。 如果您以前曾多次煮过同一道菜,那么您可以放心地遵循食谱。 同样,如果您满足以下条件,则对软件项目的估算也很容易:
您正在构建之前(不止一次)编写的确切软件。 您拥有与过去建立团队时完全相同的团队。 环境是完全一样的。编写新软件并不是确定性的过程。 它具有很高的适应性,这表明基于反馈的方法比规定的方法更好。 这使我回到最初的问题,稍作重申:“您如何估算高度自适应系统中的设计工作?”
我的雇主(ThoughtWorks)通过多种方式缓解了软件项目的高度可变性。 首先,我们说服客户,我们提供的第一个估算值并不准确。 无论如何,在软件中无法进行精确的估算,因为在您对项目了解最少的时候,系统会要求您提供该项目最细微的细节之一。 我们事先告诉客户,我们的第一个估算将基于对粗粒度业务需求和技术架构的理解。 (有关此过程的更多详细信息,请参见迭代0侧栏。)
ThoughtWorks通常以称为迭代0的初始阶段开始新项目。 在此期间,其时间长度取决于估计的项目规模,但通常在两到八周之间,同时发生两项活动:
业务分析师定义了粗粒度的需求,这些需求通常是在索引卡上捕获的(之所以选择此选项,正是因为您不能在它们上放置太多细节)。 技术成员定义架构(如第一部分中所定义的“以后很难更改的东西”)和任何非功能性要求(顺便说一句,我讨厌这个术语,我更喜欢服务质量 ),例如可伸缩性和性能。 。在开始阶段的最后,业务人员和技术人员会根据Mike Cohn出色的著作《 应用案例:敏捷软件开发》 (请参阅参考资料 )中定义的技术,来评估每个故事的复杂性。
一旦团队完成了流程,现在就可以估算出项目的总复杂度,从而可以创建燃尽图和有关项目进度的其他统计数据。 使用负载因子,项目经理将复杂性转换为时间,从而粗略估计所需的时间。
粗粒度估计反映了对范围和工作的初步了解。 但是,我们不会长期依赖该估计,因为一旦我们开始工作,我们的理解就会开始增长。 因为敏捷开发发生在相当短的迭代,它允许项目经理能够收集真实的统计数据上的所有的项目的不同的细节: 这支球队真正的速度,在这个项目上,在这种环境下,对于这个问题域。 由于迭代由重复的相同活动组成,因此项目经理可以立即收集实际数据。 经过三到四次迭代,项目经理已经调整了负载系数(用于将复杂度转换为时间的数字)以反映该项目的实际情况。 因此,我们通常会在经过几次迭代后使用更准确的数字来更新我们的估算值。 这是在高度适应性环境中可以做的最好的事情:进行有根据的猜测,然后立即开始应用实际数据来衡量(并反馈)您的工作方式。
因为最初的估计是如此粗粒度,所以存在大量的摆动空间来处理紧急设计,重构和其他技术卫生活动。 除非它们涉及异常多的设计应用程序,否则ThoughtWorks项目不会专门为这些实践保留时间,就像我们不会为会议等其他常见实践保留时间一样。
将设计时间摊销到项目预算中的另一种方法是在进度表中创建设计检查点 。 对于那些从保留大量特定设计时间的大型设计方法转变的组织中,这尤其有吸引力。 如果已经预计到该时间,请使用相同的时间,只是不要提前进行。 取而代之的是,在进度表中以合理的里程碑(例如发布或少量迭代)放置一些标记,以重新审视现有的设计决策并进行下一轮微设计。 通过等待设计决策直到最后一个负责任的时刻,您可以更好地了解和了解决策的实际影响。 如果您保留典型的前期设计时间并将其分散到整个项目中,将会发现您将有大量的时间来进行设计决策。
软件项目通常需要时间解决的另一个任务是偿还技术债务。 接下来,我将讨论一些工具和技术,这些工具和技术可以帮助您向非技术人员介绍这个问题。
在本系列的第一期中,我介绍了技术债务的基础-沃德•坎宁安(Ward Cunningham)首次阐明了一种了不起的隐喻(请参阅参考资料 )。 技术债务类似于信用卡债务:您目前没有足够的资金,因此您会为将来而借款。 同样,您的项目没有足够的时间来做正确的事情,因此您破解了即时解决方案,并希望利用将来的时间来重新改造它。
Martin Fowler撰写了大约四个象限的技术债务(请参阅参考资料 ),如图1所示:
第一象限( 鲁ck和故意 )是有意识地产生的。 经典动画片中对此进行了说明,该动画片显示了一位经理告诉一群开发人员:“你们很忙,我上楼去看看他们想要什么。” 拥有大量此类债务的公司非常急于愿意以不断下降的速度进行交易,以实现便捷的交货。 显然,这对于长期使用或庞大的代码库而言是不可持续的。 第二象限( 审慎和蓄意 )是技术债务的最常见表现。 只要您意识到自己已经背负了债务并可以偿还,这是以后最少会引起大问题的一种方法。 第三象限( 鲁and和无意中 )是最令人不安的,因为这些开发人员招致债务却没有意识到。 开发人员对此类项目的一种常见说法可能是:“将所有5,000行代码直接嵌入JSP中确实很方便,这样您就可以上下滚动查看所有定义的位置(全局)变量。”
第四象限( 审慎和无心 )似乎不应该存在-如果您审慎,怎么会无意呢? 实际上,这是具有专家设计师的项目的普遍结果。 即使是软件设计的最优秀从业者,也无法预期设计会随着时间的流逝而展现和发展的所有方式。 最后一个象限反映了一个事实,即软件中最棘手的问题之一(引用唐纳德·拉姆斯菲尔德的诗作)是我们不知道自己不知道的事实。 换句话说,最难处理的事情是我们什至不知道是问题的事情。
技术债务是软件世界中的现实。 它永远不会消失,因为计划压力将始终存在,这反映出这样一个事实,即业务决策的制定速度比我们将其编码为软件的速度更快。 技术债务本身并没有什么坏处,就像现实世界中的债务没有本质上的坏处一样。 正如审慎的公司从战略上承担货币债务一样,软件也可以做到这一点。 问题不是债务,而是谈判偿还债务。 根据我的经验,示范胜过讨论。 如果您去找项目经理说:“我感到恶心,我晚上无法入睡;我怀疑我们的代码开始变得糟透了,”您的经理会回击:“您能证明吗?” 您可以。 接下来,我将展示两个技术债务插图:第一个是手工生成的,第二个是使用一个名为Sonar的工具的。
第一个例证来自于现实世界的ThoughtWorks的项目面向公众的媒体网站,其技术领先埃里克Dörnenburg(见相关信息 )是软件度量和可视化的世界知名。 在这个项目上,他创建了一个图表,显示了技术债务的增加,如图2所示:
Dörnenburg使用低级工具的巧妙组合制作了图2中的图表。 要生成圈复杂的数字,他用JavaNCSS(请参阅相关的主题 )。 他编写了一个脚本,该脚本从版本控制中检出代码的版本1,在其上运行JavaNCSS以生成数据,并将其保存在带日期的文件中。 该脚本对版本控制中的每个版本重复此过程。 完成后,他将拥有大量带有原始数据的XML文件。 然后,他编写了一个小的Ruby脚本,将各个文件中的数据拼合为一个逗号分隔值(CSV)文件,然后将其加载到Excel中并告诉它进行图表处理。 生成此图表一次后,您可以通过保留原始版本的原始数据来缩短该过程,从而只需使用持续集成就可以针对每个版本更新数据。
图表的水平轴显示时间范围(2006年4月1日至2007年8月18日),垂直轴显示每行代码的圈复杂度。 ( 该系列的本期文章在有关测试的讨论中涵盖了圈复杂度度量标准。)底部覆盖的灰色数字是项目的版本。
该代码库的第一个公共“上线”版本发生在版本3上。在该图上,您可以看到直到那个点为止,复杂性的数量和波动性都在上升,这几乎是站在旁边的商务人士的副作用。在开发人员的肩膀上,不断问:“完成了吗?” 在第一个发行版上线之前,他们不会赚钱,因此显然他们对此感到担忧。
项目到达船期后,Dörnenburg创建了该图的第一部分,并将其显示为不断增加的技术债务的证据,然后用它来协商几个短期维护版本(版本3.0和3.1)以清理一些债务。 这种努力的惯性一直释放到7,由于不相关的原因,债务再次开始上升(尽管以更加受控的方式)。
图2中的图表令人印象深刻,但是需要一些工作来引导该过程。 开源工具Sonar可以替代您自己的工具(请参阅参考资料 )。 Sonar为您的代码库捕获了大量指标,并以图表和其他可视化形式显示它们。 Sonar的创建者已将它与各种开源项目(可通过运行中的Sonar实例在http://nemo.sonarsource.org/进行访问)进行了比较,其中包括Struts。 图3显示了Sonar对Struts的结果:
声纳显示常见质量工具在Java领域的输出,包括CheckStyle的(见相关信息 ),代码覆盖率,以及它自己的指标之一,该技术债务计算器(在右侧所示)。 此公式使用从项目代码上运行的指标得出的一堆数字。
声纳似乎开箱即用地产生了很高的数字。 例如,它表明要使Struts摆脱债务,需要572个工作日和280,000美元,我认为这是过高的。 您一定要为项目调整这些数字,然后再带给经理。 如果您生成一份报告,说要花费120万美元将代码传递到不会损坏的地步,您的经理将跳出一个窗口。 调整数字以支持您的情况,您需要投入一些专职资源来清理债务。
Sonar还具有其他一些不错的罐头可视化效果。 考虑图4所示的“ Time Machine”图:
该图显示了随时间变化的三个关键指标:代码覆盖率,圈复杂度和每种方法的圈复杂度。 从这张图表中可以看出,Struts在2009年9月1日前后发生了一件可怕的事情。它显然包含了另一个具有糟糕指标的框架,现在已反映在Struts代码库中。 将其与相同的可视化效果进行对比,但对于Spring Batch,如图5所示:
图5中的图表显示了您想要看到的更多内容:相对恒定的覆盖范围,恒定的每种方法的复杂性以及随着软件支持其他功能而逐渐增加的总体复杂性。
技术债务隐喻之所以如此有效的原因之一,在于软件项目中货币债务与时间的相似性。 当您有债务并且收到更多的钱时,其中一些钱必须偿还债务的利息。 在软件项目中,您要花费时间而不是金钱。 当您有新的时间来添加新功能时,您必须通过解决所有设计(和其他)折衷方法所花费的额外时间来偿还一部分时间。 值得全力以赴来减少债务,因为这样一来,所有人都可以在解决问题后加快工作速度。 修复技术债务可以提高整个团队的速度。
在本期中,我开始研究对紧急设计有影响的非技术因素。 我讨论了如何估算不可知因素,以及如何说明技术债务。 在下一部分中,我将继续围绕紧急设计进行外部关注,包括重构和隔离更改。
翻译自: https://www.ibm.com/developerworks/java/library/j-eaed17/index.html