将模块化架构与开发团队联系起来

移动端案例研究

模块化架构可以提高软件交付吗?是的!- 但有一些注意事项。本文记录了一家企业将架构转变为更模块化的架构以缓解其成长烦恼的旅程。他们发现模块化是一个多方面的解决方案,它超越了架构,延伸到业务沟通线、团队拓扑结构和有效的开发者体验。通过密切关注这些因素,企业能够显著提高其移动应用程序的交付性能。

2023 年 6 月 13 日


Photo of Matthew Foster

马修是 Thoughtworks 的技术主管。在担任技术人员的 14 年中,马修一直致力于为零售、能源、电信和政府等大大小小的企业构建解决方案和领导团队。


本文将展示不同移动扩展问题、技术架构和团队之间的直接联系。在 Thoughtworks,我们与许多大型企业合作,他们在扩展其移动业务时,都面临着不同的问题和需求。我们确定了大型企业移动应用程序开发中常见的两个问题

  1. 将新功能引入市场应用程序所需时间的逐渐延长
  2. 由于内部市场应用程序之间缺乏兼容性/可重用性而导致的内部功能差异

本文记录了我们的一位客户在尝试解决这些问题时所经历的旅程。我们讲述了他们的组织在过去是如何倾向于正确的解决方案,但由于对这些解决方案的内在联系的误解而无法看到预期的效益。

我们通过回顾同一个组织是如何通过将他们的团队拓扑结构与模块化架构相匹配,同时投资于开发者体验,从而实现平均周期时间缩短 60%、开发成本提高 18 倍以及团队启动成本降低 80% 的。

识别迹象

尽管有最好的意愿,但软件往往会随着时间的推移而退化,无论是质量还是性能。功能需要更长的时间才能进入市场,服务中断变得更加严重,解决问题也需要更长的时间,结果是那些从事产品工作的人变得沮丧和失去信心。其中一些可以归因于代码及其维护。然而,将责任完全归咎于代码质量对于一个多方面的问题来说显得天真。退化往往会随着时间的推移而加剧,这是一种由产品决策、康威定律、技术债务和静止架构相互作用而产生的复杂现象。

此时,介绍本文所基于的组织似乎合乎逻辑。这是一家非常大的企业,他们一直在经历将新功能引入其零售移动应用程序所需时间的逐渐延长

作为开始,该组织正确地将他们遇到的摩擦归因于应用程序增长带来的复杂性增加 - 他们现有的开发团队难以添加与现有功能保持一致和一致的功能。他们对此的最初反应是“只需添加更多开发人员”;这对他们来说确实有效。然而,最终他们发现,添加更多人会以沟通更加紧张为代价,因为他们的技术领导者开始感受到协调开销的增加。因此,亚马逊推出了两个披萨团队规则:任何团队都应该小到足以用两个披萨喂饱。理论认为,通过限制团队规模,可以避免沟通管理花费的时间超过实际价值创造的时间。这是一个合理的理论,并且为亚马逊服务良好。然而,当考虑一个已经变得过大的现有团队时,人们往往会“货物崇拜”亚马逊的例子,试图减轻这种负担……

限制认知负荷

事实上,该组织也不例外:他们曾经的小型单体应用程序已经变得越来越成功,但随着功能、责任和团队成员的增加,它也无法复制所需的成功率。随着功能交付截止日期的临近,以及多个品牌市场即将出现的可能性,他们通过将现有团队拆分成多个较小的、相互连接的子小组来应对 - 每个团队都独立管理一个市场(尽管客户旅程相似)。

事实上,这使他们的情况变得更糟,因为它将沟通税从他们的技术领导层转移到了团队本身,而没有减轻他们不断扩大的上下文负载。意识到沟通和协调正在从那些负责实际价值创造的人那里占用越来越多的时间,我们的最初建议涉及到 Skelton & Pais (2019) 提出的“认知负荷限制”的概念。这涉及到跨越单个复杂或复杂领域的团队分离。软件内部的这些接缝可以用来形成上述“两个披萨大小的团队”。结果是每个团队的开销大大减少:动力上升,使命宣言更加清晰,而沟通和上下文切换缩减到一个共同的焦点。理论上,这对于我们客户的问题来说是一个很好的解决方案,但如果孤立地考虑,实际上可能会产生误导。只有当应用程序的领域边界真正定义明确,并在代码内部始终得到尊重时,认知负荷限制带来的好处才能真正实现。

领域驱动设计

