如何避免新代码变包袱?阿里通用方法来了!

简介: 阿里妹导读:什么是设计?什么是架构?从零开始建立一个新的系统,新写的每行代码都可能成为明天的历史包袱?如何能有效的在遗留代码上工作?今天,阿里资深技术专家辉子为我们带来NBF框架下软件工程架构设计通用方法论,值得细细品读。

image
阿里妹导读:什么是设计?什么是架构?从零开始建立一个新的系统,新写的每行代码都可能成为明天的历史包袱?如何能有效的在遗留代码上工作?今天,阿里资深技术专家辉子为我们带来NBF框架下软件工程架构设计通用方法论,值得细细品读。

Note:本文讨论的是基于服务化前提下的通用软件工程架构方法论,并未涉及到微观设计或架构的具体细节。

前言

即使代码多年的人都会对这两个问题有点蒙圈:什么是设计?什么是架构?

从单词上看:设计是Software Design,架构是Software Architecture;分别对应的作者是:Designer和Architect:

  • Architect都是Designer,但Designer未必是Architect。正如所有的架构设计都是设计,但设计未必是架构设计;
  • Design关注微观代码(inside component),Architecture关注宏观软件结构(between components);
  • Architect应该都是从Designer成长起来的。毕业了用code编写软件;成长了用ppt设计软件;
  • 只会用ppt设计,但代码写得不好的Architect都是假的Architect;
  • Architecture里听到比较多的词语:Serverless、FAAS、Microservice、multi-layer、Event driven、OSGI、NBF......
  • Design里听到比较多的词语:SOLID、 DDD、正交设计、Design Pattern;
  • 搞不清SOLID,也不可能把软件的层次分好,也无法理解什么是OSGI的价值;
  • 好的Designer是通往好的Architect的必经之路。

服务化架构的基本原则

image

New System

从零开始建立一个新的系统,有几个特征:

  • 历史包袱小
  • 上下文简单
  • 设计的约束小
  • 新写的每行代码都可能成为明天的历史包袱

由于调用方还没有,新系统可以比较完美的执行我们预想的架构设计,但是切记,最后那行才是最重要的那行:不要让今天的代码成为明天的历史包袱,新的每行代码都在书写历史。

上图的1,2,3,4代表新建系统的顺序:

  1. 由“相”抽象出“心”:先思考,那么多的业务场景下“相”,共同的特征“心”是什么。并反向用更多的相去验证心。
  2. 将“心”具象成领域模型:关注领域模型(Domain Model),解耦数据模型(Persistence Model):将TUNNEL SPI化。
  3. 将领域模型中的依赖SPI化:解耦对外部系统的依赖,反转依赖控制权。
  4. Mock所有spi实现,确保“心”领域模型包裹的单元测试完全通过
  5. 实现TUNNEL BUNDLE:设计数据模型(Persistence Model),关注“存”,“取”不关注领域模型。
  6. 实现依赖SPI适配BUNDLE:连接真实依赖服务。
  7. 包装domain service:模型相关,业务无关。
  8. 根据业务需求组合/编排domain service成为scenario bundle或者业务SOP。

Working on legacy

对于一个软件工程师来讲,写代码最痛苦的事情莫过于coding on legacy,但同时又给了我们各种说辞:

  • 这些代码太烂了,改起来太费劲【需要更多人】
  • 这事做不到,因为以前系统架构问题导致的【责任不在我】
  • 经过我的修改,现在已经好很多了,工单数量大批下降【我功劳显著】
  • 知不知道:接手你代码的人其实也在重复说上述3件事情

如何能有效的在遗留代码上工作,业内有本非常不错的书,叫"Working Effectively with Legacy Code",值得精读:

image
图片来源:书籍《Working Effectively with Legacy Code》

所以我这里的标题可能不准确,我要讨论的更多是"遗留代码的重构",什么时候我们开始讨论需要把现有系统重构:

  • 代码确实腐化到无法正常维护,或者新加一个需求代价很大;
  • 目前代码的技术架构满足不了下一步业务的发展;
  • 很多特性已经下线作废,却跟有用的代码藕断丝连;
  • 业务逻辑随着发展分散到不同的应用里,界限不清;
  • 专家级的未雨绸缪,着眼未来的规划和新技术的应用;
  • 换老大了,需要立新的flag。

架构的基本原则依然是上面那幅图。但上下文的不同,我们的发力点和优先级有明显的区别。阿里整个体系里的依赖关系错综复杂,要对阿里环境下的系统做重构是件绝对谨小慎微的事情。为了完成在这么复杂体系下的架构及代码重构,我们必须有条不紊的分离关注点以及一如既往的坚持软件卓越。

聚焦与收敛上游调用

image

解耦下游依赖

image

以服务为单位切换

image

老系统下线

经过一步一步的分解,legacy系统已经完全被重构,并且具备随时切换的准备。这里我给几个建议:

  1. 先把老实现作为API的默认实现,新的实现作为老的实现的降级实现,并使用策略分流一部分流量(具体比例跟团队信心相关);
  2. 对于有业务需求变更的部分应尽快实现在新的实现里,并将新实现作为API的默认实现,老实现作为新实现的降级实现,策略应该是即时降级,也就是新实现出现问题立刻降级到老实现;
  3. 运行一段时间没有问题后,讲所有默认实现切换为新实现,并将老实现作为新实现的降级实现;
  4. 其实这时就算所有切换完毕:老实现可以永远作为新实现的降级实现,也就是只要我升级一次服务,上一次成功版本就可以作为这次的降级实现,这样,线上问题回滚就是秒级的。

