19

为维护而设计:架构设计的首要原则

 3 years ago
source link: https://www.phodal.com/blog/design-for-maintenance-the-architecture-princinple-rules/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

为维护而设计:架构设计的首要原则

Posted by: Phodal Huang Nov. 1, 2021, 7:57 p.m.

软件开发总成本 = 开发成本 + 维护成本;软件维护成本 = 理解成本 + 修改成本 + 测试成本 + 部署成本。—— Ken Beck

在设计框架、系统架构时,可扩展性是人们想追求的特征之一。从技术社区的文章上,我们可以看到大量的相关字典,诸如于“通过配置和定义进行可扩展”,又或者是“业务流程”的可扩展,还有各类的“插件 ”以及“可扩展的点” 等等的话术。

真是呢,从我们所经历的大部分项目来说,这些系统的可扩展性并没有真的那么好。有些,可能是在设计系统时,它可以满足于当前的需要,不适合未来的场景 —— 这是另外一个故事了。有此,则是架构师在设计系统的时候,缺乏对于边界的限定的考虑。对于边界限定的另外一个解释就是,系统实现了开发时的可扩展性,但是忽视了维度时期带来的问题。而软件的开发周期中,维护成本往往占据了成本的主要部分,如开头 Ken Beck 所说。

这时就不禁让人又开始思考起来,没有为维护设计的系统,真的是可扩展的吗?

架构真的是可扩展?

在诸多系统里,开发时正如它的设计那样,具备着良好的可扩展性。只是在运行和维护时,它的可扩展性可能会失去效应。

一个 DDD 系统腐化的传闻

这是一个未严格经验证的 DDD 传闻。

在《领域驱动设计:软件核心复杂性应对之道》一书中,Eric Evans 根据在项目上的重构经验,给出了领域驱动设计的系统化方法,并融合了一系列的领域特定相关的实践。我们相信 Eric 在重新构筑这个系统的架构时,经过了一系列的良好设计。而到了十几年后,这个系统的架构已经变成大泥球般,难以看出当初的精心设计。人员的流动,知识传承的流失,使得系统一步步走向腐化 —— 这几乎是大部分系统的共同问题。

OSGi 模块化的回归测试

OSGi 是一个颇为有趣的模块化、插件化方案,Eclipse 是最具备知名度的一应用场景。在可扩展性上,OSGi 有非常多的优点: 诸如于动态加载、更新和卸载模块而不用停止服务,可以实现系统的模块化、版本化。

在微服务架构流行之前,它的插件化能力对于大型系统来说非常有吸引力。发布部分新功能时,我们不需要发布整个系统,只需要发布其中的一个 bundle 包(插件)。

只是基于 OSGi 构建的 Web 应用会变成一个可怕的单体,每个 bundle 可能会被构建成“微服务”,bundle 之间存在相互调用 —— 在微内核架构里,我们不允许这样的存在。如此一来,一旦我们更新 bundle 时,会倾向于发布整个系统,而不是单个的 bundle 包。

在这时,我们依旧需要对整个系统进行回归测试。

低代码生成的遗留代码

这是的“遗留代码“ 是指难以测试的低代码平台。这里的低代码平台是指通用的低代码平台。

在那篇无代码编程 ,DSL 被视为核心要素,一个经过精心设计的领域特定语言/类编程语言(非 JSON DSL)。基于 DSL 设计,能为系统提供良好的可测试性和持续集成能力,这一点是普通 JSON 所不具备的。而一旦,我们生成的低代码是不可测试的,那么它就可能变成遗留代码 —— 它取决于平台所构建的自动化测试机制,以及自动化版本迁移的设计。

“复杂度同力一样不会消失,也不会凭空产生,它总是从一个物体转移到另一个物体或一种形式转为另一种形式。”

如果框架本身不考虑可测试和质量的问题,那么总有人得重新去考虑它们。

灵活性的另外一面

过去,PL/SQL 是我见过最灵活的系统,“人们可以不写代码,就搞定一切”。我们可以将其看作一种 DSL,它和我们遇见过的配置化系统是类似的,只需要简单地 “配置”,就可以快速地实现。

这种灵活的“配置”,非常有意思。我们可以在测试环境里,通过人工的方式,反复地、有预见性地对测试它们。在我们追求自动化和稳定性的今天,这种不稳定性变得异常的可怕。在移动端,消息推送是通过配置化和接口的形式进行的,我们经常可以接收到测试人员向生产环境发送消息推送。

从意义来看,这种灵活性更多地应该被视为补救措施,而非系统设计的核心部分。

软件开发是一项团队活动。

无节制的甜头蔓延

越是在大型系统中,在破窗效应愈加的明显 —— 一旦有一个人没有按照规范来实施,那么将会有越来越多的人违反了规范。这个问题起源于,相关的规范没有通过流程或者工具有固化。诸如于,没有严格的代码检视流程,缺乏自动化的架构守护工具。

诸如于为了单一团队的原因,临时性修改了底层库的接口,开了一道的口子。导致了后续其它团队,会要求新的口子,导致底层的库偏离了原先的设计。因此,在添加新的临时性接口之前,考虑一下系统迁移和演进时会遇到的问题。

我们允许这种临时性地方案存在(紧急地上线总是会存在的),更应当在发生之后,重新设计和填补相关的问题。

自动化保障的缺乏

在不考虑调试的情况下,对于配置化等具备灵活性的系统,它们实现功能的难以自动化测试,也无法进行持续集成与持续部署。对于中大型的软件系统来说,它将会成为维护(开发 + 运维)工作的一个恶梦。

特别是,一旦配置化的系统缺乏版本化管理机制,将会对软件的回滚造成新的挑战。

软件开发所需求的一系列因素,都应该在可扩展 + 灵活性的情况下,再次进行相关的分析。

本地测试环境的缺失

在遇到一些复杂的 bug,不论是 Serverless ,还是声明式、配置化的方式,都需要与其它开发系统进行联调。我们需要构建一个本地的环境,以快速复现出 bug,才能进行修复。基于现有的平台场景之下,需要在云端进行调度与测试。

不过,这种混合高度的模式在《云端开发时》流行之后,会有所改观。

面向维护构建有序

在最近编写的《 Architecture 3.0》时,我和我的同事 @NoaLand 一直在讨论架构的有序性,以期待构建有序性模型解决部分维护问题。在现今的这个场景之下,除了解决上述的问题,可能还需要如下的几个方面。

正视问题的复杂性

如你所见,问题本身是复杂的,只有正视它,能会真正带来突破。

周期性的传达设计思想

如我们所知,软件开发团队成员的流动性会影响系统的稳定性。每隔几年内,团队的成员便会刷新一遍 —— 诸如于在互联网行业,三年便是个老员工。系统的相关知识会随着人员的流动,而被遗忘在某个角落里,新进来的团队成员不懂得系统原先的设计。

在我们尝试过的诸多方法中,一种颇为有效的方式是:在新员工进来一段时间后(如三个月),让他们讲述一下系统的架构。过程中,纠正他们对于系统的一些认知偏差。

适时解决技术债务

在实施的过程,需要持续地为技术债务腾出时间 —— 一个老生常谈的问题。在一系列的解决方式里,持续更新依赖是一个非常简单而有效的策略,与详细可以见《管理依赖的 11 个策略》。

当然,还有其它的技术债务了。

To be continue...


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK