代码重构的实战经验和那些坑

为什么我们觉得有必要从头重写软件呢?

在第一次编写系统代码时,我们的时间表十分紧迫,必须与时间赛跑,在计划时间内赶完进度。因此无论是设计讨论,还是审查会议都没花太长时间——我们没有时间浪费在这上面——只能匆匆完成一个功能、快速测试,然后赶着去做下一个。我们与别的公司共享办公空间,我还记得其他公司的软件开发都会花很长时间做设计、讨论架构,再花上数周讨论设计模型。

除了设计仓促,原本的系统写得不差,总体来说架构也不错。其中有些意大利面条式的代码,是公司之前做概念验证时留下的,因为这些代码能用,再加上工期紧张,当时我们没有去碰。但后来我们不考虑执行优化改进,却决定要从头重写代码的原因在于:

  • 老旧代码很糟糕,很难维护;
  • “单一整体式的java架构”对我们的未来发展不利,无法支持有6千万移动用户以及多站点部署的大型运行商;
  • 我想要尝试炫酷的新技术,比如Apache Cassandra、虚拟化技术、二进制协议、SOA等等。

结果很不幸:我们说服了全公司以及董事会,实现了愿望。

代码重写之旅

正式的开发时间是从2012年春天开始的,我们将2013年1月末设定为发布时间。由于计划太过庞大,我们需要更多的人,于是在印度聘请了顾问与几个远程开发者。但是,我们没有充分预期到维护原本系统、进行新的开发工作与理解客户需求这些并行起来的工作量。

还记得我在文章最开始说过,我们有一个真实客户么?这位客户是南美最大的移动运营商之一。在我们开发的系统投入使用后,他们开始对变更和新功能提出要求,因此我们只能继续更新原来的系统。但是,由于这个系统将会被废弃,在更新时我们总有些敷衍了事,尽可能找借口拒绝了客户许多的新功能需求。结果导致了工期拖延,没能在原定的deadline完成进度。事实上,我们的进度拖延了整整8个月。

不过我们还是先说说结果吧:当项目终于完工时,新系统看起来非常棒,满足所有需求。我们做了负载测试,结果显示新系统能很容易地支持超过1亿的用户,配置集中,查看图表的UI工具也很美观,是时候废弃旧系统,改换新系统了……

但是客户拒绝了升级的请求:原本的系统已经获得了广泛应用,他们的用户已经开始依赖旧系统了,他们完全不想冒风险。长话短说,浪费了几个月之后我们收效甚微。该项目正式宣告失败。

我们学到的经验

  • 大多情况下都不该从头重写代码。我们重写代码的原因是错误的,尽管一部分代码不怎么样,如果我们花些时间阅读理解源代码,就可以通过重构修复问题。对架构的可扩展性及性能我们确实有顾虑,担心它无法支持复杂的业务逻辑,但完全可以逐步进行修复。
  • 从头重写系统对用户来说没有价值。新技术与流行词对工程师团队来说看似很酷,但如果不能按照客户需求提供新的功能,则毫无意义。
  • 我们在集中精力重写代码时,错过了真正的机会。以前我们给客户提供了一个非常基本的“站点分析工具(Web Tool)”供他们查看图表与报告。但随着内容越来越多,他们开始要求增加新功能,比如实时图表、访问级别等等。由于我们不打算继续沿用旧代码,时间也不足够,就敷衍着要么拒绝了新需求,要么凑合了事。结果最后客户不再使用这个工具了,他们坚持通过邮件来发报告。本来我们还有另一个机会来构建一个有紧迫需求的强健分析平台。
  • 我低估了在维护旧系统的同时,开发新系统所需要的工作量。我们原本预计一个月能有3到5次需求,结果却是预计数量的三倍。
  • 我们以为:由于没能花上数日讨论合适的设计模型与实例,代码就会很难阅读与维护,结果事实上我在较大公司所见过的最专业代码,比我们的代码还要糟糕两倍。因此,在这一点上我们大错特错。

何时需要重写代码