领域驱动设计 (DDD) 对于将复杂逻辑组织成可管理的组以及为每个组定义通用语言或模型非常有用。然而,将应用程序分解成领域只是持续过程的一部分。保持对边界上下文的严格控制与定义领域本身一样重要。检查我们客户应用程序的代码,我们遇到了一个常见的陷阱,即最初明确投资于正确定义和组织领域责任,但随着应用程序的增长,这种纪律开始受到侵蚀。来自利益相关者的轶事证据表明,始终忙碌的团队为了满足紧急的产品需求而采取捷径已成为常态。这反过来又导致了价值交付速度的逐渐下降,因为技术债务的累积。这在应用程序的四个关键指标中得到了进一步的体现,因为发布代码变得更加困难,调试问题也变得更加困难。

通过常见的代码分析工具,我们发现了边界上下文管理不善的进一步警告信号。我们发现代码库已经变得紧密耦合,缺乏内聚性。高度耦合的代码难以更改,而不会影响系统中的其他部分。内聚性低的代码具有许多不符合其范围的责任和关注点,这使得难以理解其目的。随着我们客户应用程序中每个领域的复杂性不断增长,这两个问题都随着时间的推移而加剧。其他迹象再次提到了认知负荷。应用程序中领域之间边界或依赖关系不明确意味着,当对一个领域进行更改时,很可能会无意中影响其他领域。我们注意到,由于这个原因,开发团队需要了解多个领域才能解决任何可能出现的问题,从而增加了认知负荷。对于该组织来说,实施对每个领域边界上下文的严格控制是确保知识和责任在同一位置的一项进步。这导致了任何更改的“爆炸半径”的限制,无论是在工作量还是知识需求方面。此外,在技术债务的累积和解决方面引入更严格的控制,确保任何短期“领域泄漏”可以在其增长之前被拒绝或纠正。

该组织的移动应用程序中缺少的另一个指标是可重用性。如前所述,存在多个现有的成熟品牌市场应用程序。这些应用程序之间的功能一致性很低,由于希望保持单个市场自治,因此难以统一成一个移动应用程序。系统之间的紧密耦合降低了在其他地方重用领域的能力:必须移植大部分现有移动应用程序才能在另一个市场中重用一个领域,这带来了高昂的集成和持续管理成本。我们对适当的领域边界上下文控制的利用是模块化的良好第一步,因为它阻止了对其他领域的直接依赖。但正如我们发现的那样,这不是我们唯一需要采取的行动。

跨越应用程序的领域

场景 1 - “整洁的单体”

当将应用程序作为一个孤立的整体来看待时,简单地将应用程序拆分成领域,分配团队,并管理它们的耦合(以避免违反它们的边界上下文)非常有效。以向单个应用程序发出功能请求为例

功能请求被传递给拥有相关领域的应用程序小组。我们严格的边界上下文意味着我们的更改的爆炸半径被限制在自身内部,这意味着我们的功能可以构建、测试甚至部署,而无需更改应用程序的其他部分。我们加快了上市时间,并允许多个功能同时独立开发。太棒了!

事实上,这在单个市场环境中运行良好。然而,一旦我们试图解决我们的第二个扩展问题 - 由于缺乏可重用性而导致的市场功能差异 - 我们就开始遇到问题。

场景 2 - “下一个市场机会”

该组织在追求领域模块化的过程中,下一步是通过将“整洁的单体”的部分移植到现有的市场应用程序中,实现快速开发节省。这涉及到创建一个通用框架(我们将在后面讨论其中的一些方面),该框架允许在移动应用程序中重用功能/领域,而无需考虑其来源。为了更好地说明我们的方法,下面的示例显示了两个市场应用程序,一个在英国,另一个是基于美国的新应用程序。我们位于美国的应用程序团队已经决定,除了他们美国特有的领域之外,他们还希望使用忠诚度积分和结账领域作为其应用程序的一部分,并已将它们导入。

对于组织来说,这似乎意味着市场团队在开发方面节省了一个数量级的成本,与他们传统上重写领域功能的行为相比。然而,这并非故事的结束——在我们急于转向模块化的过程中,我们没有考虑到组织中现有的沟通结构,而这最终决定了工作的优先级。以我们之前的例子来解释:美国团队在他们自己的市场使用这些领域后,对他们导入的领域之一提出了一个新功能的想法。他们不拥有或不了解该领域的上下文,因此他们联系了英国应用程序团队并提交了一个功能请求。英国团队接受了该请求并表示这是一个“好主意”,但他们目前“正在处理来自英国利益相关者的请求”,因此不清楚他们何时能够开始工作…

