下载本文的PDF版本 PDF

分布式开发经验教训
MICHAEL TURNLUND,思科系统

为什么要重蹈覆辙,如果可以避免的话?

交付一个基于技术的项目是具有挑战性的,即使在良好控制、熟悉的情况下也是如此。而一个紧密的团队可能是成功的关键因素。因此,大多数小型、新兴的技术团队选择在车库(有时甚至是字面意义上的车库)工作也就不足为奇了。将每个人的精力集中在手头的开发任务上,意味着最大限度地减少非工程管理开销。

开发和交付一项技术是团队成员的核心问题集。这是工程师们训练有素、技术娴熟且感兴趣的领域。当与团队每天密切合作时,小型、封闭团队内部的不同技能水平很容易适应和优化。

不幸的是,封闭模型的完整性很快就会瓦解。根据团队和动机的不同,单个团队的临界点很少超过15名开发人员,通常更少。而且,每周都有新闻头条赞扬将一半的开发工作转移到另一个大陆的美德,团队规模越来越仅仅是人们没有都在同一个车库工作的原因之一。

无论一个项目是在同一栋大楼的不同楼层的两个团队之间,还是在地球两端的两个团队之间分配,这种分布式代码组装都引入了一个新的复杂层面:现在有两个团队在同一个代码库上工作。这种共享的数据或源代码库需要更协调的管理风格。发布的协调变得更加复杂。测试变得更加复杂。诸如设计标准、编码标准、风格指南、定义的生命周期流程、清晰的文档化规范和完善的需求文档等小事都成为保持开发成为可能的共同价值观的核心。当团队有不同的前沿和不同的中心时,保持这种共享的价值观成为中心就变得更加困难。

在本文中,我们将探讨分布式开发的一些难点。我们将探讨在以下四个领域需要注意的陷阱:工作组封闭性、组件化、开发环境和验证。多年来,我们已经吸取了很多教训——如果你必须在分布式环境中开发,就没有必要盲目地跳进去。

那些忘记历史的人...

我所知道的关于分布式开发环境的第一个轶事是美国海军“ Monitor”号的案例。回顾一百多年前分布式开发是如何完成的,这将是有启发意义的——只要不断地对自己重复,“当船只撞毁时,它们会沉没;当软件崩溃时,至少我们可以重启。”在美国内战初期,北方军事战略家意识到,在听说南方开发了一艘铁甲舰“CSS Virginia”号后,他们迫切需要自己的铁甲海军军舰。该舰的设计师约翰·埃里克森赢得了项目竞标,条件是他必须在100天内交付——他意识到自己无法在一个铸造厂中快速完成这项工作。为了加快工作进度,他将船只的不同部件分包给美国东北部的九个不同的铸造厂。1

这个项目很复杂——“ Monitor”号在下水时就拥有47项不同的可专利发明。(你认为你有集成问题吗?)当部件从铸造厂运到造船厂时,它们组装起来很差,必须进行大量的改造。这影响了进度和质量标准。因此,最终进行了仓促的最终集成工作和几次试运行改造。尽管该舰在1862年与更大但机动性较差的铁甲舰“CSS Virginia”号的首次战斗中取得了成功(双方都宣布胜利——木制军舰成为过去),但“ Monitor”号在几个月后的新年前夜在公海拖曳时沉没。

用今天的术语来说,他们经历了新的设计、模糊的规范、艰难的系统集成、压缩的发布时间、不稳定的beta版和某种程度上成功的首次部署。回顾过去,开发和生产任务的协调,或者说缺乏协调,是最初努力中吸取的重要教训。这一切都归结为在构建过程之前和期间沟通明确的需求。规范松散,承包商店的能力被高估,材料短缺和技术问题普遍存在。假设的太多,而定义的太少。就当时而言,该舰的技术绝对是革命性的。随后的船只,而且有很多,不仅融入了造船技术的许多经验,也融入了分布式开发中遇到的问题。

除了战时考虑的压力外,现在解决的分布式开发问题远比这些水平复杂得多。即使是中型组织也通常有两个或多个物理站点进行开发,至少有一个站点与时区相差三个以上,而且很可能具有不同的组织规范。更不用说软件本身的复杂性了。规模和接口数量的问题本身就令人难以承受。然后还有社会政治方面的考虑。工程师们希望专注于技术实施,而不是外交。他们对解决这些非常现实的问题不感兴趣。凌晨2点,计算机科学实验室里没有有抱负的外交官。(有关社会方面的更多信息,请参阅本期第52页Olson和Olson的“远程软件开发团队中的文化惊喜”。)

