BT

架构腐化之谜

作者 陈金洲 发布于 2011年7月5日 | 注意:GTLC全球技术领导力峰会 推动杰出的技术领导者学习和成长!

前言

新技术层出不穷。过去十年时间里,我们经历了许多激动人心的新技术,包括那些新的框架、语言、平台、编程模型等等。这些新技术极大地改善了开发人员的工作环境,缩短了产品和项目的面世时间。然而作为在软件行业第一线工作多年的从业者,我们却不得不面对一个现实,那就是当初采用新技术的乐趣随着项目周期的增长而迅速减少。无论当初的选择多么光鲜,半年、一年之后,只要这个项目依然活跃,业务在扩张——越来越多的功能需要加入,一些公共的问题就会逐渐显露出来。构建过慢,完成新功能让你痛不欲生,团队成员无法很快融入,文档无法及时更新等等。

在长期运转的项目中,架构的腐化是怎么产生的?为什么常见的面向对象技术无法解决这类问题?如何延缓架构的腐化?

本文将尝试解释这一切,并提出相应的解决方案。读者需要具备相当的开发经验——至少在同一个项目的开发上一年以上;公司负责架构演进、产品演进的角色会从本文找到灵感。

架构

架构这个词在各种场合不断地以各种面目表现出来。从维基百科的词条看来,我们经常听到的有插件架构(Plugin),以数据库为中心的架构(Database Centric),模型-视图-控制器架构(MVC),面向服务的架构(SOA),三层模型(Three-Tier model),模型驱动架构(MDA)等等等等。奇妙的是,这些词越大,实际的开发者就越痛苦。SOA很好——但在它提出的那个年代,带给开发者的只是面向厂商虚无缥缈的“公共数据类型”;MDA甚至都没有机会沦为新一轮令人笑话的CASE工具。

在继续阅读之前,读者不妨问自己一个问题:在长期的项目中,这些大词是否真的切实给你带来过好处?更为功利的问题是:你,作为战斗在一线的开发者,在长期项目中可曾有过美好的体验?

技术的演变与挥之不去的痛

企业应用的发展似乎从十年前开始腾飞。从Microsoft ASP/LAMP(Linux、Apache、MySQL、PHP)年代开始,各种企业应用纷纷向浏览器迁移。经过十年的发展,目前阵营已经百花齐放。与过去不同,现在的技术不仅仅在编程语言方面,常见的编程套路、最佳实践、方法学、社区,都是各种技术独特拥有的。目前占据主流的阵营有:

  • Rails
  • Java EE平台。值得一提的是Java VM已经成为一种新的宿主平台,Scala、JRuby更为活跃并引人瞩目
  • LAMP平台。Linux/MySQL/Apache并没有多少变化,PHP社区从Rails社区获得了不少养分,出现了许多更加优秀的开发框架
  • Microsoft .NET平台
  • Django

没有理由对这些新技术不感到振奋。它们解决了许多它们出现之前的问题。在它们的网站上都宣称各种生产效率如何之高的广告语,类似于15分钟创建一个博客应用;2分钟快速教程等等。比起过去21天才能学会XXX,现在它们在上手难度上早已大幅度降低。

