您已经阅读了有关敏捷软件开发的炒作。 您已经说服了老板,采用该业务将使企业受益,并且应该请一些专家顾问来提供帮助。 您的团队已经拥有频繁发布,迭代开发和日常维护的优势。 一切都很好。
但是随着团队的成长和系统的成熟,您开始遇到问题。 对于集体所有权,代码库太大。 人们会忘记细节,并且不存在任何文档。 开发人员抱怨构建时间太长。 有些人甚至在构建完成之前检入代码,有时会破坏代码。 您开始认为,您雇用的昂贵的敏捷顾问是扶手椅上的将军,他们不知道自己是一名步兵。
这个故事太普遍了。 可悲的是,一些团队然后返回瀑布方法。 有些变成了“水手”。 但是,少数人仍然保持高度敏捷和繁荣。 我有幸在一家大型电信公司的一个这样的部门担任开发人员。 在敏捷顾问离开之后很长的时间里,他们已经以敏捷的方式交付软件。 本文介绍了他们如何做到的。 我将为您提供工作环境,所使用的流程以及所遇到的挑战的图片-到目前为止都尚未满足。 我无法提供敏捷的蓝图(没有人可以提供),但是我希望从我的经验中学到的经验可以帮助其他组织在战斗中真正成为敏捷。
数据库管理员,系统管理员,数据仓库管理员和网络管理员的团队对于电信公司的系统操作都是必不可少的。 但是当涉及到敏捷时,一切都是(引用史蒂夫·巴尔默的话)开发人员,开发人员,开发人员。
所有50名左右的开发人员都位于一个楼层,并在大约10个6人以下的团队中工作。 在这样规模的团队中,沟通的机会很小,每天的站起来不超过15分钟。 每个团队都负责编写和管理一些针对离散业务领域的小型应用程序。 除了在与其他团队的接口上达成一致之外,每个团队都是一个自治部门,可以决定自己的工作方式。
尽管正在开发的应用程序具有清晰的体系结构,但我们不使用任何架构师。 所有架构决策均由在代码方面工作的开发人员之间达成共识而制定。 但是,每个团队都有一名业务分析师。 他或她充当可能不在同一城市的客户的代理人。 业务分析师将每次迭代的需求提供给开发人员(请注意不要过度使用)。 开发人员所看到的人与可以形容为经理的人最接近。 除了业务分析师之外,每个团队都有自己专用的用户接受度测试仪。 测试人员和分析人员都参加开发人员会议。
这种由小型团队构建小型应用程序,同时就跨领域问题进行协作的配置,解决了早期关于敏捷开发的经常听到的抱怨之一:它无法扩展。 否则,它既有优点也有缺点。
一个优点是,如果任何应用程序过时或出现问题,都可以以相对较少的费用将其丢弃。 对于如此小的应用程序,重写代码不一定是一件坏事。 另一个优势是,这些应用程序不太可能遭受功能蠕变的困扰,因为它们无需尝试将所有内容都提供给所有人。 我们许多应用程序的用户范围从服务台操作员到该领域的高技能工程师。 显然,一个应用程序无法满足这些不同用户的需求。
但是,许多应用程序意味着系统管理员要监视的JVM比单片应用程序期望的更多。 最初,他们不愿意承担由此带来的额外工作量。 但是,当开发人员承诺向所有应用程序添加Java Management Extensions(JMX)监视并编写一个收集运行状况数据的工具时,系统管理员同意了。
除了开发人员站立训练外,我们还会举行每个开发团队的代表参加的日常站立训练。 此处讨论了诸如证书和许可证到期之类的跨领域问题。
所有应用程序的体系结构都反映了团队的组成。 所有的应用程序都通过XML相互通信。 传输协议有时是Java™消息服务(JMS),但更常见的是HTTP。 与自治团队保持一致,Web服务器的选择是无关紧要的,到目前为止,已经避免使用大型企业服务总线解决方案。 只要遵循一些准则,这种方法就可以很好地工作。
第一条准则是通过将版本控制包含在URL或XML有效负载中,将版本控制应用程序之间发送的消息。 这样,如果服务器应用程序检测到已给其提供了不完整的数据,则可能会Swift失败。
另一个是为客户团队提供XML有效负载的XML模式(XSD)。 这种简单的手势令人惊讶地引起了客户团队的感谢,客户团队随后对他们的代码要求感到更加自在。
第三是使用免费和开源的Yatspec来作为在测试套件中运行的文档(请参阅本文结尾处的参考资料)。 如果文档过时,则构建将中断直到对其进行更新。 Yatspec产生的文档使用人类可读的网页来描述XML,该XML在系统中的各种路径之间来回传递。 在每次迭代结束时,都可以将其提供给客户端。 它比Web服务描述语言(WSDL)文件更具可读性。 我将在以后再讨论Yatspec。
这么小的自组织团队遵循明智的准则,可能会出错吗? 很多,实际上。
无自我编程只能在无自我存在的世界中真正实现。 无论世界在哪里,都不是地球。 尽管条件可以最大程度地减少强者之间的摩擦,但是当足够多的人进行交互时,不可避免地会发生崩溃。 我看到的一个例子是,有一半的团队正在将Spring添加到代码库中,而另一个正在忙于删除它。 在这种情况下,管理必须强大,并通过必要的手段打破僵局。 有时,别无选择,只能将人们从一个遭受内战的团队中撤出。
做出自己的体系结构决策的团队几乎没有空间来做prima donnas。 但是,一个团队做出的架构决策可能会影响其他团队。
例如,一个团队决定使用NoSQL内存数据库。 后来,他们决定实际上数据必须在崩溃后幸免。 因此,他们要求从中获取数据的团队为其存储数据。 对于提供数据的团队来说,这是一个次优的想法,而该团队并未计划这项工作。 他们认为跨多个应用程序的业务问题在架构上是令人讨厌的(并且使用数据的团队沉迷于“职业驱动开发”中)。
当出现这种情况时,团队将向仲裁员求助。 仲裁员是不参与任何团队的开发人员; 他或她不一定是场上最好的开发商,而是通常被认为具有良好的人际交往能力和公正听取辩论双方意见的能力的人。
不幸的是,我们目前并未充分利用小型应用程序的联合环境。 在发行之夜,一个无法运行的应用程序导致所有应用程序的发行版回滚。
这种必要性不仅仅是由于版本之间的接口差异而引起的。 (由于系统之间发送的消息中的版本号,可以为这种情况准备一个应用程序。)相反,这是由于消息可能消失的缘故。
考虑一个成功部署的应用程序启动并将消息传递给另一个新部署的应用程序的情况。 然后,第一个应用程序认为完成的工作。 然后,注意到使用该消息的第二个应用程序有故障,因此其部署和数据库均被回滚。 第一个应用程序不了解其协作者的回滚,因此对尝试重新发送其消息没有兴趣。
解决此问题的第一种方法是将应用程序分为不同的系列。 希望这至少意味着如果发生问题,仅一部分应用程序将被回滚。 例如,似乎客户订购(客户首先要连接到我们的网络),供应(客户的线路需要在电话交换机处物理连接)和服务保证(检查客户的连通性)的工作流程是完全独立的域。 但是后来变得很清楚,一个支持工程师想知道为什么新客户的电话线无法正常工作跨越了所有领域。 因此,根据需要,这些应用程序需要相互通信。
这种表面分析的另一个问题是,即使是同一个家族中的应用程序也不一定会相互通信。 尽管从业务角度看,这些家族似乎很明显,但他们未能捕获应用程序之间的依赖关系。 例如,在检查座机和检查来自无线热点的连接时没有通用的工作流程。 尽管属于假定保证系列的一部分,但处理此流程的两个应用程序却彼此不对话,也没有共享公共依赖项。
las,尽管我们正在游说购买更多设备,以便我们可以在类似生产的环境中进行全面测试,但我们尚未设法解决所有或全部发布的问题。 在我们解决此问题之前,必须部署所有应用程序或全部回滚。 单个无法部署的应用程序将危及整个发行版-最终成本非常高昂。
尽管在应用程序较小的情况下可以减轻它们的痛苦,但并不是所有的重写过程都是无痛的。 某些跨领域的关注不断被重写。 一个示例是影响所有应用程序的身份验证系统。 缺乏长期规划导致它不得不进行几次重写,从而影响了几乎所有应用程序。 不幸的是,我们还没有解决这个问题。
即使两个团队就某个方法达成共识,也仍然有可能犯下架构错误。
一个恰当的例子是,一个工作量不足的团队同意承担工作过度的团队的通用功能。 这个想法是让两个团队都在共享库上工作。 但这很快导致一个团队在固定到自己的构建监视器时无意中破坏了另一个团队的构建。 另一个团队的建物是他们不愿探索的陌生土地。
回想起来,第一个团队应该向第二个团队提供某种XML / RPC服务。 由于第一团队工作过度,他们本可以从第二团队中选拔成员。 六个月后,这种痛苦的经历仍在继续。
整个工作阶段持续两个星期。 在具有奇数的迭代中,应用程序会在生产前提升到环境中,而不是提升到生产本身中。 这使系统管理员和数据库管理员可以进行虚拟运行。 在具有偶数的迭代中,应用程序将上线。 所有应用程序均在同一晚发布,确保它们可以一起工作。
问题在于,部门外部的团队和系统没有遵循相同的周期,或者甚至根本没有使用迭代版的敏捷。 他们可能属于不同的部门,甚至属于不同的公司。
由于我们无法控制客户,因此我们只能做些事情。 但是我们发现,为每个面向客户端的应用程序都为客户端提供“入门”应用程序有助于他们的采用。 这些入门应用程序对与我们面向客户的应用程序进行交谈的后端系统进行了存根。 这些入门文章有一个简单的Web前端,使我们的客户能够以他们想要测试的任何配置来设置系统。
由于这些底漆的发布与生产发布保持同步,因此在每次增量构建的测试环境中也都可以使用它们。 这样,我们的客户就可以在迭代过程中继续使用它们。
如果每个团队都独立完成各自的任务,那么一些工作就更容易陷入困境。 例如,一个应用程序在很大程度上依赖于Apache ActiveMQ,但是负责它的人却一直是争论的源头。 开发人员将其视为基础架构。 系统管理员认为,开发人员更适合处理消息问题,因为他们更了解软件。 只有反复试验,再加上一点点交易,才达成了协议。
通过有时具有远见,但更常见的是通过反复试验,我们提出了一些最佳实践来帮助我们实现目标。 他们认识到代码只是成功迭代的一部分。 测试,构建,部署和文档都有自己的过程,必须随时间进行调整。 在这里,我将分享一些对我们有用的技术。 您的里程可能会有所不同,但是如果您采用并调整它们,则可以提高生产率。
要保持开发人员的注意力,快速构建至关重要。 快速构建还使他们在进行更改时勇敢无畏,因为他们可以非常快速地测试自己没有破坏任何东西。
如果应用程序较小,构建时间自然会减少。 但是减少构建时间的另一种方法是使构建成为多线程。 清单1显示了一部分Apache Maven项目对象模型(POM)文件,该文件使用Maven Surefire插件(请参阅本文末尾的参考资料)来运行多线程测试:
摘录摘自该应用程序的完整版本用了不到一分钟的时间。
使您的构建成为多线程时,应注意两条建议。 首先是,在一个成熟的项目上这样做通常是开箱即用的。 串行运行的测试在并行运行时可能会神秘地失败。 因此,最好从一开始就使构建成为多线程的。
第二,必须考虑如何运行测试。 例如,某些测试在启动之前先清理数据库是很常见的。 这些测试不适用于多线程,因为一个测试可能会删除数据,而另一个测试正在填充表。
对于我们的许多联邦应用程序而言,一个相互交叉的关注点是它们是否已发布到各种环境中。 为此,我们编写并开源了一个名为Conduit的Ant库,该库有助于发布过程。 它具有有用的宏,例如,将工件scp到适当的环境并使用SSH远程启动它。
以统一的方式启动和停止应用程序的一个很好的副作用是,编写必须启动和停止整个应用程序集的集成测试要容易得多。 每个脚本都相同。 为您部署应用程序的库的另一个优点是,您可以每天多次释放到多种环境中。 您执行的次数越多,进行真正发布时出现惊喜的机会就越少。
但是要警告一句:管道仍然不成熟,错误消息可能相当深奥。
敏捷顾问经常建议避免使用文档,因为它很快就会过时。 但是,如果文档是构建的一部分,则情况并非如此。
我工作的部门使用Yatspec(请参阅本文末尾的参考资料),这是他们编写并开源的工具。 它运行用Java编写的测试,并将结果和源代码转换为HTML文档。 输出类似于图1:
生成图1中的输出的测试方法看起来像清单2中的代码一样简单:
清单2中的每个方法都进行断言并突出显示特别有趣的数据。
Yatspec甚至可以自动生成序列图,如图2所示:
捕获序列图中的动作仅是将测试代码侦听器注入生产代码中即可。
最终,源代码是开发人员的文档。 迟早另一个团队将不得不查看您的团队。 我发现,如果在每个构建版本中发布源代码,您都会对相邻的开发人员表赞不绝口。
只需使用标准插件即可在Maven中轻松做到这一点(请参阅本文末尾的参考资料)。 清单3显示了如何使用Maven源插件:
想象一下这种情况:在发布之夜的凌晨3.30,您的应用程序无法启动。 您想知道为什么,因为您花了一个月的大部分时间来编写测试。 您很累,想回家,而您的同事们正以越来越烦恼的眼神看着您。 您仔细检查了代码,但似乎没有错。
很容易忘记,配置和代码一样,已成为应用程序的一部分。
很容易忘记,配置和代码一样,已成为应用程序的一部分。 例如,我们的一个应用程序发布了三个完整版本,而没有一个代码错误。 但是,它受到配置问题的困扰,使发布之夜成为令人难忘的体验。
为了避免这种不愉快,我们编写了一个类,将对所有属性进行完整性检查。 这个简单的JUnit测试检查所有环境的属性文件中的所有值。 它可能会对值执行逻辑检查,可能只是检查它是否与正则表达式匹配。 或者,它可能只是检查以确保该值确实存在。 这种测试无法阻止深夜恐慌的发生。
敏捷方法论认识到项目永无止境。 这就是软件开发在现实世界中发展的方式。 因此,即使经过了近十年的敏捷开发,新的问题(例如粗粒度的发布过程和跨领域关注的协议)仍然困扰着我们。
但是作为开发人员已经工作了16年,我相信我们的状况比大多数其他公司都要好。 发布很少回滚。 生产问题往往很小。 团队合作精神很高。
敏捷的方法论将极大地改善您的软件交付,但是您必须对实现的目标和实现的速度要保持现实。 任何一位告诉您敏捷解决了流程中所有问题的顾问,都可能试图向您出售产品。 用温斯顿·丘吉尔(Winston Churchill)的话来说,敏捷是最差的方法,除了其他方法之外。
翻译自: https://www.ibm.com/developerworks/library/a-agiletrenches/index.html
相关资源:2019高教社杯全国大学生数学建模竞赛评阅要点C题.pdf