演化架构和紧急设计

设计环境因素,第 1 部分

评估紧急设计并演示技术债务

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: 演化架构和紧急设计

敬请期待该系列的后续内容。

此内容是该系列的一部分:演化架构和紧急设计

敬请期待该系列的后续内容。

正如一些读者所知,本系列与我出席会议所作的同名演讲是共生的。 会议的问答部分提出的一个共同的主题就是,将敏捷设计融入到一个还不太具备敏捷性的生态系统中。大量关于这一主题的问题使我意识到了它的重要性。

本系列之前的文章已经介绍了紧急设计的技术因素,但是软件设计并不是凭空出现的。非技术因素也有很大的影响。关于紧急设计最常见的一个问题是 “您如何对其进行评估?” 紧急设计最常见的一个混淆器(obfuscators)是技术债务 — 由于外力原因强加于您设计的折中方案,例如,时间压力。这一期将讨论如何评估紧急设计,以及如何制定一个令人信服的偿还技术债务方案。

紧急设计评估

Martin Fowler 在联合主题方面提出了一个很好的类比,这是我和他在今年年初在讨论关于为什么运行敏捷实践(agile practices)时得到的。先停下来,考虑一下水果蛋糕和酒店淋浴。Fowler 似乎有一个极好的祖传水果蛋糕配方,他已经烤过很多次了,而且如果他使用比例正确的配料,在特定温度下,且设定时间,那么每次都会得到同样的水果蛋糕。

根据酒店淋浴选择适当温度的经验,如果您在家使用时也进行同样的设置,那么就是非常愚蠢且危险的。相反,您会先打开,然后一点点的试水温,直至变暖。这里使用的是调节、测量、重复直至正确这样一个快速反馈循环。

这两类过程的区别阐明了规定项目和反馈驱动项目之间巨大的差异。如果一道菜您之前已经做过很多次了,您可以很自信的照着菜谱做。类似地,如果您符合以下标准,评估一个软件工程也是很容易的:

  • 您正在构建与您之前所编写的(不止一次)完全相同 的软件块。
  • 与过去配置的团队完全一样。
  • 环境完全一样。

编写新软件不是一个 确定过程;它具有高可适应性,这意味着一个基于反馈的方法比规定方法会工作得更好。我又绕回原来的问题了,重申:“您如何在一个高度自适应系统中评估设计工作?”

粗粒度评估和回旋空间

我的雇主(ThoughtWorks)用几种方法减轻了软件工程高度可变的本质。首先,我们告诉客户我们提供的第一次评估将不再精确,软件的精确评估是不可能的,因为在您对项目了解最少的时候,您可能被要求提供最细微的项目细节。我们预先告诉客户我们的第一次评估是基于对粗粒度业务需求和技术架构的理解。(关于该流程的更多细节,见 Iteration 0。)

粗粒度评估反映了对范围和成果的原始理解。然而,我们不能长期依赖评估,因为只要开始运行,我们的理解就会逐渐加深。由于敏捷开发会在短期迭代中出现,它允许项目管理员收集一个项目的所有细节的真实数据:在这个 环境下,这个 项目中,针对这个 问题领域,这个 团队的真实速度。3 次或 4 次迭代之后,项目管理员调整负载系数(用于将复杂度转换成时间的数字)来反映这个 项目的真实情况。这是在一个高度可调整环境下您所能做的最好的:进行有根据的推测,然后立即应用真实数据来测量(和反馈)您完成的方法。

因为最初评估是粗粒度的,还有很大的回旋空间,可以处理紧急设计、代码重构、以及其他技术清洁活动。正如我们不为其他常见实践(例如,会议)预留时间,ThoughtWorks 项目不为这些实践特别留出时间,除非他们陷入了一个常见的重型设计应用程序。