需要泼冷水的是,本文开篇提出的问题,在上述任何一种技术下,都如幽灵般挥之不去。采用Ruby on Rails的某高效团队在10人团队工作半年之后,构建时间从当初的2分钟变成2小时;我们之前采用Microsoft .NET 3.5 (C# 3.0)的一个项目,在产生2万行代码的时候,构建时间已经超过半小时;我们的一些客户,工作在10年的Java代码库上——他们竭尽全力,保持技术栈与时俱进:Spring、Hibernate、Struts等,面对的困境是他们需要同时打开72个项目才能在Eclipse中获得编译;由于编译打包时间过长,他们去掉了大部分的单元测试——带来巨大的质量风险。

如果你真的在一个长期的项目工作过,你应该清楚地了解到,这种痛苦,似乎不是任何一种框架能够根本性解决的。这些新时代的框架解决了大部分显而易见的问题,然而在一个长期项目中所面对的问题,它们无能为力。

一步一步:架构是如何腐化的

无论架构师在任何时代以何种绚丽的方式描述架构,开发中的项目不会超出下图所示:

基本架构示意

一些基本的准则:

  • 为了降低耦合,系统应当以恰当的方式进行分层。目前最经考验的分层是MVC+Service。
  • 为了提供基础的访问,一些基本的、平台级别的API应该被引入。用Spring之类的框架来做这件事情。
  • 用AOP进行横向切分业务层面共性的操作,例如日志、权限等。
  • 为了保证项目正常构建,你还需要数据库、持续集成服务器,以及对应的与环境无关的构建脚本和数据库迁移脚本。

阶段1

满足这个条件的架构在初期是非常令人愉悦的。上一部分我们描述的框架都符合这种架构。这个阶段开发非常快:IDE打开很快,开发功能完成很快,团队这个时候往往规模较小,交流也没有问题。所有人都很高兴——因为用了新技术,因为这个架构是如此的简单、清晰、有效。

阶段2

好日子不算太长。

很快你的老板(或者客户,随便什么)有一揽子的想法要在这个团队实现。工作有条不紊的展开。更多的功能加入进来,更多的团队成员也加入了进来。新加入的功能也按照之前的架构方式开发着;新加入的团队成员也对清晰的架构表示欣喜,也一丝不苟的遵循着。用不了多久——也许是三个月,或者更短,你会发现代码库变成下面的样子:

正常开发之后

你也许很快会意识到这其中有什么问题。但你很难意识到这到底意味着什么。常见的动作往往围绕着重构——将纵向相关的抽取出来,形成一个新的项目;横向相关的抽取出来,形成一个名叫common或者base的项目。

无论你做什么类型的重构,一些变化在悄悄产生(也许只是快慢的不同)。构建过程不可避免的变长。从刚开始的一两分钟变成好几分钟,到十几分钟。通过重构构建脚本,去掉那些不需要的部分,构建时间会降到几分钟,你满意了,于是继续。

阶段3

更多的功能、更多的成员加入了。构建时间又变长了。随着加载代码的增多,IDE也慢了下来;交流也多了起来——不是所有人能够了解所有代码了。在某些时候,一个很有道德的程序员尝试重构一部分重复逻辑,发现牵涉的代码太多了,好多都是他看不懂的业务,于是他放弃了。更多的人这么做了,代码库越来越臃肿,最终没有一个人能够搞清楚系统具体是怎么工作的了。

系统在混乱的状态下继续缓慢地混乱——这个过程远比本文写作的时间要长很多,之间会有反复,但据我观察,在不超过1年的时间内,无论采用何种技术框架,应用何种架构,这个过程似乎是不可抗拒的宿命。

常见的解决方案

我们并非是坐以待毙的。身边优秀的同事们在问题发现之前采取了各种解决方案。常见的解决方案如下:

升级工作环境

没有什么比一台与时俱进的电脑更能激励开发人员了。最多每隔三年,升级一次开发人员的电脑——升级到当时最好的配置,能够大幅度的提升生产效率,激励开发人员。反过来,利用过时的电脑,在慢速的机器上进行开发,带来的不仅仅是客观上开发效率的降低,更大程度上带来的是开发人员心理上的懈怠。

升级的工作环境不仅仅是电脑,还包括工作的空间。良好的,促进沟通的空间(以及工作方式)能够促进问题的发现从而减少问题的产生。隔断不适合开发。

分阶段的构建

一般而言,构建的顺序是:本地构建确保所有的功能运行正常,然后提交等待持续集成工作正常。本地构建超过5分钟的时候就变得难以忍受;大多数情况下你希望这个反馈时间越短越好。项目的初期往往会运行所有的步骤:编译所有代码,运行所有测试。随着项目周期的变长,代码的增多,时间会越来越长。在尝试若干次重构构建脚本再也没办法优化之后,“分阶段构建”成为绝大多数的选择。通过合理的拆分、分层,每次运行特定的步骤,例如只运行特定的测试、只构建必要的部分;然后提交,让持续集成服务器运行所有的步骤。这样开发者能够继续进行后续的工作。

分布式构建

即便本地快了起来,采用分阶段构建的团队很快发现,CI服务器的构建时间也越来越让人不满意。每次提交半小时之后才能得到构建结果太不可接受了。各种各样的分布式技术被创建出来。除了常见的CI服务器本身提供的能力,许多团队也发明了自己的分布式技术,他们往往能够将代码分布到多台机器进行编译和运行测试。这种解决方案能够在比较长的一段时间内生效——当构建变慢的时候,只需要调整分布策略,让构建过程运行在更多的集群机器上,就可以显著的减少构建时间。

采用JRebel或者Spork

一些新的工具能够显著地提速开发人员的工作。JRebel能够将需要编译的Java语言变成修改、保存立即生效,减少了大量的修改、保存、重新编译、部署的时间;Spork能够启动一个Server,将RSpec测试相关的代码缓存于其中,这样在运行RSpec测试的时候就不用重新进行加载,极大提升了效率。

到底是什么问题?

上述的解决方案在特定的时间域内很好地解决了一部分问题。然而,在项目运转一年,两年或者更久,它们最终依然无法避免构建时间变长、开发变慢、代码变得混乱、架构晦涩难懂、新人难以上手等问题。到底问题的症结是什么?

人们喜欢简洁。但这更多的看起来是一个谎言——没有多少团队能够自始至终保持简洁。人们喜欢简洁只是因为这个难以做到。并不是说人们不愿意如此。很多人都知道软件开发不比其他的劳动力密集型的行业——人越多,产量越大。《人月神话》中已经提到,项目增加更多的人,在提升工作产出的同时,也产生了混乱。短期内,这些混乱能够被团队通过各种形式消化;但从长期看来,随着团队人员的变动(新人加入,老人离开),以及人正常自然的遗忘曲线,代码库会逐渐失控,混乱无法被消化,而项目并不会停止,新功能不断的加入,架构就在一天天的过程中被腐蚀。

人的理解总有一个边界,而需求和功能不会——今天的功能总比昨天的多;这个版本的功能总比上个版本的多。而在长时间的开发中,忘记之前的代码是正常的;忘记某些约定也是正常的。形成某些小而不经意的错误是正常的,在巨大的代码库中,这些小错误被忽视也是正常的。这些不断积攒的小小的不一致、错误,随着时间的积累,最终变得难以控制。

很少有人注意到,规模的变大才是导致架构腐化的根源——因果关系在时空上的不连续,使得人们并不能从其中获得经验,只是一再重复这个悲剧的循环。

解决方案

解决方案的终极目标是:在混乱发生之前,在我们的认知出现障碍之前,就将项目的规模控制在一定范围之内。这并不容易。大多数团队都有相当的交付压力。大多数的业务用户并没有意识到,往一个项目/产品毫无节制地增加需求只会导致产品的崩溃。看看Lotus Notes,你就知道产品最终会多么令人费解、难以使用。我们这里主要讨论的是技术方案。业务上你也需要始终对需求的增长保持警惕。

0. 采用新技术

这可能是最廉价的、最容易采用的方案。新技术的产生往往为了解决某些特定的问题,它们往往是经验的集合。学习,理解这些新技术能够极大程度减少过去为了完成某些技术目标而进行的必要的经验积累过程。就像武侠小说中经常有离奇遭遇的主人公突然获得某个世外高人多年的内力一样,这些新技术能够迅速帮助团队从某些特定的痛点中解脱出来。

已经有足够多的例子来证明这一观点。在Spring出现之前,开发者的基本上只能遵循J2EE模式文档中的各种实践,来构建自己的系统。有一些简单的框架能够帮助这一过程,但总体来说,在处理今天看起来很基础的如数据库连接,异常管理,系统分层等等方面,还有很多手工的工作要做。Spring出现之后,你不需要花费很多精力,很快就能得到一个系统分层良好、大部分设施已经准备就绪的基础。这为减少代码库容量以及解决可能出现的低级Bug提供了帮助。

Rails则是另外一个极端的例子。Rails带来的不仅仅是开发的便利,还带来了人们在Linux世界多年的部署经验。数据库Migration, Apache + FastCGI或者nginx+passenger,这些过去看起来复杂异常的技术在Rails中变得无足轻重——稍懂命令行的人即可进行部署。

任何一个组织都无法全部拥有这些新技术。因此作为软件从业者,需要不断地保持对技术社区的关注。闭门造车只能加速架构的腐化——特别是这些自己的发明在开源社区早已有成熟的方案的时候。在那些貌似光鲜的产品背后,实际上有着无数的失败的案例成功的经验在支撑。

我们曾经有一个项目。在意识到需求可能转向类似于key-value的文档数据库之后,团队大胆的尝试采用SQLServer 2008的XML能力,在SQL Server内部实现了类似于No-SQL的数据库。这是一个新的发明,创造者初期很兴奋,终于有机会做不同的事情了。然而随着项目的进行,越来越多的需求出现了:Migration的支持、监控、管理工具的支持、文档、性能等等。随着项目的进展,最终发现这些能力与时下流行的MongoDB是如此的相似 ——MongoDB已经解决了大多数的问题。这个时候,代码库已经有相当的规模了——而这部分的代码,让许多团队成员费解;在一年之后,大约只有2个人能够了解其实现过程。如果在早期采用MongoDB,团队本有机会摒弃大部分相关的工作。

值得一提的是,高傲的开发者往往对新技术不够耐心;或者说对新技术的能力或局限缺乏足够耐心去了解。每一个产品都有其针对的问题域,对于问题域之外,新技术往往没有成熟到能够应对的地步。开发者需要不断地阅读、思考、参与,来验证自己的问题域是否与其匹配。浅尝辄止不是好的态度,也阻碍了新技术在团队内的推广。

新技术的选型往往发生在项目/产品特定的时期,如开始阶段,某个特定的痛点时期。日常阶段,开发者仍然需要保持对代码库的关注。下一条,重构到物理隔离的组件则是对不断增大的代码库另一种解决方案。

1. 重构到物理隔离的组件

显而易见的趋势是,对于同一个产品而言,需求总是不断增多的。去年有100个功能,今年就有200个。去年有10万行代码,今年也许就有20万行。去年2G 内存的机器能够正常开发,今年似乎得加倍才行。去年有15个开发人员,今年就到30个了。去年构建一次最多15–20分钟,今年就得1个小时了,还得整个分布式的。

有人会注意到代码的设计问题,孜孜不倦地进行着重构;有人会注意到构建变慢的问题,不懈地改进着构建时间。然而很少有人注意到代码库的变大才是问题的根源。很多常规的策略往往是针对组织的:例如将代码库按照功能模块划分(例如ABC功能之类)或者按层次划分(例如持久层、表现层),但这些拆分之后的项目依然存在于开发人员的工作空间中。无论项目如何组织,开发者都需要打开所有的项目才能完成编译和运行过程。我曾经见到一个团队需要在Visual Studio中打开120个项目;我自己也经历过需要在Eclipse中打开72个项目才能完成编译。

解决方案是物理隔离这些组件。就像团队在使用Spring/Hibernate/Asp.NET MVC/ActiveRecord这些库的时候,不用将它们对应的源代码放到工作空间进行编译一样,团队也可以将稳定工作的代码单元整理出来形成对应的库,标记版本然后直接引用二进制文件。

在不同的技术平台上有着不同的方案。Java世界有历史悠久的Maven库,能够良好的将不同版本的 JAR以及他们的以来进行管理;.NET比较遗憾,这方面真正成熟的什么也没有——但参考Maven的实现,团队自己造一个也不是难事(可能比较困难的是与MSBuild的集成);Ruby/Rails世界则有著名的gem/bundler系统。将自己整理出来的比较独立的模块不要放到rails/lib /中,整理出来,形成一个新的gem,对其进行依赖引用(团队内需要搭建自己的gems库)。

同时,代码库也需要进行大刀阔斧的整改。之前的代码结构可能如下,(这里以SVN为例,因为SVN有明确的trunk/branches/tags目录结构。git/hg类似)

原来的库结构

改进之后,将会如下图所示:

改进的库结构

每个模块都有属于自己的代码库,拥有自己的独立的升级和发布周期,甚至有自己的文档。

这一方案看起来很容易理解,但在实际操作过程中则困难重重。团队运转很长一段时间之后,很少有人去关心模块之间的依赖。一旦要拆分出来,去分析几十上百个现存项目之间的依赖相当费劲。最简单的处理办法是,检查代码库的提交记录,例如最近3个月之内某个模块就没有人提交过,那么这个模块基本上就可以拿出来形成二进制依赖了。

很多开源产品都是通过这个过程形成的,例如Spring(请参考阅读《J2EE设计开发编程指南》,Rod Johnson基本上阐述了整个Spring的设计思路来源)。一旦团队开始这样去思考,每隔一段时间重新审视代码库,你会发现核心代码库不可能失控,同时也获得了一组设计良好、工作稳定的组件。

2. 将独立的模块放入独立的进程

上面的解决方案核心原则只有一条:始终将核心代码库控制在团队可以理解的范围内。如果运转良好,能够很大程度上解决架构因为代码规模变大而腐化的问题。然而该解决方案只解决了在系统在静态层面的隔离。当隔离出的模块越来越多,系统也因此也需要越来越多的依赖来运行。这部分依赖在运行期分为两类:一类是类似于 Spring/Hibernate/Apache Commons之类的,系统运行的基础,运行期这些必须存在;另外一类是相对独立的业务功能,例如缓存的读取,电子商城的支付模块等。

第二类依赖则可以更进一步:将其放到独立的进程中。现在稍具规模的系统,登录、注销功能已经从应用中脱离而出,要么采用SSO的方案来进行登陆,要么则干脆代理给别的登陆系统。LiveJournal团队在开发过程中,发现缓存的读写实际上可以放到独立的进程中进行(而不是类似EhCache的方案,直接运行于所在的运行环境中),于是发明了现在鼎鼎有名的memcached. 我们之前进行的一个项目中,发现支付模块完全能够独立出来,于是将其进行隔离,形成了一个新的、没有界面的、永远在运行的系统,通过REST处理支付请求。在另外一个出版项目中,我们发现编辑编写报告的过程实际上与报告发行过程虽然存在类级别的重用,但在业务层面是独立的。最终我们将报告发行过程做成了一个常驻服务,系统其他的模块通过MQ消息与其进行交互。

这一解决方案应该不难理解。与解决方案1不同的是,这一方案更多的是要对系统进行面向业务层面的思考。由于系统将会以独立的进程来运行这一模块,在不同的进程中可能存在一定的代码重复。例如Spring同时存在两个不相关的项目中大家觉得没什么大不了的;但如果是自己的某个业务组件同时在同一个项目的两个进程中重复,许多人就有些洁癖不可接受了。(题外话:这种洁癖在OSGi环境中也存在)这里需要提醒的是:当处于不同的进程时,它们在物理上、运行时上已经彻底隔离了。必须以进程的观点去思考整个架构,而不是简单的物理结构。

从单进程模型到多进程模型的架构思维转变也不太容易——需要架构师有意识的加强这方面的练习。流行的.NET和Java世界倾向于把什么都放到一起。而 Linux世界Rails/Django则能更好的平衡优秀产品之间的进程协调。例如memcached的使用。另外,现在多核环境越来越多,与其费尽心思在编程语言层面上不如享受多核的好处,多进程能够简单并且显著地利用多核能力。

3. 形成高度松散耦合的平台+应用

现在将眼光看更远一些。想象一下我们在做一个类似于开心网、Facebook、人人网的系统。它们的共同特点是能够接入几乎无限的第三方应用,无论是买卖朋友这类简单的应用,还是绚丽无比的各种社交游戏。神奇的是,实现这一点并不需要第三方应用的开发者采用跟它们一样的技术平台,也不需要服务端提供无限的运算能力——大部分的架构由开发方来控制。

在企业应用中实现这个并不难。这其中的秘诀在于:当用户通过Facebook访问某个第三方应用的时候,Facebook实际上通过后台去访问了第三方应用,将当前用户的信息(以及好友信息)通过HTTP POST送到第三方应用指定的服务网址,然后将应用的HTML结果渲染到当前页面中。某种意义上说,这种技术本质上是一种服务器端的mashup. (详情参考InfoQ 文章

facebook

Facebook App架构

这种架构的优点在于极度的分布式。从外观上看起来一致的系统,实际由若干个耦合极低、技术架构完全不同的小应用组成。它们不需要被部署在同一台机器上,可以单独地开发、升级、优化。一个应用的瘫痪不影响整个系统的运行;每个应用的自行升级对整个系统也完全没有影响。

这并非是终极的解决方案,只在某些特定的条件下有效。当系统规模上非常庞大,例如由若干个子系统组成;界面基本一致;子系统之间关联较少。针对这个前提,可以考虑采用这种架构。抽象出极少的、真正有效公用的信息,在系统之间通过HTTP POST.。其他的系统完全可以独立开发、部署,甚至针对应用访问的情况进行特定的部署优化。如果不这么做,动辄上百万千万行的代码堆在一个系统中,随着时间的推移,开发者逐渐对代码失控,架构的腐化是迟早的事情。

例如,银行的财务系统,包括了十多个个子系统,包括薪资、资产、报表等等模块,每一部分功能都相对独立并且复杂。整个系统如果按照这种方式拆分,就能够实现单点优化而无需重新启动整个应用。针对每个应用,开发者能够在更小的代码内采用自己熟悉的技术方案,从而减少架构腐化的可能。

结语

没有糟糕的架构,变化使之

我访问过很多团队。在很多项目开始的时候,他们花很多时间在选择用何种技术体系,何种架构,乃至何种IDE。就像小孩子选择自己钟爱的玩具,我相信无论过程如何,团队最终都会欣然选择他们所选择的,并且坚信他们的选择没有错误。事实也确实如此。在项目的开始阶段很难有真正的架构挑战。困难的地方在于,随着时间的增长,人们会忘记;有很多的人加入,他们需要理解旧代码的同时完成新功能;每一次代码量的突破,都会引起架构的不适应;这些不适应包括:新功能引入变得困难,新人难以迅速上手;构建时间变长等等。这些能否引起团队的警觉,并且采取结构性的解决方案而不是临时性的。

关于文档

很多人说敏捷不提倡文档。他们说文档很难写。他们说开发人员写不了文档。于是就没有文档。

奇怪的是我看到的情况却不是这样。程序写得优秀的人,写起文字来也很不错。ThoughtBlogs上绝大多数都是程序员,很多人的文字写得都很赞。

而项目中的文档往往少得可怜。新人来了总是一头雾水。令人奇怪的是,新人能够一天或者两天之内通过阅读RSpec或者JBehave迅速了解这些工具的使用,到了团队里面却没有了文档。

抛开项目持续运转并交付的特性不谈,我认为巨大的、不稳定的代码库是文档迅速失效的根源。如果我们能够按照上述的解决方案,将代码库缩小,那么独立出来的模块或者应用就有机会在更小的范围内具备更独特的价值。想象一下现在的Rails3/Spring框架,他们往往有超过20个第三方依赖,我们却没有觉得理解困难,最重要的原因是依赖隔离之后,这些模块有了独立的文档可以学习。

企业级项目也可以如此。

创建应用程序的生态环境,而非单一的项目

功能总是不断的、不断的加到同一个产品中。这毫不奇怪。然而通过我们前面的分析,我们应当重新思考这个常识。是创建一个日益庞大的、缓慢的、毫无生机的产品,还是将其有机分解,成为一个生机勃勃的具有不同依赖的生态系统?项目的各方人员(包括业务用户、架构师、开发者)应当从短视的眼光中走出来,着眼于创建可持续的应用程序生态系统。

关于作者

陈金洲,Buffalo Ajax Framework作者,ThoughtWorks中国公司首席咨询师,现居西安。目前的工作主要集中在RichClient开发,同时一直对Web可用性进行观察,并对其实现保持兴趣。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我
社区评论

架构崩溃的原因 by Lin Anders

架构崩溃的原因在于如下两个方面:
1.没有专人的维护
在没有专人维护的情况下,让模块的开发人员来维护架构在技术或许可行,但在管理上是不现实的。模块开发人员需评估影响,并通知相关人员,同时进行测试和升级工作。这些需要较多的时间,而实际管理层可能只给他一天时间(解决孤立的问题本身)
2.虽然有专人的维护,但维护人不承认架构已经不适应新情况
这种情况更可怕,维护人由于“工作的重要性”而成为高高在上的“架构师”,远离开发一线,并且不承认现状,将出现的问题归咎于开发人员的责任心或者能力问题,将问题掩盖住。

-----分割线-----
之前发的是乱码了,请管理员删除吧!

分析的很有道理 by niu Vniu

其中太多的架构选型也是其中之一,标准太多。

模块化很难把握 by 王 凯

原因1.什么时候模块化,时机很难把握
2.模块化得风险很难评估

浮躁的社会 浮躁的人 by geng gavin

RT

奇怪的是我看到的情况却不是这样。程序写得优秀的人,写起文字来也很不错。ThoughtBlogs上绝大多数都是程序员,很多人的文字写得都很赞。 by helen wu

Agree!
Clear thinking results in clear writing.

有很多的人加入,他们需要理解旧代码的同时完成新功能;每一次代码量的突破,都会引起架构的不适应;这些不适应包括:新功能引入变得困难,新人难以迅速上手;构 by helen wu

有很多的人加入,他们需要理解旧代码的同时完成新功能;
True
每一次代码量的突破,都会引起架构的不适应;
Not necessary
这些不适应包括:新功能引入变得困难,
Either the architecture is not good enough for the new function to be added
Or the new programmer does not understand the architecture.
Newer technology brings new opportunity to change. They are not the key for architecture corruption/corrosion.
新人难以迅速上手;构建时间变长等等。这些能否引起团队的警觉,并且采取结构性的解决方案而不是临时性的。

Re: 有很多的人加入,他们需要理解旧代码的同时完成新功能;每一次代码量的突破,都会引起架构的不适应;这些不适应包括:新功能引入变得困难,新人难以迅速 by helen wu

I meant, the new technology is not the key for solving architecture corrosion problem.

架构腐化根本原因 by 景 志刚

“建立新的概念”和“修正已有的概念”,是人们走在认识客观世界道路上的两条腿。传统的开发语言、工具、开发框架甚至架构几乎都是围绕“建立新的概念”这支腿的展开的。“修正已有的概念”这支腿几乎被人们忽略了。这不仅仅是架构腐化根本原因,而且是许多昙花一现的新技术的腐化原因。

学习之 by yang Michael

学习之

Re: 有很多的人加入,他们需要理解旧代码的同时完成新功能;每一次代码量的突破,都会引起架构的不适应;这些不适应包括:新功能引入变得困难,新人难以迅速 by 古 宏

其实很简单,就是一个系统逐渐庞大的过程:重复代码封装成子函数->封装成类->封装成动态链接库->组件化各个功能模块。

架构腐败解决之道 by 景 志刚

用户可以感知是真正的企业架构与一般应用开发框架的分水岭。


企业应用是典型的有大量人(用户)和大量应用功能的系统。


凡是有大量人(用户)和大量应用功能的系统架构,必须包含人(用户)或者他们的委托人可以感知到架构的体系和功能。

问题总结很到位,解决方案概括为一个词: SOA 治理 by october leo

"系统在混乱的状态下继续缓慢地混乱——这个过程远比本文写作的时间要长很多,之间会有反复,但据我观察,在不超过1年的时间内,无论采用何种技术框架,应用何种架构,这个过程似乎是不可抗拒的宿命。"
我们公司的系统就是这种状态。

防止架构腐化:
1) 及时的重构
2) 及时的模块化 --- Maven是不二选择,甭用ant+ivy,半截子方案
3) 及时的面向service的垂直切割 --- ebay数据库的设计采用的即是此方案,从不使用join。各个domain完全切分,可以选择物理隔离,也可以合并部署。
4) 及时的引入异步系统 --- Message Queue 使系统运行时解耦,带来了更多的灵活性和伸缩性。

除了分解,还有一些最重要的因素 by Tseng Joseph

作者的观点简言之就是在规模扩大时没有适当适时分解。但后面的原因呢?难道都是维护架构演进的负责人傲慢或者愚蠢吗? 这样推断当然省事,但真的不解决问题。

我的观点,架构适应性大部分是水面下的“内部质量”,在内部质量不怎么可见,在外部质量(需求符合度)还凑合的情况下,项目投资者不太愿意投入更多资源在维护架构上,架构演进的负责人需要克服这种倾向,需要清晰的理念,坚定的执行,和有效说服的技巧。

很多情况更糟,项目或产品的外部环境就决定了这个项目或产品的短期行为。那些为政府镀金的项目哪需要什么长期维护啊?到时候重新做一个还能重复赚钱呢。按人月付费的项目也没有改良的冲动,最好产品需要更多人维护,只要糊弄得了客户,或者糊弄得了客户的上头就可以了。上述这些项目的架构负责人,需要多大的理想主义的支持,才能做好架构的维护呢?

Re: 除了分解,还有一些最重要的因素 by Tseng Joseph

(接上),所以最初要弄清楚对于架构的真正的需求: 假设是不需要长期维护的架构,就别折腾吧,一个支持事务脚本的贫血模型,包含一切的大项目,一堆lib方式复用的组件,一队毕业生劳力,足矣。架构的腐化就忍受吧。 精力放在理解业务和搞定客户上,更有利于个人价值的增值。如果不能忍受,请考虑去另外类型的项目组吧。

