三生三世,亿万遗留代码

程序员的那点事2019-06-30 20:17:47

科幻大师阿瑟·克拉克说,任何足够先进的技术都和魔术无异。程序员通过编程实现需求,赋予软件生命,正如魔法一般推动技术的革新和社会的进步。然而


软件远远达不到 100% 的成功


你是否也注意到——有那么多的软件并未按照期望的那样工作,或者没有正常工作多长时间,而且近乎无法修补。


也许这不是“人为因素”的问题,

而是遗留代码的问题


1. 什么是遗留代码?

不同的人对“遗留代码”有着不同的定义,但是简而言之,遗留代码就是指因为种种原因,格外难以修正、改进以及使用的代码

Michael Feathers在他的《修改代码的艺术》一书中,提出了当我们听到“遗留代码”的时候会想到什么:

如果你也和我一样,那么大抵会联想到错综复杂的、难以理清的结构,需要改变然而实际上又根本无法理解的代码

你会联想到那些不眠之夜,试图添加一个本该很容易就添加上去的特性;

你会联想到自己是如何的垂头丧气,以及你的团队中的每个人对一个似乎没人管的代码库是如何打心底里感到厌烦的,这种代码简直让你生不如死。

你内心深处甚至对于想一想怎样才能改善这种代码都感到痛苦。这种事情似乎太不值得我们付出努力了。

2. 软件是如何演变成遗留代码的?

和其他事物一样,软件也有一定的生命周期,被创造、使用、修补,最终淘汰。软件像生物一样,如果它赖以生存的操作系统被淘汰,也会死亡。正如医生一样,软件开发者能做的最多是推迟大限的到来。

在一个程序的生命中,代码被修修改改,使得原本的设计变得脆弱,所以软件变得越来越难用。由于许多现如今编写的软件通常都是难以变更的,最后我们往往是替换代码而不是修复代码

3. 什么导致了遗留代码的产生和繁衍?

瀑布模型一度作为软件构建的主流方法,它的概念很简单,提供了 7 个独立的步骤,按照如下顺序进行。

需求分析

从对应领域的专家和潜在用户那里收集信息,从而建立一个需求文档。需求文档是一系列要求,指导我们在当前发布版本中应该实现哪些功能。功能就是软件的种种职责。

设计

接着是要根据写好的需求文档去设计软件。这些设计的形式通常是设计图例和其他表达设计思想的产出物。这并非代码,相当于另一个文档:如何构建软件的图例和描述。和蓝图不一样,蓝图是对建筑的每个细节的事无巨细的表达,但是软件的架构远谈不上精确或者全面。

实现

在设计之后是实现阶段,这个阶段代码被编写出来以满足设计。编码就是单纯地完成设计产出物中描述的设计。

集成

在所有代码编写完毕之后开始集成阶段。在集成阶段中,所有团队成员编写的代码放到一起。这通常是第一次所有代码被编译到一个计算机程序中。

测试

一旦软件集成完毕则测试阶段开始,验证软件表现是否如预期的那样。这个阶段包括对软件执行一系列的测试用于证明软件正常工作。

安装

在安装阶段,软件发布给用户。此阶段可能包括将载有程序的CD邮寄给用户或者在线上提供软件下载。

维护

最后,就是对软件进行持续维护:修复问题,添加新功能,提供更新。

在传统的瀑布开发中,为了让一个部分正常工作,所有其他部分必须都能正常工作。程序员并不知道他们的代码和系统其他部分配合得如何,直到集成阶段——发布前最后的阶段之一——所有的独立代码才组合到一起。

这种在瀑布模型开发的项目中持续累积风险的方式,和在拉斯维加斯赌博的方式有着惊人的相似。

直到最后阶段才做集成,基本上是在玩轮盘赌,而且连中十次才算赢。

在在传统的瀑布模型开发环境下,我们要等到最后才能看到一切是否正常运转,当bug出现的时候,它难以修复,非常耗时,以至于追踪修复的成本很高。软件难以维护。

4. 怎么解决因遗留代码造成的项目失败问题?


起决定性作用的是流程而非人力


<1>  敏捷

从不同的角度出发,一群成功的软件开发者努力找到了一个轻量级的软件开发流程,命名为“敏捷软件开发流程”。

何为“轻量级”?就是避免浪费,软件中的浪费可以这么理解:任何不是软件的东西,或者没有给客户产生直接价值的东西,都可以视为浪费。

“通过持续不断地及早交付有价值的软件使客户满意”,这一承诺是敏捷流程的核心。换句话说,敏捷理论摒弃了用增加流程来保证质量的方式,建议流程更加精简,好让开发者有更多的时间实施更切实际的工程实践。

敏捷理论引入了一些技术性实践,如测试驱动开发和结对编程,这些实践有助于创建可修改的,更容易部署、维护和扩展的软件。

<2> 敏捷误区

敏捷2001年就已经诞生了,但是在软件行业中依然未被广泛理解。许多组织只实施了一个或者几个简单的敏捷实践,诸如“站立式会议”和“两周冲刺”,然后就声称自己已经“敏捷”了。