我们发现,这种在优先考虑领域功能方面的利益冲突限制了共享功能使用者可以期望的重用程度——这在市场团队对导入领域缺乏进展感到沮丧时很明显。我们设想了几个解决这个问题的方案:使用团队可以考虑复制他们自己的领域版本并围绕它组建一个团队。然而,正如我们已经知道的,学习/拥有整个领域以添加少量功能效率低下,并且分叉也会给未来市场之间任何升级或功能一致性的共享带来问题。我们研究的另一个选择是通过拉取请求进行贡献。然而,这给贡献团队带来了自己的认知负担——迫使他们在第二个代码库中工作,同时仍然依赖于来自主要领域团队的跨团队贡献的支持。例如,尚不清楚领域团队是否有足够的时间在他们自己市场的功能开发之间提供架构指导或代码审查。

场景 3 - “市场无关领域”

显然,问题在于我们团队的组织方式。 康威定律 指出,组织会设计其业务系统以反映其自身的沟通结构。我们之前的例子描述了一种场景,其中从技术角度来看,功能是模块化的,但从所有权角度来看,它仍然是单体的:“忠诚度积分最初是为英国应用程序创建的,因此它属于该团队。” 对此的一种可能的回应在 逆康威策略 中有所描述。这涉及改变开发团队的结构,以便他们能够实现所选择的技术架构。

在下面的例子中,我们从之前的场景中前进,并对团队结构进行更改以反映我们之前采用的模块化架构。领域从特定的移动应用程序中抽象出来,而是成为自治的开发团队本身。当我们这样做时,我们注意到应用程序团队之间的关系发生了变化,因为他们不再依赖于市场之间的功能。取而代之的是,我们发现形成了新的关系,这些关系可以用消费者和提供者来更好地描述。我们的领域团队为他们的市场客户提供功能,而市场客户反过来消费这些功能,并反馈新的功能请求以更好地开发领域产品。

这种重组相对于我们之前的迭代的主要优势是重点的明确。早些时候,我们描述了当一个市场提出更改来自另一个市场内部的领域的请求时发生的利益冲突。将领域从其市场中抽象出来改变了重点,从仅为市场利益构建任何功能,转变为更全面的使命,即构建满足其消费者需求的功能。成功可以通过消费者采用率以及最终用户对它的接受程度来衡量。任何新功能的审查仅基于它为领域及其消费者带来的价值总量。

专注于开发者体验以支持模块化

回顾一下,组织现在拥有一个支持跨市场组件模块化的拓扑结构。自治团队被分配了领域来拥有和开发。市场应用程序被简化为配置容器。从概念上讲,这一切都有道理——我们可以很容易地绘制出反馈从消费者到提供者的流动方式。我们还可以做出高级的乌托邦假设,例如:“所有领域都是独立开发/部署的”“消费者‘只需’拉取他们想要形成应用程序的任何可重用领域。”

然而,在实践中,我们发现这些都是难以解决的技术问题。例如,如何在自治领域团队之间保持一定程度的 UX/品牌一致性?当您只负责整个应用程序的一部分时,如何启用移动应用程序开发?如何允许发现领域?可测试性?跨市场的兼容性?解决这些问题是完全可能的,但会带来自己的认知负担,而这种责任在我们目前的结构中没有明确的所有者。所以我们创建了一个!

解决核心问题的领域

我们的新领域被归类为“平台”。平台本质上是我们用来描述工具和指导的包罗万象的术语,这些工具和指导使我们的团队能够在选择的架构中独立交付。我们的新领域团队维护着我们已经看到的提供者/消费者关系,并负责改善在平台内构建其应用程序和领域的团队的开发人员体验。我们假设更强大的开发人员体验将有助于推动我们新架构的采用。

但“开发人员体验”(DX)是一个相当笼统的术语,因此我们认为有必要定义我们的新团队提供良好体验所需的内容。我们将 DX 领域细化到一组必要的功能——第一个是高效的启动

对于任何通用框架,都存在不可避免的学习曲线。良好的开发人员体验旨在尽可能减少该曲线的严重程度。合理的默认值和入门套件是一种非专制的方式,可以减少在入职时感受到的摩擦。我们为我们的平台领域定义了一些示例

我们承诺

  • 您将能够使用一个命令快速生成一个新的领域,其中包含所有相关的移动依赖项、通用 UI/UX、遥测和 CI/CD 基础设施
  • 您将能够独立构建、测试和运行您的领域
  • 您的领域在捆绑到应用程序中时,运行方式与独立运行时相同

