今天我们来说说,一个代码模块的代码是如何一步步腐化变质,到最后程序员都不愿意去维护它,然后要么重构,要么废弃换新模块的? 代码是有一定的周期的,这个没有错。 不符合以上标准都可以称之为代码腐化,形象的理解就是一个苹果,从内部开始烂了,烂到原本应该负责内部代码的程序员拒绝去维护了。 突然产品提出了某一个Job的图片有特别,要求显示10张图片,就对抽象的图片部分做了if-else的处理……无用代码,废弃的接口没有标明 代码腐化的原因 没有代码会是init commit的时候就开始腐化的 防止代码腐化的建议: 代码规范标准化,统一化治理 代码的内部质量其实很难保证,规范执行也更多的靠人治,甚至个别标准化的东西,只能通过代码层面去检验,无法通过测试或其他手段进行。 有必要经常深入解决腐化问题的代码,必然是业务需求很多,经常要变更的代码。不要等到它已经腐化到代码生命周期都快走到头了,才想起来去维护清理它,要经常维修一下,才能更好的让它发挥作用,是吧?
前言 任何人类的设计都会腐化,软件系统也不例外 腐化之谜 随着系统的规模增长和复杂度膨胀,系统会慢慢腐化。 ? 于是改一个很简单的下单地址,就会牵动整个交易系统十几处的改动。 如何解决这种腐化之谜呢? 参考计算机系统架构: 一个复杂的计算机系统架构包括:软件系统元素,元素之间的联系,元素本身有自己特有属性。 于是我们可以在架构角度参考计算机系统架构的实现。 架构建模 ? 业务是否重要且有很高的预期 是否可以让运营和PM介入 遵循迭代式的开放方法 领域模型好坏的标准: 模型反映了对于问题的抽象,抽象没有统一的标准 模型是迭代演进的,需要持续集成,改进 通用语言,领域模型和代码目标意图一致
为分析这个问题, 我们会从 instant add/drop column 在 Innodb 的实现原理与细节方面来阐述这个数据腐化bug的具体原因。 数据腐化问题 由 instant add/drop column 引入了多个数据腐化问题,其中一个问题可以从: [PS-8292] MySQL 8.0.29 fails to perform crash 从代码上看,这个bug可能会造成数据的静默错误(数据完全错乱而且不报任何错误),而不仅仅是崩溃这一种现象。 为支持 instant add/drop column,redo log 记录的格式发生了变化,因为代码bug,导致在解析 redo log 做恢复的时候,得到的字段信息错误,导致数据腐化。 8.0.30的修复代码本身也是不能正确解析8.0.29产生的 redo log ,只是根据版本号检测出8.0.29 redo log,进而报错防止数据进一步恶化。
为分析这个问题, 我们会从 instant add/drop column 在 Innodb 的实现原理与细节方面来阐述这个数据腐化bug的具体原因。 数据腐化问题 由 instant add/drop column 引入了多个数据腐化问题,其中一个问题可以从: [PS-8292] MySQL 8.0.29 fails to perform crash 从代码上看,这个bug可能会造成数据的静默错误(数据完全错乱而且不报任何错误),而不仅仅是崩溃这一种现象。 为支持 instant add/drop column,redo log 记录的格式发生了变化,因为代码bug,导致在解析 redo log 做恢复的时候,得到的字段信息错误,导致数据腐化。 8.0.30的修复代码本身也是不能正确解析8.0.29产生的 redo log ,只是根据版本号检测出8.0.29 redo log,进而报错防止数据进一步恶化。
,一旦出现故障,需要及时告警并及时处理等 虽然每一位架构师可能都清楚架构边界的重要性,事实却是一个边界清晰的软件架构,往往会随着时间的推移,慢慢变得混乱,最后变成让人迷路的依赖网: 如此,也就产生了代码的腐化 02 代码评审 频繁开展代码评审是避免代码腐化的最有效武器。然而,总是有许多团队和个人推说没有时间,而不开展代码评审。 这里所说的代码评审,可以有三种形式: 融入到CI/CD流水线的代码静态扫描 团队定期召开的内部代码评审(可以是每天或每周) 以提交流程方式运作的代码评审 如下图所示,是我为某客户制订的代码评审流程: 因此,我们需要将测试代码与产品代码等同视之,也需要纳入到代码评审的范围,并做到及时重构,持续不断的提升其代码质量。 结论 回归到Twitter的问题。 次之,如果在提交代码(包括测试和运维代码)时,能启动代码评审的流程,通过引入更多评审者,也许就能通过更多双慧眼及时发现问题。
在长期运转的项目中,架构的腐化是怎么产生的?为什么常见的面向对象技术无法解决这类问题?如何延缓架构的腐化? 本文将尝试解释这一切,并提出相应的解决方案。 很少有人注意到,规模的变大才是导致架构腐化的根源——因果关系在时空上的不连续,使得人们并不能从其中获得经验,只是一再重复这个悲剧的循环。 将独立的模块放入独立的进程 上面的解决方案核心原则只有一条:始终将核心代码库控制在团队可以理解的范围内。如果运转良好,能够很大程度上解决架构因为代码规模变大而腐化的问题。 如果不这么做,动辄上百万千万行的代码堆在一个系统中,随着时间的推移,开发者逐渐对代码失控,架构的腐化是迟早的事情。 针对每个应用,开发者能够在更小的代码内采用自己熟悉的技术方案,从而减少架构腐化的可能。 结语 没有糟糕的架构,变化使之 我访问过很多团队。
在长期运转的项目中,架构的腐化是怎么产生的?为什么常见的面向对象技术无法解决这类问题?如何延缓架构的腐化? 本文将尝试解释这一切,并提出相应的解决方案。 很少有人注意到,规模的变大才是导致架构腐化的根源——因果关系在时空上的不连续,使得人们并不能从其中获得经验,只是一再重复这个悲剧的循环。 将独立的模块放入独立的进程 上面的解决方案核心原则只有一条:始终将核心代码库控制在团队可以理解的范围内。如果运转良好,能够很大程度上解决架构因为代码规模变大而腐化的问题。 如果不这么做,动辄上百万千万行的代码堆在一个系统中,随着时间的推移,开发者逐渐对代码失控,架构的腐化是迟早的事情。 针对每个应用,开发者能够在更小的代码内采用自己熟悉的技术方案,从而减少架构腐化的可能。 结语 没有糟糕的架构,变化使之 我访问过很多团队。
在长期运转的项目中,架构的腐化是怎么产生的?为什么常见的面向对象技术无法解决这类问题?如何延缓架构的腐化? 本文将尝试解释这一切,并提出相应的解决方案。 很少有人注意到,规模的变大才是导致架构腐化的根源——因果关系在时空上的不连续,使得人们并不能从其中获得经验,只是一再重复这个悲剧的循环。 将独立的模块放入独立的进程 上面的解决方案核心原则只有一条:始终将核心代码库控制在团队可以理解的范围内。如果运转良好,能够很大程度上解决架构因为代码规模变大而腐化的问题。 如果不这么做,动辄上百万千万行的代码堆在一个系统中,随着时间的推移,开发者逐渐对代码失控,架构的腐化是迟早的事情。 针对每个应用,开发者能够在更小的代码内采用自己熟悉的技术方案,从而减少架构腐化的可能。 结语 没有糟糕的架构,变化使之 我访问过很多团队。
2.1 防腐化治理的背景 2022年去哪儿落地了一个公司级的代码瘦身项目,我们做到了代码精简50%,应用精简50%。 然而,如果后期不继续进行干预,随着功能的迭代和人员的更替,应用数量和代码量还会增加,系统会逐渐腐化。如果我们不能保持代码瘦身成果,那么我们之前花费的精力就会完全白费。 比如无用代码变多,无效配置多,代码覆盖率低,发布依赖太多,内外部调用不合理……这些都是系统腐化的表现。但看起来,这些更像是一个系统复杂度的度量。 静态属性度量应用,包括代码量、代码圈复杂度和有效代码量。调用属性度量应用接口,包括外部依赖、内部链路复杂度和外部链路复杂度。 在具体的详情中,还能看到它的代码行数等具体数据,这些数据为我们提供了一个参考。 3)应用腐化治理面板 在应用腐化治理面板中,我们主要针对的是应用的治理。这个过程主要针对的是静态复杂度的部分。
Tech 导读 本文从Archunit的比较优势、功能和验证范围介绍、引入方式多方面进行阐述,介绍了Archunit在降低或及早发现架构腐化现象的发生,保持统一的系统架构约束中的作用。 架构演进和架构腐化是看待架构的不同视角:架构腐化着眼于现状,架构演进侧重于未来。 架构腐化不可避免,随着时间流转腐化现象必然发生。而需要做的是:通过某种方式及早发现和修正。 需要通过引入一种机制或技术,降低或及早发现架构腐化现象的发生,保持统一的系统架构约束。 : 代码评审:通过强流程控制代码评审活动发生,增强代码评审的强度和质量 【优势】 不需要引入额外的技术,没有学习成本 灵活:通过人工评审方式可以覆盖架构约束更全面 【劣势】 非自动化方式执行,质量靠人工保证 引入Archunit进行架构约束自动化检查后,将对以下方面产生影响: 有助于降低系统架构腐化,提升系统可维护性 新类库引入有一定的学习成本 代码评审活动增加一项活动:执行架构约束单元测试 开发成员日常开发中需要持续执行并关注架构约束单测结果
几天前,我发表了文章《Twitter的问题说明再好的软件也会腐化》,文中提到避免软件腐化的三种有效手段,其中之一是持续测试。 巧的是,上周刚拿到于君泽兄亲赠的译作《持续架构实践》。 书中给出该过程的示意图: 持续架构的本质就是为了在架构不断演进的过程中避免架构的腐化,而避免架构腐化的前提则需要迅速而及时地知道架构可能走向腐化的征兆。 剩下的就是最为真实的代码,然而庞大而糟糕的代码库,仿佛迷雾重重的冒险乐园,需要我们不断探索。 运行这些测试也能帮助我们发现已有代码与文档的不一致,也可以帮助我们更好地探测源代码在运行时的调用关系,尤其针对消息驱动的分布式系统,价值更为明显。 架构腐化是我们需要不断斗争的敌人,而持续测试会是一件非常锋利的武器。不要把它藏到武器库,赶紧用起来吧! ----
写这款工具主要是看了优酷的几篇 向工程腐化开炮 的系列文章,觉得其中的几个点可以通过依赖检查的方式提前找到问题,所以着手找了几个点写了下,并输出 report html 方便查看。 四、总结: 基于工程腐化系列的文章其实可以做很多的检查,比如混淆章节中: layout 中引用不存在的 class 需要进行检查,而且在 apk 编译过程中,并不会引发构建失败,但依然会生成相对应的keep 一旦在运行时被“加载“,那么会引发 Java 类找不到的异常 其他的实现就交给大家自己发挥实现了,最后附上源码地址:https://github.com/MRwangqi/pluginDemo/ 向工程腐化开炮系列 : 向工程腐化开炮 | proguard治理 向工程腐化开炮 | manifest治理 向工程腐化开炮 | Java代码治理 向工程腐化开炮|资源治理
3.2 不可避免的代码腐化 除了业务本身的耦合带来的复杂性,代码腐化也是另一个让业务系统变得复杂的重要原因。 偶然间看见自己多年前写的代码.gif 到底代码是如何腐化的? 聊了这么多关于瀑布流式开发和敏捷开发,这和代码不可避免的腐化有什么关系呢? 通过上面的例子,我们可以看到,腐化除了来自开发者低质量的代码,更核心的是来自于系统架构的腐化。而在“中华田园敏捷开发”的这种开发方式下,需求本身就是零散的,目标也是模糊的。 只要状态好,日敲千行代码不在话下。程序员最害怕的还是代码腐化。
供应链系统相对于 Keep 电商业务发展明显滞后,甚至有可能进一步阻碍 Keep 电商业务发展,而当时的供应链系统因缺乏系统性规划、代码缺少规范,导致这个元老级系统积重难返。 当时面临的主要问题如下: 系统边界不清晰 架构混乱,系统内部分层不清晰 越来越模糊 usecase,导致代码边界和事务不清晰; 分层后各层职责和接口职责不清晰,导致接口依赖混乱,甚至出现循环依赖。 防止定制化查询腐化领域模型 我们通过引入 CQRS 模式,隔离命令与查询领域模型。 防止与其他限界上下文交互导致领域模型腐化 如下图所示采购上下文通过防腐层 (ACL) 将仓储库存核心上下文中的仓库信息映射为自身上下文中的仓库值对象,防止仓库信息依赖腐化。 发布领域事件代码如下: 订阅领域事件 注册订阅组 在订阅组中声明订阅事件 在持续集成开发过程中如何同时保障效率和质量 - 单元测试保驾护航 核心领域模型添加单元测试,对应 Domain 测试
供应链系统相对于 Keep 电商业务发展明显滞后,甚至有可能进一步阻碍 Keep 电商业务发展,而当时的供应链系统因缺乏系统性规划、代码缺少规范,导致这个元老级系统积重难返。 当时面临的主要问题如下: 系统边界不清晰 架构混乱,系统内部分层不清晰 越来越模糊 usecase,导致代码边界和事务不清晰; 层后各层职责和接口职责不清晰,导致接口依赖混乱,甚至出现循环依赖。 防止定制化查询腐化领域模型 我们通过引入 CQRS 模式,隔离命令与查询领域模型。 防止与其他限界上下文交互导致领域模型腐化 如下图所示采购上下文通过防腐层 (ACL) 将仓储库存核心上下文中的仓库信息映射为自身上下文中的仓库值对象,防止仓库信息依赖腐化。 测试 核心业务接口场景添加单元测试,对应 CmdExe 测试 引入 Mockito 库,mock 相关接口和数据,验证流程环节是否正确 在单测代码中造单测相关数据,保证单测数据可靠性 单测采用 H2
供应链系统相对于 Keep 电商业务发展明显滞后,甚至有可能进一步阻碍 Keep 电商业务发展,而当时的供应链系统因缺乏系统性规划、代码缺少规范,导致这个元老级系统积重难返。 当时面临的主要问题如下: 系统边界不清晰 架构混乱,系统内部分层不清晰 越来越模糊 usecase,导致代码边界和事务不清晰; 分层后各层职责和接口职责不清晰,导致接口依赖混乱,甚至出现循环依赖。 防止定制化查询腐化领域模型 我们通过引入 CQRS 模式,隔离命令与查询领域模型。 防止与其他限界上下文交互导致领域模型腐化 如下图所示采购上下文通过防腐层 (ACL) 将仓储库存核心上下文中的仓库信息映射为自身上下文中的仓库值对象,防止仓库信息依赖腐化。 发布领域事件代码如下: 订阅领域事件 注册订阅组 在订阅组中声明订阅事件 在持续集成开发过程中如何同时保障效率和质量 - 单元测试保驾护航 核心领域模型添加单元测试,对应 Domain 测试
供应链系统相对于 Keep 电商业务发展明显滞后,甚至有可能进一步阻碍 Keep 电商业务发展,而当时的供应链系统因缺乏系统性规划、代码缺少规范,导致这个元老级系统积重难返。 当时面临的主要问题如下: 系统边界不清晰 架构混乱,系统内部分层不清晰 越来越模糊 usecase,导致代码边界和事务不清晰; 分层后各层职责和接口职责不清晰,导致接口依赖混乱,甚至出现循环依赖。 防止定制化查询腐化领域模型 我们通过引入 CQRS 模式,隔离命令与查询领域模型。 防止与其他限界上下文交互导致领域模型腐化 如下图所示采购上下文通过防腐层 (ACL) 将仓储库存核心上下文中的仓库信息映射为自身上下文中的仓库值对象,防止仓库信息依赖腐化。 发布领域事件代码如下: 订阅领域事件 注册订阅组 在订阅组中声明订阅事件 在持续集成开发过程中如何同时保障效率和质量 - 单元测试保驾护航 核心领域模型添加单元测试,对应 Domain 测试
由于中文语法纠错的训练语料过少,无法充分训练序列生成模型,我们提出了一种腐化语料的单语数据增强方法,能够有效的扩大训练集的规模,并进一步提升模型的纠错效果。 -THREE- 基于腐化语料的单语数据增强方法 互联网中存在着大量的中文单语数据,即完全正确的中文语句。在这些容易获取且完全正确的单语语料中,合理地添加错误,即可得到大量的语法纠错并行语料。 我们设计了一种腐化算法,可以根据所需的错误类型比例,对单语语料进行造错,具体实现如下: ? 腐化后的语料示例: ? 受限于训练数据过少的情况,我们还提出了一种腐化语料的单语数据增强方法,扩充了训练集的规模。这种数据增强的方法可以在任何领域或者语言的单语语料上使用。
就像《架构腐化之谜》中提到的一样,我们的软件也会像一个生命体,经历从新生到衰老腐化的过程。而重构就像是一次手术,通过优化内部结构,减慢腐化衰老,让软件“青春永驻”,可见重构的威力。 就像“看板”不是“我们看到的那个白板”一样,“重构”也不是“重新修改代码”那么简单。 我就看到过太多打着重构的幌子,把系统改的面目全非,最后出了问题直接甩锅到重构身上的场景了。 重构只是代码级别的么?数据库如何重构呢?系统架构如何重构呢?工具框架如何重构呢?微服务架构下的服务重构呢?公司组织重构呢? 大道至简,一旦领悟并掌握了这个心法,就发现自己一下从之前狭义的代码重构中跳脱出来,任何广义上的重构都立刻变得有章可循。 通过不断地改进软件设计以达到简单设计的目标,减少由于设计与业务的不匹配带来的架构与设计腐化。
任何人类的设计都会腐化,很遗憾 软件尤其会... 其实,对于单体系统,也可以按照上下文拆分领域。如果这样做了,再把代码级的领域拆分为部署视角的微服务,也不是那么难的事情了。