Joel Spolsky强烈反对重写代码,他建议大家都不要这样做。不过我不是特别认同:有时候逐步优化与重构非常困难,唯一读懂代码的方式就是重写。此外软件开发人员喜欢编写代码,创造新东西——阅读别人写的代码,尝试理解他们的代码与“思维抽象”会很无聊。不过,优秀的程序员也是优秀的维护者。

如果你想要重写代码,一定要出于正确的理由,并有着合适的计划。比如:

  • 有时候在发布新版很久之后,老旧代码仍需维护,维护两个版本的代码需要耗费大量工作,在开始重写前请根据项目规模评估所需的时间与资源。
  • 想想其他失去的机会,并比较任务的优先级。
  • 重写大型系统比小型系统风险更高,考虑一下能否逐步重写。我们同时执行了以下几项工作:切换到新的数据库、使用“SOA”架构、更换为二进制协议,其实本可以逐步执行这些更换。
  • 考虑开发者的偏见。在开发者想要学习新技术或新语言的时候,他们会想要使用这些来重写某些代码。不过我不反对这样做,这也是良好环境与文化的标志,但应当将它与风险和机遇做比较。

Michael Meadows对何时有需要进行“大型”重写有着很好的看法

技术上

  • 组件的耦合度很高,无法单独对某个组件进行修改。重新设计单个组件会导致一连串的变化,不仅会影响到相邻的组件,甚至间接影响到所有的组件。
  • 技术堆栈太过复杂,未来状态设计需要变更很多的基础架构。出于这个原因执行完全重写十分必要,逐步重新设计在这种情况下没有优势。
  • 重新设计单个组件无论如何都会导致对该组件的重写,在现有设计中没有可以插入新功能的地方。这种情况下逐步重新设计没有优势。

政策上

  • 赞助商无法理解逐步重新设计需要对项目进行长期投入。不可避免的是:大多数公司对于在逐步重新设计上继续耗费预算没有兴趣。在完全重写代码时,这种现象也很难避免,但赞助商更愿意继续投入,因为他们不想用着半成品的新系统与部分过时的旧系统。
  • 系统用户更习惯使用“原本的界面”:在这种情况下,政策上不会允许修改系统的重要部分(前端)。但如果完全从头开始重写,则会绕过这个问题。用户还会坚持使用“相同的界面”,但这次你反击的理由更为充足。要记得:逐步重新设计的总成本总是要高于完整重写代码,但一般来说对企业的影响更小一些。在我看来,如果重写理由充足,公司又有超级优秀的开发者,那么就开工吧。

放弃正在开发的项目很危险:浪费大量的时间和金钱重复实现已有功能,同时还会放弃实现新功能的机会,有可能激怒客户并导致工作计划推迟。如果你正在重写代码,那是你的权力,不过请确保这么做的理由正确,同时了解风险也做了相关计划。

原文链接:http://www.colotu.com/html/gcs/49.html

时间: 2024-09-30 14:18:34

代码重构的实战经验和那些坑的相关文章

我的代码重构经验

说明 本文在<MDU某产品OMCI模块代码质量现状分析>一文的基础上,分享作者对该模块进行重构时的实践经验. 具体的重构手段可参考<代码大全2>或<重构:改善既有代码的设计>,本文不再班门弄斧,而侧重重构时一些粗浅的“方法论”,旨在提高重构效率. 作者未采用重量级的重构工具,仅用到Source Insight的”Smart Rename”功能.也未使用CUnit等单元测试工具,而是通过在线调测和自动化测试保证代码的正确性. 一 背景 MDU系列产品从他处接手,OMCI模

mysql触发器的实战经验-不错的文章

1   引言Mysql的触发器和存储过程一样,都是嵌入到mysql的一段程序.触发器是mysql5新增的功能,目前线上凤巢系统.北斗系统以及哥伦布系统使用的数据库均是mysql5.0.45版本,很多程序比如fc-star管理端,sfrd(das),dorado都会用到触发器程序,实现对于数据库增.删.改引起事件的关联操作.本文介绍了触发器的类型和基本使用方法,讲述了触发器使用中容易产生的误区,从mysql源码中得到触发器执行顺序的结论,本文最后是实战遭遇的触发器经典案例.没有特殊说明时,本文的实

转有关代码重构