第一课:工作组封闭性

在地理基础上将团队限制在离散的任务中,是最大限度减少沟通开销的最有效方法。即使对于位于同一地点的团队,这也已被广泛接受,但功能跨团队的溢出所带来的代价更容易在没有地理挑战的情况下得到缓解。

如果团队A中的每个人都根据一组API(应用程序编程接口)进行设计和编码,并且该团队有一项封闭的工作(子系统gamma,它控制温度监控,文件foo{1}到bar{30},定义的控制台接口,OS,控制子系统,每个子路径和子例程的测试挂钩,功能规范,代码提交标准,系统架构文档),生活就相当简单。如果团队B有一个客观的规范来编写,那么就无需猜测他们想要与团队A的代码的哪个部分以及多远的部分联系起来以用于他们的下游应用程序。定期与团队负责人联系以检查进度并获得问题的答案将是大部分互动。当然,当这些部分进入集成和系统测试时,将需要更多的互动。

如果团队位于同一地点,则协同工作可以进行即时沟通。团队内部的沟通与单个站点上的单个团队的沟通没有太大不同。团队中的人员了解与他们一起工作的人,并且可以依靠非常非正式的沟通渠道来协调任务或寻求解决问题的支持。

在广阔的地理区域混合单个团队有点棘手。根据我的经验,如果需要大量指导和指导的个人没有与至少他们的主管和一到两名高级技术贡献者位于同一地点,通常会遇到性能下降。复杂的学习本身就足够困难了,更不用说电子邮件的脱节沟通以及与地理位置相关的时间滞后了。而且,如果没有首要的开发人员工具——白板,就很难真正理解项目。

一些高级、训练有素的个人实际上可以在远离团队其他成员的环境中茁壮成长。其中的一个因素可能是,这些高级开发人员中的一些人有能力成为出色的子项目负责人——但如果没有管理团队的愿望或倾向,一些顶尖高手最好独自工作。然而,邻近问题总是会使沟通不够紧密,并且通过电子邮件或电话进行沟通的开销仍然是一个问题。当然,人们需要警惕我喜欢称之为“你的第19次神经衰弱综合症”的情况,即一位高智商的程序员试图拥有太多的代码。一个真正杰出的人的极限突破点可能在一百万行代码左右,但对于大多数顶尖高手来说,这个数字会远远低于这个数字。我认为这种综合症的名称应该充分突出潜在的危险。

无论团队是位于同一地点还是相距遥远,在关键集成点和测试标准上的前期努力都将防止未来发生地盘之争。“我们一直使用Red Mountain的调试器”是完全有效的,只要整个团队都在使用它。

如果有来自先前开发的系统部分的可用代码(或库)可供重用,它们可以为后续团队提供良好的非正式模板和测试工具。对于新合作的团队,了解最终规范和预期结果对每个团队都有好处。在他们一起完成多个项目并了解他们想要在哪里简化开发流程之后,期望每个团队都有相同的流程和编码风格是可以的。在两个团队相互理解对方的术语和偏好之前,将花费大量时间来弄清楚这些事情。不考虑这一点的进度安排将会遇到麻烦。

第二课:组件化

在分布式环境中组件化代码的关键是关注第一条规则:需要离散地容纳的移动部件越多,系统内部接口的正式定义越少,引入系统开发的复杂性和风险就越大。在表面层面,将系统的每个部分都推送到其自身的细粒度、自包含的实体中是很吸引人的。对于单个物理开发地点,一个小组可以在此模型内执行。从组合方面(地理位置、互连数量、执行的可变性)来看,这种“信任每个人都理解整个系统”的方法将成为一场灾难。主要子系统之间的API需要保护(检查工具、文档等),以保护任何给定子系统内的组件所需的自由,以便运行有效的并行开发程序。如果没有这种摆脱许多相对复杂的相互关系的自由,集成练习将成为开发练习中复杂、充满返工、漫长、不确定的主要部分。

当在组件化框架中开发时,无论是在单个开发站点还是在一个远程开发站点,协调系数都会降低。由于相互依赖的沟通的快速性,这可以允许相互关联的子集的数量增加。但是,如果系统具有比规定的更多的子集,并且有多个远程组,“城邦”问题就开始抬头。这里粗略的类比是政府的城市/县/州/国家层级结构。在一个有很多人参与的大型系统中,扁平的层级结构会崩溃,变成一个只有一个大国和许多城市(或城邦)的大混乱局面。所有地方问题都变成全球问题。在“国家”和“城市”之间添加“州”层允许层级结构更有效地处理地方和全球问题,将它们控制在适当的关注级别。