Re: 除了分解,还有一些最重要的因素 by Chen Michael

同意你的观点,人的因素、项目本身的因素确实让人沮丧。但这不是本文表达的观点。这篇文章主要讨论的是在每个人都想把系统做好的主观意愿前景下,为什么架构依然无法跟上变化。更多的是一种提醒,而非指责团队没有尽到最大努力。

总结很不错,“规模扩大时没有适当适时分解”。但意识到这一点并不容易,认识到这一点会给项目带来长久的影响更不容易。很长起的架构失效过程的演变,就是为了让架构维护者意识到,适时分解的重要性,以便为长期的计划打下基础。

语义的不断变化 by Zhu Felix

整个设计建模阶段至关重要的一点就是语义(概念、职责)与事实逻辑分割的mappping。

随着项目规模的变化,赋予给个组件的语义发生变更之时,没有及时的调整组件的逻辑划分,于是发生mismatch,最终导致腐化。

Re: 架构崩溃的原因 by fan lu

我觉得第2条很可能,护犊子嘛

很好的文章 by 熊 军

非常一见地!
架构师应该该参考一下地球架构。

Re: 架构腐化根本原因 by helen wu

Can you differentiate the two from the design and implemention point of view?
I think, if the existing architecture cannot support the latter, it won't be able to support the former likely. Gu Huong has stated what is needed for a change at architecture level. Tseng Joseph has pointed out that the result of any necessary changes may not be worthy of the effort of such changes. The only difference I can see is whether the both can be accomendated by changes at the architecture level easily or not. Good architecture is able to sustain changes better than bad one.