转自:http://blog.csdn.net/weiky626/article/details/1602691 开发人员可能听到过"bad smell"这个词,这个词用在软件编码中是什么意思呢? 代码还有smell吗?当然没有,正如计算机病毒,bug等词一样,这只是个形象的说法.这个词在这里的意思是代码实现了需求,但是代码却不精炼,冗余,结构混乱,难读懂,难维护,难扩展等等.与之相对应的一个词是"refactor",即代码重构.我们在看些外国人写的程序时可以发现,

EXTJS项目实战经验总结一:日期组件的change事件:

1  依据选择的日期,加载相应的列表数据,如图:   开发说明    1 开发思路: 在日期值变化的事件中获得选择后的日期值,传给后台,然后从后台加载相应的数据 2 问题:在查看extjs2.2 的api的官方说明文档,文档对datefield组件的change事件说明如下: change : ( Ext.form.Field this, Mixed newValue, Mixed oldValue )       Fires just before the field blurs if the

谈谈代码重构

开发者可能听到过"bad smell"这个词,这个词用在软件编码中是什么意思呢? 代码还有smell吗?当然没有,正如计算机病毒,bug等词一样,这仅仅是个形象的说法.这个词在这里的意思是代码实现了需求,可是代码却不精炼,冗余,结构混乱,难读懂,难维护,难扩展等等.与之相相应的一个词是"refactor",即代码重构.我们在看些外国人写的程序时能够发现,他们的代码里通常会定义大量的类.接口.方法,类与类,类与接口之间非常多是继承和实现的关系,方法的代码行数非常少,超

ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(十一) 代码重构使用反射工厂解耦

前言 自从此博客发表以及代码开源以来,得到了许多人的关注.也没许多吧,反正在我意料之外的.包括几位大牛帮我做订阅号推广,真的很感谢他们.另外,还有几个高手给我提了一些架构上的问题.其实本身这个项目是没有做什么架构设计的.只是简单分了分层.不过我在经过仔细思考之后决定对项目架构做些调整,当然在我的技术范围之内,我相信还会有第二次,第三次甚至更多重构,我希望把他变得更加完美. 重构思路 对于重构思路,我首先想到的是,让程序能够支持多种数据库,比如我现在用的是SQLServer,而好多朋友用MySQL

NET代码重构

记一次.NET代码重构 好久没写代码了,终于好不容易接到了开发任务,一看时间还挺充足的,我就慢慢整吧,若是遇上赶进度,基本上直接是功能优先,完全不考虑设计.你可以认为我完全没有追求,当身后有鞭子使劲赶的时候,神马设计都是浮云,按时上线才是王道,毕竟领导是不会关注过程和代码质量的,领导只看结果,这也许就是我等天朝码农的悲哀. 需求:是这样的,要开发一个短信发送的模板,不同客户可能会使用不同的模板,而不同的客户使用的变量参数也是不同的.之前为了应急,线上已经完成了一个短信模板发送短信的功能,短信模板

我们需要循序渐进的代码重构

对于如何进行代码重构,一直有着很多种说法.很多人都认为应该将重构代码放在backlog里.但是其实,这并不是一个理想的方法. 在项目刚刚开始的时候,你的代码很干净. 即使有的时候需要小小的绕一下路,但是这个时候我们可以轻松.平稳的添加功能.这个阶段一般都不会出现问题,而且由于我们比较着急,所以即使出现了一些小问题,我们也不会注意到. 然而,随着项目做的时间变长,这些小的问题就会累计起来.这就是人们所说的"技术债务".其本质,就是并不算特别好的代码,但是这个时候其问题还没有完全显现出来.

代码重构规则

下面个人对于代码重构时根据实战后的感受做的总结: 重构目的:重构的目的就是让代码无需注释,别人也能看懂.读懂. 重构原则: 1.去除重复代码 2.若不理解一个方法,则将其分解为更小部分,并为它们取适当名称 3.尽量让你的公共处理方法.函数只做一件事情 4.公共处理方法.函数追求短小.精悍 5.易读/易理解的代码要优于性能更好的代码,简单.易懂的代码是最终目的 参考文章: 重构规则-http://www.cnblogs.com/zhangdx/archive/2012/11/21/2780025.