分期偿还项目评估的设计时间的另一个 方案是在您的时间表中创建设计检验点,这对那些远离 big-design-up-front 理论 — 为设计预留特定时间 — 的企业是非常有吸引力的。如果已经预料到这个时间,那么就使用同样多的时间,只是不要提前于这个时间。相反,在您的时间表中的一些合理里程碑(比如发布或少量迭代)上放入一些标记来再次访问现有设计决策,进行下一轮微设计。等待设计决策直至最后责任时刻,您对决策的实际影响的背景有一个更好的了解。如果您预留一定的前置设计时间,将它散布到整个项目,您将会发现您有大把的时间制定设计决策。

软件项目通常需要花费时间处理的另一个任务是偿还技术债务。接下来,我将讨论一些工具和技术,可以帮助您为非技术同事揭露该问题。

偿还技术债务

在该系列的 第 1 期,我曾经介绍过技术债务 — 一个很棒的隐喻,Ward Cunningham 提出的(见 参考资料)。技术债务类似于信用卡债务:您目前没有足够的资金,可以预支未来。类似地,您的项目现在没有足够的时间处理一些事情,那么您可以采用即时(just-in-time)解决方案,使用未来的时间来改进它。

Martin Fowler 已经编写了技术债务的 4 个象限(见 参考资料),如图1 所示:

图 1. 技术债务象限
图表显示 Martin Fowler 定义的技术债务的 4 个象限

第一象限(轻率、故意)的发生是有意的。经典动画片中说明了这一点,管理员对开发人员说,“您们忙,我上楼看看他们想要什么。” 公司有很多此类债务,他们很着急,愿意用交付换取逐渐降低的速度。这显然不是长久之计,而且对于大型代码也是行不通的。第二象限(慎重、故意)是最常见的技术债务表现。它也是后来最有可能导致大问题的,一旦您意识到您正处于技术债务中,就马上偿还。第三象限(轻率、无意)是最麻烦的,因为开发人员欠债了,但是他们并没有意识到。在此类项目中,一个常见的开发人员声明可是 “在 JSP 中正好嵌入 5000 行代码的确很方便,这样您只需要向上或向下滚动来查看您定义的所有变量(全局)在什么地方。”

第四象限(谨慎、无意)似乎是不存在的 — 如果您是谨慎的、您有怎么可能疏忽呢?事实上,这对于那些拥有专业设计师的项目是一个普遍现象。即使最优秀的软件设计师 也不能预料到随着时间的推移设计将显示和演化的所有方法。最后这个象限是现实的反映,软件中最困难的一个问题是我们不知道我们不知道什么,换句话说,最难处理的是那些我们甚至不知道有问题存在的事情。

技术债务是软件领域的一个现实,永远不会消失,所以时间表压力总是存在,反映这样一个现实,业务决策的制定速度比我们将它们编码到软件的速度更快。技术债务并不是永远都是坏的,正如显示债务并不是永远都是坏的一样。正如有远见的公司战略性地借债一样 ,在软件中同样可以这样做。问题不是债务,而是磋商债务偿还。我的经验是演示胜过讨论。如果您找到您的项目经理说 “我感觉很不舒服,夜里无法安睡;我怀疑我们的代码开始吸入。” 项目经理会马上问 “您能证明吗?” 您可以。接着,我将向您展示技术债务的两个插图:第一个是手工生成的,第二个是使用 Sonar 工具生成的。

生成技术债务图

第一个插图来自一个真实的 ThoughtWorks 项目,一个面向公众的媒体网站 ,他的技术主管 Erik Dörnenburg(见 参考资料)在软件度量和可视化方面很著名。在这个项目中,他创建了一个图来显示不断增长的技术债务,如图 2 所示:

图 2. 技术债务图
技术债务图
技术债务图

图表的横坐标显示时间(2006 年 4 月 1 日到 2007 年 8 月 18 日),纵坐标是每行代码的圈复杂度。(本系列的 这一期 在讨论测试时介绍了圈复杂度度量。)底部的灰色数字是项目版本。