Re: 架构崩溃的原因 by helen wu

You mean the owners reluctant to change their own design?!

Re: 语义的不断变化 by helen wu

Changes are inevitable. The question is whether the budget allows such changes or not.

咨询做多了感慨自然多 by Bright Zheng

Michale咨询项目做多了,感慨不少嘛:)
这次谈的更多关注IT架构、资产管理方面的思考,有感而发之余,还是比较深入的,感谢分享。

事实上,现在没有多少IT公司版本管理清晰,架构发展顺畅——无论这个公司怎么包装怎么光鲜,进去了之后你就会发现到处都是虫子。这种问题简直是避无可避,更有甚者,这会逐步形成一种管理惰性——甚至可称之为负面企业文化。

到了最后,简直就是头痛医头脚痛医脚,或者沦为一两个Rambo型的人物的超级玩具——完全不可捉摸,也害怕触碰。
说到底还是管理的坚持性和韧性问题,任何简单的问题一旦放任,都会逐步变成超级怪物

下次希望也谈点积极点的,哈哈

Re: 咨询做多了感慨自然多 by Bright Zheng

Michael, sorry for my typo.

Re: 咨询做多了感慨自然多 by helen wu

@zhengbright:可称之为负面企业文化?
任何事物都有生有死,这是事物正常的新陈代谢过程,软件生命可长可短,架构腐化是通向死亡的必然!不是吗?