总结

本文基于借助NBF提供的远程多态,服务编排等能力下基础资料,商品,组网等系统新建,重构的经验及方法论总结。仅供遇到架构重构,解耦等问题困扰的技术团队参考。

Note:本文所有图形出自玉简

原文发布时间为:2019-09-30
作者:辉子
本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。

相关文章
|
7月前
|
设计模式 算法 程序员
程序员为何需要反复修改Bug?探寻代码编写中的挑战与现实
作为开发者,我们在日常开发过程中,往往会遇到反复修改bug的情况,而且不能一次性把代码写的完美无瑕,其实开发项目是一项复杂而富有挑战性的任务,即使经验丰富的程序员也难以在一次性编写完美无瑕地完成代码,我个人觉得一次性写好代码是不可能完成的事情。虽然在设计之初已经尽力思考全面,并在实际操作中力求精确,但程序员仍然需要花费大量时间和精力来调试和修复Bug。那么本文就来分享程序员需要反复修改Bug的原因,以及在开发中所面临的复杂性与挑战。
181 1
程序员为何需要反复修改Bug?探寻代码编写中的挑战与现实
|
4月前
|
Java 缓存 数据库连接
揭秘!Struts 2性能翻倍的秘诀:不可思议的优化技巧大公开
【8月更文挑战第31天】《Struts 2性能优化技巧》介绍了提升Struts 2 Web应用响应速度的关键策略,包括减少配置开销、优化Action处理、合理使用拦截器、精简标签库使用、改进数据访问方式、利用缓存机制以及浏览器与网络层面的优化。通过实施这些技巧,如懒加载配置、异步请求处理、高效数据库连接管理和启用GZIP压缩等,可显著提高应用性能,为用户提供更快的体验。性能优化需根据实际场景持续调整。
80 0
|
7月前
|
存储 Web App开发 运维
发布、部署,傻傻分不清楚?从概念到实际场景,再到工具应用,一篇文章让你彻底搞清楚
部署和发布是软件工程中经常互换使用的两个术语,甚至感觉是等价的。然而,它们是不同的! • 部署是将软件从一个受控环境转移到另一个受控环境,它的目的是将软件从开发状态转化为生产状态,使得软件可以为用户提供服务。 • 发布是将软件推向用户的过程,应用程序需要多次更新、安全补丁和代码更改,跨平台和环境部署需要对版本进行适当的管理,有一定的计划性和管控因素。
1581 1
|
5月前
|
SQL 缓存 安全
codereview开发问题之CodeReview阶段性能问题如何解决
codereview开发问题之CodeReview阶段性能问题如何解决
|
6月前
|
Java 数据库连接
惊呆了!JAVA反射:你的代码竟然能这样“自我修复”?
【6月更文挑战第30天】Java反射允许运行时访问类和方法,模拟“自我修复”能力。当UserService的getUserById方法抛出异常时,通过反射捕获异常并调用handleException进行处理。此示例展示了如何记录错误,返回默认用户对象,而无需原始代码更改。反射提供了一种动态异常处理机制,增强代码的适应性和弹性。
45 0
|
7月前
|
算法 安全 数据安全/隐私保护
深入探究一个长期隐藏的底层bug的学习报告
在软件开发的过程中,底层bug往往像一颗定时炸弹,随时可能引发严重的问题。本文将分享我在开发过程中遇到的一个长期未被发现的底层bug,以及我如何逐步排查并最终解决这个问题的全过程。通过这次排查,我深刻认识到了代码规范性的重要性。一个不规范的代码修改,虽然短期内可能不会引起问题,但长期累积下来,可能会引发灾难性的后果。此外,我也意识到了底层模块的通用性和风险意识的重要性。在解决一个问题的同时,应该审视是否有相似的问题存在,以避免未来的风险。
133 3
|
存储 缓存 搜索推荐
想要快速地拥有Sitecore DXP平台!这九个开发大坑一定要避开!
随着互联网技术的深入的发展,人们对于个性化的渴望已经达到了新的阈值,这也让以数字洞察力、个性化体验为名的Sitecore DXP平台成为了品牌们竞相追捧的新宠。而在这样的需要背景下,一众新手企业纷纷投身市场,想要分一杯羹。但是经验不足的新人入场,难免会带来不少麻烦,甚至引发了人们对于Sitecore性能的质疑。
|
设计模式
重构·改善既有代码的设计.04之重构手法(下)完结
重构改善既有代码的设计完结篇,汇总了全部的重构手法。看看哪些手法对你的项目能有所帮助…
7419 2
重构·改善既有代码的设计.04之重构手法(下)完结
|
算法
简单几行代码背后的重大意义
简单几行代码背后的重大意义
|
设计模式 JSON 缓存
如何“好好利用多态”写出又臭又长又难以维护的代码?| Feeds 流重构方案
如何“好好利用多态”写出又臭又长又难以维护的代码?| Feeds 流重构方案
85 0