组件化通过解决这个层级结构问题来帮助最大限度地减少互连的数量。更扁平的结构很快导致几乎无限的互连、无限的细微差别和无限的沟通需求,这反过来又导致系统级聚合的噩梦。而对于多个团队来说,这意味着太多人必须学习太多关于太多代码的细节。因此,显然,逻辑上的地理分组组件控制在这里是有意义的。

第三课:开发环境

同构的开发环境使开发过程更容易跟踪,并消除再现性问题中的细微错误。在读者发出明显的“duh”之前,与shell变体、不同的驱动程序版本、关于支持与不支持的硬件的争论以及其他考虑因素相关的令人沮丧的经历已经导致了巨大的头痛,这些头痛在分布式开发环境中只会加剧。在前期的少量标准化努力本可以为我节省大量麻烦。

一般来说,团队成员越成熟,在其环境中发现的差异就越大。一位主管渴望去附近的电子产品超市购买团队桌面电脑,并节省3%的成本,这可能会导致未来漫长的侦查项目。简单地假设一切都会协同工作(在没有部署标准的情况下)意味着以后会浪费时间进行回溯。远程团队的最佳过程监控器之一是他们的工作是否会与其他人的工作一起构建。他们将通过工具链快速发现这一点。优秀的领导者最大限度地减少变量;他们控制可能出现的问题。

开源工具。 用于分布式开发的开发工具是我们都熟知的通用工具。对于基于Unix的开发,诸如GCC(GNU编译器集合)、GNU make和GDB(GNU项目调试器)之类的开源工具通常是很好的选择。让一个团队拥有工具的版本和维护权是保持每个人都在同一环境中的好方法。源代码管理工具可以像CVS(并发版本系统)一样简单,但团队仍然需要建立开发协议和实践,以及跨站点的源代码复制方法。

与在单一供应商环境中相比,版本选择在开源社区中是一个更有趣的练习。由于有供应商基本上通过为主流开源工具和工具包提供支持服务来模仿单一供应商专有工具集,因此这种难题在某种程度上得到了缓解。然而,保持接近前沿的难题需要更多的判断,并且需要特定的工具知识——最新的版本可能有一些你想要的好处,但许多人已经为使用尚未准备好投入使用的工具进行开发付出了代价。

单一供应商环境。 购买诸如Rational Rose、Green Hills、MontaVista或Wind River之类的现成开发环境,可以消除跨团队代码开发中的大部分差异。供应商提供的套件提取了实践中的差异,并为流程提供了共同的参考点。供应商提供良好实现的GUI、开发流程、测试方法、源代码管理方案等。缺点是团队在很长一段时间内都被锁定在这些工具和供应商中。此外,与公共领域工具相比,熟悉任何给定商业工具集的人员较少;因此,培训是必要的。

与开源工具相比,基于供应商的环境通常对定制(基于可用开发团队的资金)和持续的定制维护更加开放。如果价格合适,供应商更有可能被诱惑去追逐特定的版本或功能集。供应商拥有的客户可能比您的团队多——所有客户都需要一组功能、接口或IDE(集成开发环境)选项,这足以成为支持您的需求的令人信服的商业案例。

然而,供应商和开源项目都会消亡。经济状况会变化,趋势会变化,命运也会变化。实施你的开发环境,使其与一个工具或IDE过于紧密地联系在一起,意味着你将面临扼杀或不得不完全重做你的项目的风险。(有关分布式开发工具的更多信息,请参阅本期第40页Li-Te Cheng等人的“将协作构建到IDE中”。)

第四课:开发验证

开发验证是大多数开发人员都熟悉的另一个领域——但是当需要在分布式环境中进行验证时,还有一些额外的方法可能会让你感到痛苦。首先,一些规模较小、较年轻的公司采用耻辱墙方法进行代码验证——对在签入工作中发现的错误进行善意的公开羞辱。只要组织的价值观支持这种方法,这种方法就可以很好地工作。但是,请预先警告,这种方法在许多文化中并不适用。如果每个人都选择加入该系统,那么公开而直接地将某人钉死为白痴是一种很好的激励。如果两个团队需要协同工作,但其中一个团队不理解这个概念,则应采用更正式的验证方法。