请注意,这些承诺描述了开发人员生产力平台中自助服务的元素。因此,我们认为一个有效的开发平台 是一个允许专注于最终用户功能的团队专注于他们的任务,而不是在看似无休止的非生产性任务 列表中挣扎。

我们为平台领域确定的第二个必要功能是技术架构即服务。在组织中,架构功能也遵循康威定律,因此架构决策的责任集中在一个与需要指导的团队分离的独立筒仓中。我们的自治团队虽然能够做出自己的决定,但往往需要某种程度的“技术指导”来协调原则、模式和组织治理。当我们将这些需求扩展到按需服务时,我们创建了一些看起来像

我们承诺

  • 我们提供的最佳实践将附带您可以使用或实际可以采取的步骤的示例
  • 我们将维护每个应用程序的领域使用情况的总体情况,并在需要时协调跨垂直领域的协作
  • 生产路径将是可见且正确的
  • 我们将与您合作

请注意,这些承诺描述了对团队的仆人式领导 关系,认识到每个人都对架构负责。这与有些人可能描述的命令和控制架构治理策略形成对比。

关于平台领域,还有一个值得从之前的例子中重新审视的最后一点。根据我们的经验,成功的平台团队是深入了解其客户需求的团队。在丰田精益生产中,“现场现物” 大致翻译为“亲自去看看”。这个想法是,通过访问问题的根源并亲眼看到它,只有这样你才能知道如何解决它。我们了解到,一个专注于改善开发人员体验的团队必须能够与使用其产品的开发人员产生共鸣,以真正了解他们的需求。当我们最初创建平台团队时,我们没有给予这个原则应有的关注,结果我们的自治团队找到了自己的方法。这最终导致了工作重复、不兼容以及对架构缺乏信任,这需要时间来纠正。

结果

我们讲述了如何模块化移动应用程序的故事,但它随着时间的推移有多成功?获得经验证据可能很困难。根据我们的经验,在一个组织中拥有一个遗留应用程序和一个新架构的应用程序,它们使用相同的领域,并且两个应用程序都有交付指标,这种情况并不常见。然而,幸运的是,在这种情况下,该组织足够大,可以一次转换一个应用程序。对于这些结果,我们比较了两个功能相似的零售应用程序。一个遗留应用程序具有高度耦合和低内聚性,尽管它拥有一个高效且成熟的开发团队(“遗留单体”)。另一个是之前描述的模块化重构练习的结果——一个定义明确且管理良好的边界上下文,但由“较新的”独立领域团队支持(“领域边界上下文应用程序”)。周期时间在这里是一个很好的衡量指标,因为它代表了在代码中“进行”更改所需的时间,并且不包括将应用程序推送到商店——这是一个应用程序类型没有影响的可变长度过程。

移动应用程序类型周期时间
遗留单体17 天
领域边界上下文(平均)10.3 天

即使在我们的第二个应用程序中对所有领域团队的周期时间进行平均后,我们也看到了与经验较少的遗留应用程序相比的显著提升。

我们的第二个比较涉及重用选项,或缺乏重用选项。在这种情况下,我们检查了组织中的相同两个移动应用程序。同样,我们将一个需要现有领域功能(除了自己编写之外别无选择)的应用程序与我们的模块化应用程序(能够即插即用现有领域)进行比较。我们忽略了生产路径上的常见步骤,因为它们对我们正在衡量的内容没有影响。相反,我们专注于开发团队控制范围内的方面,并从生产前的“产品签署”到一个全职工作的开发人员对与设计师合作的单个开发对的开发完成,来衡量我们的开发过程。

集成类型平均开发时间
非模块化90 天
模块化5 天

上面的数据显示了模块化架构在具有业务需求的环境中的强大功能。

顺便说一下,值得一提的是,我们排除的这些外部因素也应该被衡量。优化您的开发性能可能会揭示您整个流程中的其他瓶颈。例如,如果创建发布需要 6 个月,而治理需要 1 个月才能批准,那么治理只是该流程中相对较小的一部分。但是,如果开发时间线可以缩短到 5 天,而批准仍然需要 1 个月,那么合规性 可能会成为下一个需要优化的瓶颈。

上面结果中没有体现的另一个优势是,围绕领域组织的团队对集成活动的影响。我们发现自治领域团队自然而然地把自己派到市场应用程序团队中,以试图加快活动。我们认为,这源于领域团队的重点转变,即其领域产品的成功来自于其采用。

