编程感悟:为什么要重写代码?
码农的绝大部分工作精力是在维护代码上,至少在我所工作的部门是这样的。由此观点出发,码农界形成了如下共识:
代码首先是写给人看的。
这就要求码农们在编码时,可读性/可维护性要放在重要的位置进行考量,使用简单的设计,通用的方法,统一的风格。
每个码农心目中都有一个代码的伊甸园,在那里,代码的设计良好,实现优雅,赏心悦目。代码所要表达的意思很明晰,注释恰到好处,无需猜测就能理解编写者的意图。即使有Bug出现,通过检查代码就能容易找到错误处,无需复杂的调试过程。不再惧怕需求的增加和变化,因为代码有很好的可扩展性。
但实际上,绝大部分码农维护的代码,却是两外一番景象:代码质量惨不忍睹,错误百出,码农面对蜂拥而至的Bug和新需求,加班加点,疲于奔命。不得不承认,码农是追求完美的一个群体,码农们怀着激动的心情,按捺不住重写的冲动,在码农心中,重新实现一套系统,将极大的提高代码质量,消灭Bug。需求变更时,也不再惧怕,因为轻而易举就可修改和扩展,做到了拥抱变化。码农时不时的幻想,系统如果重写过,以后的维护工作是多么的幸福和美好。实际上,每一个码农都有、或至少有过这样的冲动,我们不得不深入思考一下其原因,重写真的能带来想象中的美好结局吗?
1,客观原因
经过前辈开发者们前仆后继呕心沥血的堆砌,摆在我们面前的代码库,就如同瓦力在末日世界执着搭建的垃圾大厦,表面上看非常整齐,仔细看去,就是一块块垃圾拼凑而成,代码库中充斥着烂代码。
代码质量
好的代码大致相同,烂的代码各式各样。随便举一些例子:
- 设计复杂,或者干脆没有设计,无法理解
- 耦合严重,各个模块、功能间相互交织,无法分离
- 代码重复,逻辑重复严重的代码,遍地Ctrl+C、Ctrl+V的代码
- 风格杂乱,前辈码农前仆后继,都留下自己独有的代码性格和印记
- 废弃代码,有用,或者没用,代码就在那里,只增不减
- 注释失效,过时的注释不是好注释,误导码农的注释是流氓注释
一般认为,代码质量差,是产品Bug频发的罪魁祸首。任何代码,无论质量高低,都隐藏着Bug,质量低的代码,尤为严重。
一旦出现Bug,码农的一般反应是:这不是我写的代码,这是老代码,一直就有问题。即使Bug是自己新引入的,也可以轻而易举的找出理由:“老代码太烂了,谁改都会出错”。没错,在码农眼中,代码库中的代码是如此之烂,以至于所有的问题,都可以归罪于老代码。
敏捷传说
需求是在不断变化的,码农们为此疲于奔命。有一种传说,叫做敏捷,据说可以解决这个问题。值得一提的是,目前我司的敏捷事业取得了可喜的成果。敏捷了,就不再惧怕Bug,因为有完备的测试,把Bug消灭在萌芽状态;敏捷了,就不再惧需求的变化,因为我们拥抱变化;敏捷了,就不再惧怕开发人员的变动,因为敏捷要求每个人都是全才,要掌握;敏捷了,我们不再惧怕烂代码,因为我们掌握了重构代码的神技。
敏捷是鼓励进行代码重构,提高代码质量的。敏捷倡导,代码应该拥抱变化,为了适应新的需求和变化,应不断的重构代码。敏捷认为:好的代码质量带来好的产品质量。这就鼓励码农进行代码测试和重构,代码测试给了码农信心,重构不用再担心和犹豫。
既然提到敏捷,多说点题外话。毫无疑问,敏捷实践是软件开发的最佳实践,但是把敏捷引入到管理流程中时,一定要万分小心。敏捷不是银弹,需要认清其本质,才能利用敏捷,提高开发效率,提高产品质量。而不是为敏捷所用,反而使得团队变得“不敏捷”。
2,主观原因
重写代码是码农内心萌生的欲望,是码农对优美代码的追求的正面体现。重写代码可能的动机有哪些呢?
- 代码洁癖,代码整洁很重要,但是过犹不及
- 优雅强迫症,设计要优雅,要抽象,为了适应未来的变化,可是未来在哪里?
- 眼高手低,认为可以轻而易举的重写代码,把既有代码替换掉
- 先苦后甜的心里预期,认为在一番努力之后,后续的维护工作将更加轻松
- 内心泛滥的对后代码农的责任心,希望留下优秀的代码遗产,和一段传说
每个码农都有自己的代码审美观,爱美之心,人皆有之,码农希望能写出优雅的代码,维护优雅的代码,无可厚非,而且是应当鼓励的。
3,重写代码的阻力
但是代码重写这件事,至少在使用时间相对长的系统中,却鲜有发生。
(1)外部原因
领导的三个终极问题
领导和码农的相同点是,都关注代码的质量,不同点是,领导关注Bug的数量和严重程度等KPI指标,码农关心代码的好坏和优雅程度。码农认为愈优雅的代码,就带来愈高质量的产品,码农每每向领导游说,既有代码如何如何烂,重写之后如何如何能提高代码、产品的质量。领导总是很淡定,然后抛出三个终极问题:
- 会不会引入新问题?
- 对哪些功能有影响?有多大的影响?
- 工程上有多大的风险?
重写当然会引入新问题,当然会对现有功能有影响,影响有多大,码农深思许久,依然没有把握,只好郁郁而归。
这个原因,是绝大部分码农经常提起的,也是最为码农们愤慨的,仿佛没有了这个阻力,将是一片广阔的天空,可以尽情施展自己的才华。
玄机暗藏
那些年,前辈码农和Bug们决战光明顶,为了改好Bug,使出了浑身解数。在烂代码上改Bug,用更烂的方式去修改,是一种快速讨巧,深受领导喜爱的方式。因为,方法虽烂,但是很好的解决了当前问题。事实证明,使用烂上加烂的方式,解决了Bug,而没有引入新问题,没有影响到既有的功能,将工程的风险控制到最小,圆满的回答领导的三个终极问题。
每个改过无数Bug的码农,都素有谦卑之心,深藏功与名,不着痕迹,以至于后代码农看到一段让人惊讶无比的代码,却不知这里暗藏玄机,封印了系统的大Bug。代码也成了一个宝藏,富含前辈码农积累下来的宝贵财富,可是隐藏的太深,无数的淘金者都失败而归。这些隐藏的知识宝藏,逐渐的成为历史,慢慢的,历史成为传说,传说成为神话。
在冒冒失失乱改一通,载了跟头,被领导臭骂之后,我们这些后世码农,在审视和修改代码时,始终怀着敬畏之心。时常用身临其境的代入感,去感受前辈码农内心的纠结和矛盾,体会他们审时度势,不得不忍痛写出如此令人不解的代码时的复杂心情。于是我们处处谨慎,不敢随意改动,总是左思右想,谨防唤醒沉睡中的Bug Boss。
(2)内部原因
1984
工程风险、领导的要求,担心改出Bug,码农被重重包围,工作中每一步都慎之又慎,不敢越雷池半步。注意!老大哥一直在盯着你!
烂代码不但带来糟糕的产品质量,也带来大的代码变更成本,修改一句代码往往都意味着工程风险,领导的责骂,自身的惶恐。更严重的是,烂代码将一步步浸染开发者追求“美”的初心。为了熟悉业务,不得不顺着既有代码的思路;为了新增功能,不得不堆砌更多的烂代码;为了解决Bug,不得不浸泡在烂代码中,无法自拔。潜移默化是最厉害、最彻底的变化方式,不知不觉间,开发者找到了成本最低的工作方法:以烂代码的思考方式解决问题,堆砌更多的烂代码。
失去了“初心”的码农,一边在挣扎,一边继续沉沦,沉沦于“舒适区”。码农已经完全忘却了曾经拥有的“初心”,没有了挣扎,并在彻夜未眠终于解决一个故障后露出了欣慰的笑容。终于,码农没有了抱怨,他在享受。
代码江湖
代码就是江湖。码农是江湖中行走的剑客,剑客们心怀天下,行侠仗义、快意恩仇,梦想着有朝一日一统江湖。可是,江湖险恶,危机暗藏。初入江湖的新手剑客,学艺不精,却心比天高,不畏艰险。浪迹江湖多年的老道剑客,往往知道水深而不敢涉险,因为他们已渐渐读懂江湖。
新手剑客,让其路见不平,拔刀相助是一件很容易的事情;但是让其谦虚谨慎,静待时机却非易事。仗剑江湖比读懂江湖要来的容易,这是一个知难行易的典型例子,我们也喜欢这样的故事。码农何尝不是?新手码农路见Bug,便想祭出重写之剑,可是却不愿意去读懂既有代码。
可是,重写代码真的比读懂代码容易吗?新手剑客是否已经剑术精湛,内功深厚,认清了江湖,才能仗剑江湖。新手码农是否夯实了技能,深入理解了要解决的问题,无法驾驭既有代码,便不可能重写出理想的代码。
江湖老鸟老罗,在其Rom发布会开头的发问:“大家准备好了吗?”,我们要时常问自己:对于彻底革新整个系统或者模块,真的准备好了吗?
4,总结
面对阻力时,我们的注意力往往集中在外因上,码农经常抱怨的也是,如果领导同意,代码早已经重写,而不会造成现在的这样一个烂摊子。却往往忽视了自身的原因,码农往往宁愿躲在“舒适区”内哭泣,也不愿意折腾之后满足的笑。
重写代码的冲动是应当珍视的,没有推翻陈旧的勇气和行为,就没有大的进步。可是,我们是否就可以大胆的对代码进行重写呢?对系统/模块是否真正掌握了?如何获取隐藏的知识?敏捷是不是万能的良药?测试能带来多大程度的保证?
带着这些问题,我们还需要做进一步的思考。