第一个公开 “正式上线” 的代码基版本是版本 3,在这个插图上,您可以看到复杂度的数量和波动不断上升,直至那一刻 — 业务人员几乎站到了开发人员的肩膀上了,不断地问 “还没有完成吗?” 第一个版本上线之前他们没有赚钱,所以很显然他们很担心。

当项目到了发行日期后,Dörnenburg 创建了这个图形的第一部分,并将它作为挂载技术债务的证据,然后用它来协商两个短期维护版本(版本 3.0 到 3.1)清理一些技术债务。努力的惯性将所有方法带到版本 7,在版本 7 中债务因为一些不相关的原因开始再一次增多(尽管有很多控制方法)。

使用 Sonar 说明债务

图 2 中的插图给人影响深刻,但是它需要一些工作来引导程序。自主开发的另一种选择是开源工具 Sonar(见 参考资料)。Sonar 为您的代码基捕获许多度量,然后将它们在插图和其他可视化组件中显示出来。Sonar 的创造者将它运行于各种开源项目(通过在 http://nemo.sonarsource.org/ 运行 Sonar 实例可以获得),包括 Struts。图 3 显示了 Struts 的 Sonar 结果:

图 3. Sonar 显示关于 Struts 的细节
Sonar 显示关于 Struts 的细节
Sonar 显示关于 Struts 的细节

Sonar 显示在 Java 空间常见的质量工具的输出,包括 CheckStyle(见 参考资料)、代码覆盖、以及一个它自己的度量工具 Technical Debt Calculator(在右手边显示)。该公式使用源自(在项目代码中运行的)度量的一大串数字。

Sonar 似乎生成了非常多的数字,例如,建议花费 572 个工作日、280,000 美元来使 Sonar 偿清债务,我认为这是过度夸张。在您发送到管理员之前,您的确需要根据您的项目调整这些数字。如果您生成的报告说您将花费 120 万来使您的代码达到不可吸入的点,您的管理员可能会从窗户跳出去。调整数字来支持您的案例,您需要投入一些全时资源(full-time resources)来清理债务。

Sonar 也有一些比较好的封装的可视化。考虑图 4 中的 “Time Machine” 图:

图4. Struts 中的 Sonar Time Machine 插图
Struts 时间机制图
Struts 时间机制图

该图显示了过去的 3 个关键度量:代码覆盖、圈复杂度和每个方法的圈复杂度。正如您从图表中所看到的,2009 年 9 月 1 日在 Struts 发生了一些很可怕的事情。显然包括另外一个有糟糕度量的框架,现在在 Struts 代码库中反映出来了。将这与同一个 可视化(除了 Spring Batch )相比较,如图 5 所示:

图 5. Spring Batch 的 Time Machine 插图
Spring Batch 的时间机制插图
Spring Batch 的时间机制插图

图 5 中的插图显示了更多您想要看到的:相对恒定覆盖、每个方法的恒定复杂度、以及当软件支持附加特性时整体复杂度的缓慢增长。

技术债务隐喻如此好用的一个原因是,经融债务的钱和软件项目的时间有相似之处。如果您有欠债,当您收到一些钱时,一部分钱必须用来还债。在软件项目中,您处理的是时间而不是金钱。当您有了一些时间来添加新特性时,您必须通过外部时间补偿一些设计(或其他)中花费的时间,进行折中。全力减少债务是很值得的,因为当它解决了之后,每个人都可以更快速。修复技术债务可以提高整个团队的速度。

结束语

在本期中,我开始介绍了非技术因素对紧急设计的影响。讨论了如何评估不可知因素,如何说明技术债务。在下一篇中,我将继续讨论紧急设计外部因素,包括重构和隔离修改。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology
ArticleID=625947
ArticleTitle=演化架构和紧急设计: 设计环境因素,第 1 部分
publish-date=02092011