验证中需要注意的另一个领域是第二课中讨论的组件化。组件化有助于验证周期,因为存在客观的调用、边界和时序可以针对它们进行验证,而无需系统的其余部分。我们再次看到了线性规划模型的问题;你越能简化和构建应用程序内部通信,你就越有可能避免因“城邦”而产生的陷阱。在分布式环境中,紧密定义的接口使得单元和子系统测试在相对自主的情况下可以完成。添加到等式中的远程维度使得开发的这些方面,以及最终的集成,不太容易受到不同的解释和潜在的返工。

用于此目的的工具包括验证套件或测试平台(例如,模拟或实际目标平台、代表性用例的设置等等)。桌面或共享测试和仿真环境(例如,具有目标嵌入式环境仿真的主机平台)在这里是理想的选择,因为可以立即获得反馈。如果地球两端的两个团队拥有相同且完整的测试环境,那么一个团队将不必等待另一个团队上班才能找出昨晚的更改是否破坏了任何东西。单个组件或子系统可以反复、定期地从整个系统和功能集中反弹,从而快速发现细微的问题。

结论

需求仍然是发明之母,分布式环境的扩展需要对多样性保持敏感。将广泛应用或系统开发所需的稀缺技能组合并在一起在经济上和后勤上都太困难了。资源最丰富的地区需求量很大。在具有所需技能组合的“偏远”地区的人们成为拥挤地区人们的可行替代方案,但是构建运营联系所需的工作量很大。

为了使精力集中在手头的开发任务上,你将需要克服工作组封闭性、组件化、开发环境和验证方面的障碍。分布式开发不仅在所有这些领域都增加了独特的困难,而且你自己的特定团队构成也将带来你自己的挑战。然而,我希望,在了解了这四个关键领域中的一些常见问题和经验教训之后,你将能够更好地为自己的分布式开发项目开发解决方案。

参考文献

1. 水手博物馆USS Monitor中心:请参阅 http://www.monitorcenter.org/

MICHAEL TURNLUND目前是思科系统的工程总监。他和他的团队致力于软件开发工具、操作系统优化和开发流程,作为思科互联网技术集团的一部分。在过去的19年中,他在思科、AMD和联合技术公司担任过各种角色,参与了许多大型和小型开发项目。他于1983年和1984年获得了加州大学圣巴巴拉分校的经济学学士和硕士学位。

 

acmqueue

最初发表于Queue vol. 1, no. 9
数字图书馆中评论本文





更多相关文章

Martin Kleppmann, Alastair R. Beresford, Boerge Svingen - 在线事件处理
对跨异构存储技术分布式事务的支持要么不存在,要么遭受较差的运营和性能特征。相比之下,OLEP越来越多地用于在此类设置中提供良好的性能和强大的 一致性保证。在数据系统中,日志通常用作内部实现细节。OLEP方法是不同的:它使用事件日志而不是事务作为数据管理的主要应用程序编程模型。传统数据库仍然被使用,但它们的写入来自日志,而不是直接来自应用程序。使用OLEP不仅仅是开发人员的实用主义,而是它提供了许多优势。


Andrew Leung, Andrew Spyker, Tim Bozarth - Titus:将容器引入Netflix云
我们相信我们的方法使Netflix能够快速采用容器并从中受益。虽然细节可能是Netflix特有的,但通过与现有基础设施集成并与合适的早期采用者合作来提供低摩擦容器采用的方法对于任何希望采用容器的组织来说都可能是一种成功的策略。


Marius Eriksen - 大规模函数式
现代服务器软件在开发和运营方面要求很高:它必须始终在所有位置可用;它必须在毫秒内回复用户请求;它必须快速响应容量需求;它必须处理大量数据和更多流量;它必须快速适应不断变化的产品需求;在许多情况下,它必须容纳一个大型工程组织,其众多工程师就像一个大型、混乱厨房里的谚语厨师。


Caitie McCaffrey - 分布式系统的验证
莱斯利·兰波特以其在分布式系统方面的开创性工作而闻名,他曾说过:“分布式系统是指即使你不知道存在的计算机发生故障也会导致你自己的计算机无法使用的系统。” 鉴于这种黯淡的前景和大量可能的故障,你甚至如何开始验证和确认你构建的分布式系统正在做正确的事情?





© 保留所有权利。

© . All rights reserved.