Re: 除了分解,还有一些最重要的因素 by F Mic

非常同意你的观点,投资者往往关心的是外部质量,内部质量很少有人去关心。没有review,没有重构,再好的架构随着时间的推移也会慢慢塌陷。

架构腐化伴随着维护者的崩溃 by 果 林

需要同时打开72个项目才能在Eclipse中获得编译

可悲的是,我们是209个项目,死命的将项目关掉,要把编译通过至少也需要百来个项目。

Re: 架构腐化伴随着维护者的崩溃 by Lee Vincent

需要同时打开72个项目才能在Eclipse中获得编译
可悲的是,我们是209个项目,死命的将项目关掉,要把编译通过至少也需要百来个项目。

需要同时打开72个项目才能在Eclipse中获得编译? 什么,还有209个的?
你们的意思是说 这些项目靠Eclipse的工程依赖进行编译的?
悲剧啊!
可能是架构师们只顾着关注那些牛掰的架构问题去了,对于项目开发中的各种卫生死角、环境脏乱差等小事儿没兴趣吧,
所以他们总是不明白,为何自己设计的光鲜架构总是变成豆腐渣工程。

简洁是一种习惯 by Guo Ford

可以说,要有“洁癖”的team,才可能生产出一个不腐化的架构。

Re: 简洁是一种习惯 by helen wu