我们发现两个同心反馈循环影响着应用的采用率。外层循环是来自领域消费者(例如,应用容器)的良好集成体验。这是一个以开发者为中心的反馈循环,通过消费者在整体品牌特定产品中配置和实施领域的难易程度来衡量。内层循环是良好的最终用户体验——消费者市场客户对整体旅程(包括集成领域)的接受程度。糟糕的消费者体验会影响应用的采用率,最终可能导致领域团队与实际使用该功能的用户隔绝。我们发现,与消费者团队紧密合作并直接接触最终用户的领域团队拥有最快的反馈循环,因此也取得了最大的成功。

最后值得一提的比较来自我们的平台领域。开始一个新的领域功能是一个耗时的活动,会增加功能的整体开发成本。如前所述,平台团队的目标是通过识别流程中的痛点并优化它们来减少这一时间——改善开发者体验。当我们将此模型应用于我们模块化架构中的领域团队时,我们发现每个团队的启动成本 *降低了 80% 以上*。一对开发人员可以在一个下午完成原本预计需要一周才能完成的团队开发工作!

局限性

到目前为止,您应该已经对模块化架构在移动应用中的优势有了相当乐观的认识。但在用大锤敲打您病入膏肓的单体应用之前,值得记住这些方法的局限性。首先,也是最重要的是,这种架构转变 *需要大量持续的时间和精力*。它应该只用于解决围绕上市速度的严重现有业务问题。其次,赋予领域团队自主权既是福也是祸。我们的平台团队可以以合理的默认值的形式提供通用实现,但最终的选择权在团队自己手中。自然,如果领域团队希望被纳入/接受到市场应用中,那么在平台要求(如通用 UI/UX)上达成一致符合他们的利益。然而,*管理来自类似内部依赖项或异类设计模式的膨胀* 却很棘手。忽视这个问题,任由整体应用不受控制地增长,会导致客户体验性能低下。同样,我们发现,投资技术领导力,结合强大的护栏和指南,可以通过提供架构/设计监督、指导和最重要的是沟通来帮助缓解这个问题。

总结

回顾一下,在本文开头,我们确定了在采用多应用策略的组织中表现出的两个重大交付问题。*引入新功能到生产环境的时间越来越长*,以及*其他类似的内部应用之间功能差异越来越大*。我们证明了这些问题的解决方案不在于围绕技术架构、团队结构或技术债务的单一策略,而在于所有这些方面的同时演进的复合体。我们首先展示了如何演进团队结构以支持所需的模块化和以领域为中心的架构,从而改善认知和上下文负载,同时赋予团队独立于其他团队进行开发的自主权。我们展示了如何自然地将团队和领域提升到与它们起源的应用/市场无关,以及这如何减轻康威定律在应用单体中固有的影响。我们观察到,这种变化使消费者/提供者关系自然发生。我们进行的最后同步转变是识别和投资“平台”领域,以解决我们观察到的由于团队和领域解耦而产生的核心问题。

将所有这些方面结合在一起,我们能够证明在市场应用中所有模块化领域平均的 *循环时间缩短了 60%*。我们还发现,将模块化领域集成到市场应用中,而不是从头开始编写,*开发成本提高了 18 倍*。此外,对工程效率的关注使我们的模块化架构蓬勃发展,因为新领域的 *启动成本降低了 80%*,以及“平台团队”提供的持续支持。对于我们的客户来说,这些节省意味着能够抓住以前被认为 ROI 太低而无法证明其努力的市场机会——这些机会多年来一直是其竞争对手的无争议领域。

关键的收获是,与团队紧密相连的模块化架构 *在适当的情况下* 对组织非常有利。虽然我们与突出显示的组织合作期间取得的结果非常出色,但它们是针对此特定案例的。花时间了解您自己的环境,寻找迹象和反模式,然后再采取行动。此外,不要低估将我们所描述的这种生态系统整合在一起所需的预先和持续的努力。一个考虑不周的努力很可能带来的问题比解决的问题更多。但是,通过接受您的情况在范围上将是独一无二的,因此抵制“货物崇拜”的吸引力:专注于同理心、自主权和能够同时实现架构的沟通渠道,*那么您就有充分的理由复制我们所看到的成功*。


致谢

特别感谢 Carl Nygard、Tim Cochran 和 Greg Davis 对所有建议和关键反馈。还要感谢 Rob Horn 说服我整理我的想法并实际写这篇文章。最后,感谢 Martin Fowler 的指导。

重大修订

2023 年 6 月 13 日:发布