单纯去掉需求说明文档而没有用软件开发者和产品负责人之间的交流取代,并不是敏捷的初衷。如果产品负责人成天所做的就是从客户那儿拿到需求说明再进行细化之后一路维护文档,那不过是瀑布模型加上一个产品负责人而已。那不是敏捷。

我们需要深入其中,去了解敏捷中隐含的精髓,并不仅仅是有一个产品负责人然后抛弃所有文档,而是要真正地将对话的主题从如何去做变为做什么和为什么这么做。

<3> 实现敏捷

有些开发者比其他的人更高效,是什么让这些卓越开发者如此出色?如果我们理解了他们所理解的,学习他们的原则和实践,那么我们也可以达到类似的卓越成效。

但是从何入手?

人们常常通过一系列“应该这样做,别那样做”的规则学习敏捷。那仅仅是学习的第一阶段,但是很多人认为只要学了一些规则便可以进行敏捷了。

像软件开发这样的复杂活动很难用一些规则定义。和其他的人类创造一样,计算机程序是某些事物的模型。我们对物理模型习以为常,但程序同样也可以是行为模型。

为了准确建模,我们首先必须了解建模的目标,同时需要理解我们有哪些建模技能或者方法。把这些方法分为两类,即原则和实践,比较便于理解。

A. 首要原则

原则帮助我们把某件事情通用化,有助于梳理知识体系。并不是所有的原则都是对等的。有些更加纯粹,比其他的更基础。可以引出其他原则的原则就是首要原则。

  1. 软件开发首要原则的例子是单一职责原则 :“修改某个类的原因有且只有一个。”

    因为类作为系统中对象的模板,意味着我们需要把它设计成代表单一事物。通过将类的职责变得单一,我们限制了这个职责和系统中其他类的交互。这让这个类变得更容易测试,更容易找到 bug,而且便于将来的扩展。

  2. 软件开发的另一个首要原则的例子是Bertrand Meyer在《面向对象的软件构建》中表述的开闭原则:“软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。”

    这意味着设计的系统应该允许在不修改原有代码的前提下轻易扩展。因为修改现有代码往往很困难,而且比编写新代码容易出错。当开发者理解并且重视“开闭原则”的时候,他们倾向于编写出更容易维护的代码以降低日后扩展的成本。

B. 实践

原则很重要,但是光有原则是远远不够的。我们同样需要在实际场景下达成原则的方式,这就是实践的作用。用严格的标准来定义实践。若要成为一个实践必须:

  • 在多数情况下产生价值;

  • 容易学习且容易传授;

  • 简单易行——简单到无需思考。

当一个实践符合这三个条件时,它就很容易在团队中推广并且使团队受益。只需要使用某个实践,然后自然而然就能够在日常工作中节约时间和精力。

下面的9个实践代表了一组极具价值的实践,它们经常被误解和误用,但对可持续的生产至关重要。

C. 9个实践

头两个是多数人在想到Scrum的时候想到的,剩下的7个是技术实践。这9个实践是为了帮助我们思考正确的软件开发方式而设计的,从而缩短发布周期,这是对传统观念的颠覆。

9个实践如下:

  1. 在问如何做之前先问做什么、为什么做、给谁做

  2. 小批次构建

  3. 持续集成

  4. 协作

  5. 编写整洁的代码

  6. 测试先行

  7. 用测试描述行为

  8. 最后实现设计

  9. 重构遗留代码

你并不需要实施全部9个实践,但必须理解实践目标。如果有其他方法避免其中某一实践解决的问题,便可以安全地替换这个实践。但是和毕加索一样,在打破规则之前必须要理解他们。

这9个实践有助于在保证软件开发流程顺利进行的同时创建可修改代码。当然也有许多其他的实践有助于编写可修改的代码,但这9个提供了一个坚实的基础,为降低构建和维护软件的成本开了一个好头。

——本文摘编自《修改软件的艺术》


《修改软件的艺术》


作者:David Scott Bernstein

译者:李满庆

定价:55.00元 / 电子书:27.5元

  • Wiki之父、设计模式和敏捷开发方法先驱 Ward Cunningham 作序推荐

  • 美国亚马逊全五星好评,深受程序员认可的代码设计与重构之道

  • 揭示高质量软件的秘密,阐述真正的敏捷开发之道

  • 作者30年软件开发经验总结

如果你是软件开发者,

你将学到一套实践方法以构建易修改的代码,因为代码在应用当中经常需要修改。

对于和软件开发者合作的管理者来说,

本书会向你展示为何引入这9个基本的实践方法,会使你的团队更加有效地交付软件,而不至于让软件演变成遗留代码。

为此,你需要的不仅仅是一份技术性的任务清单,还需要对为什么有这些实践方法以及如何实施这些实践方法有着深刻的理解。



目录

第一部分 遗留代码危机

第1章 有些事情不对劲

1.1 什么是遗留代码

1.2 顺流直下

1.3 孤注一掷

1.4 为什么瀑布模型不管用

1.5 当“流程”变成“体力劳动”