不腐化的构架是能够随着需求变化而变化的构架,容易适应变化的构架可称好构架。简洁需要训练有素,随需求变化而变化需要资源。资源不足构架自然要腐化。

好评 by 孙 小卒

文章写的很不错,主题鲜明、思路清晰!

关注 by kong jie

写的很好,看完了,虽然还没有接触比较大的项目,但是描述的场景可以理解!

一针见血 by Tai Wang

讲的太好了,不愧是高手啊

re by 冰雪 飞舞

写的真棒,有些项目确实文中所述,但从来没有总结过,高手就是高手,学些了

都是需求的错 by 曹 力文

一切都是需求添加的错,请你原谅我。

谢谢分享 by zhiwen liu

谢谢楼主的分享,学习,期待楼主的更多作品

解耦 by shi andy

想要实现项目的分拆,架构的分拆,实现模块的解耦,彻底的解耦,不仅仅是代码的解耦。是那种可以独立升级,独立维护,独立部署的解耦。是一种调用关系的解耦,通信解耦。不能项目之间进行引用,模块之间的通信要使用JSON,或者是REST,之类的基于某种格式的的通信。

文章看了四遍了,才有了一点点的体会!!感觉到进步,欣喜中。。。

人类思维和软件工程学 by Chen David

关于架构演化的很深刻的一篇总结分析文章。