1.6 坚如磐石的管理

1.7 此处有龙

1.8 评估未知

1.9 一个充满外行人的产业

1.10 回顾

第2章 逃出混乱

2.1 混乱报告

2.2 驳斥斯坦迪什咨询集团

2.3 项目为何会失败

2.4 失败的代价

2.5 总结

第3章 聪明人,新想法

3.1 走进敏捷

3.2 小即是好

3.3 实现敏捷

3.4 艺术与技能的平衡

3.5 敏捷跨越鸿沟

3.6 追求技术卓越

3.7 总结

第二部分 延续软件生命(和价值)的9种实践方法

第4章 9个实践

4.1 专家知道些什么

4.2 守-破-离

4.3 首要原则

4.4 关于原则

4.5 关于实践

4.6 原则指导实践

4.7 未雨绸缪还是随机应变

4.8 定义软件中的“好”

4.9 为什么是9 个实践

4.10 总结

第5章 实践1:在问如何做之前先问做什么、为什么做、给谁做

5.1 不要说如何

5.2 将“如何”变为“什么”

5.3 要有一个产品负责人

5.4 故事描述了做什么、为什么做、给谁做

5.5 为验收测试设立明确标准

5.6 自动化验收标准

5.7 让我们付诸实践

5.8 总结

第6章 实践2:小批次构建

6.1 更小的谎言

6.2 学会变通

6.3 控制发布节奏

6.4 越小越好

6.5 分而治之

6.6 更短的反馈回路

6.7 提高构建速度

6.8 对反馈做出响应

6.9 建立待办列表

6.10 把用户故事拆分为任务

6.11 跳出时间盒子思考

6.12 范围控制

6.13 让我们付诸实践

6.14 总结

第7章 实践3:持续集成

7.1 建立项目的心跳

7.2 理解完成、完整完成和完美完成的区别

7.3 实践持续部署

7.4 自动化构建

7.5 尽早集成,频繁集成

7.6 迈出第一步

7.7 付诸实践

7.8 总结

第8章 实践4:协作

8.1 极限编程

8.2 沟通与协作

8.3 结对编程

8.4 伙伴编程

8.5 穿刺,群战,围攻

8.6 在时间盒子中对未知进行调研

8.7 定期代码审查和回顾会议

8.8 加强学习和知识分享

8.9 诲人不倦且不耻下问

8.10 让我们付诸实践

8.11 总结

第9章 实践5:编写整洁的代码

9.1 高质量的代码是内聚的

9.2 高质量的代码是松散耦合的

9.3 高质量的代码是封装良好的

9.4 高质量的代码是自主的

9.5 高质量的代码是没有冗余的

9.6 让代码特质指导我们

9.7 今天的代码质量提高会为将来带来速度的提升

9.8 让我们付诸实践

9.9 总结

第10章 实践6:测试先行

10.1 测试的种类

10.2 质量保证

10.3 编写优质测试

10.4 TDD可以提供迅速的反馈

10.5 TDD可以为重构提供支持

10.6 编写可测试的代码

10.7 TDD也会失败

10.8 如何将TDD引入团队

10.9 成为测试感染者

10.10 让我们付诸实践

10.11 总结

第11章 实践7:用测试描述行为

11.1 红条、绿条、重构

11.2 一个用测试先行来描述行为的实例

11.3 引入限制条件

11.4 我们创建了什么

11.5 测试就是标准

11.6 测试需要完整

11.7 让测试独一无二

11.8 用测试来覆盖代码

11.9 bug是缺失的测试

11.10 用模拟对象来测试工作流

11.11 建立防护网

11.12 让我们付诸实践

11.13 总结

第12章 实践8:最后实现设计

12.1 可变性的阻碍

12.2 可持续性开发

12.3 编码与清理

12.4 软件被阅读的次数比编写次数多

12.5 意图导向编程

12.6 降低圈复杂度

12.7 将创建和使用分离

12.8 演化式设计

12.9 让我们付诸实践

12.10 总结

第13章 实践9:重构遗留代码

13.1 投资还是借贷

13.2 变成“铁公鸡”

13.3 当代码需要修改时

13.4 重构技巧

13.5 以支持修改为目的重构

13.6 以开闭原则为目的重构

13.7 以提高可修改性为目的重构

13.8 第二次做好

13.9 让我们付诸实践 

13.10 总结

第14章 从遗留代码中学习

14.1 更好,更快,更廉价

14.2 不在不需要的事情上花钱

14.3 循规蹈矩

14.4 提升整个软件行业

14.5 超越敏捷

14.6 将理解具象化

14.7 成长的勇气

参考文献


赠书活动

小伙伴们在构建软件的时候有没有遇到过什么问题呢?留言吐槽下前任的遗留代码,或者谈一下遇到过的那些 “奇葩队友”,精选评论中点赞排名前3位,每人赠送一本《修改软件的艺术》,截止到2017年11月3号22:00


 点击【阅读原文】,到京东购买《修改软件的艺术》

Copyright © 天津红桥区杨洋后援团@2017