最初提到的AOP的概念让我重新去维基百科 zh.wikipedia.org/wiki/面向侧面的程序设计 仔细看了一下,AOP作用的范围其实是可以远超过"日志和权限"的,说白了就是业务之外的依赖技术才是难点。

发现我之前写的《人类思维和软件工程学》 mvj3.github.io/2013/12/15/human-mind-and-softwa... 里的提到的三间段已经被 @mechilandx 这篇两年前的《架构腐化之谜》 形式上做了类似的说明了。

我把他对应的解决方案重新理解一下,"采用新技术"理解为"针对业务来说更精简的新技术", "重构到物理隔离的组件, 将独立的模块放入独立的进程"理解为"开源和Unix工具化pipe", "形成高度松散耦合的平台+应用"理解为"资源调用管理的操作系统式开发"。

和"关于文档"提到的"想象一下现在的Rails3/Spring框架,他们往往有超过20个第三方依赖,我们却没有觉得理解困难,最重要的原因是依赖隔离之后,这些模块有了独立的文档可以学习。"一样,开源是把系统架构解耦和模块化的最好手段。

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

37 讨论
提供反馈
错误报告
商务合作
内容合作
Marketing
InfoQ.com及所有内容,版权所有 © 2006-2016 C4Media Inc. InfoQ.com 服务器由 Contegix提供, 我们最信赖的ISP伙伴。
北京创新网媒广告有限公司 京ICP备09022563号-7 隐私政策
BT