代码有一个死亡日期
代码有一个死亡日期

代码是如何死亡的

代码出生,成长,成熟,衰老然后死亡。它可能被视作一个生命体,就像我的花园里的郁金香一样。把这样一个图景放到代码上有点可怕:小小的笔误出现了,一点点代码消失了,常量随着时间的推移改变着它们的值(代码也有通货膨胀吗?),一部分代码长出来了没有任何道理。这样一个样子,那么难怪代码会有一个寿终正寝的时候。

既然已经再没有人用FTP来做开发了,而且用着一个严肃的版本控制系统(VCS),这样一种幻象倾向于保持它的本性:一个幻象。代码自己不会改变,它停留在它被写下的样子。但是,它将会达到它的”过期作废”的日子然后突然释放出一大堆bugs。一个东西究竟是如何在不改变它自身的境况下改变它的行为的呢?

代码赖以生存的环境改变

既然代码不会改变自己,这意味着它的生存环境改变了。代码生存的环境中的每一件东西都在改变,一天一天地,一点一点地。“唯一不变的东西就是变化”——古希腊老哲学家赫拉克利特说。让我们来想想一些我们的代码运行所依赖的东西:

  • 代码库
  • 框架
  • PHP本身
  • 数据库,组成其架构中的其他每一个小部分和它们的布局
  • 操作系统
  • 用来跑它的硬件

随着它们的改变,上面的每一个东西都需要我们的注意。而且它们会对代码带来冲击:可能会要适用,修复,改变,调整和直接重写你的代码来保持对它们的演化的适用性。正是在这样一个时候,代码死了,或者,简单地说,消失或腐烂了。

引入的新的代码可能替换,一部分或全部,之前的代码。这样的之前的代码,通常会被标识为“old”。旧的代码倾向于保留在它们自己的地方直到被剪掉,或重构;因为在这之前,我们都害怕有什么其他的东西会依赖于它。这个的道理是因为没有人注意到无用的代码,而这些在消失中的代码将会非常壮观地,或直接说,痛苦地死去。

已经死亡的代码

让我们来看看代码过死亡的不同例子:

  • 常量:它们通过define()或const来定义,但是没有再被用到了。
  • 变量:它们在代码的某处声明了,但是之后从来没有被用到过。最典型的例子是所谓的“一次性变量”(used-once-varible),但是也适用于只被写下过声明的变量。注意,那些只读变量会引入一个错误:它们可不会平静地死去。
  • 函数:它们被定义了,但是它们没有被调用。这个也适用于闭包(closures)。在实践中,函数是通过于其他的一系列的相关函数来定义的;或在一个特殊的库文件中,然后作为一个整体引入(included)进来。是时候来清理那个文件了。
  • 类:它们被定义了,但是没有被(通过new)初试化过,静态地调用过(针对类常量,属性或方法)或继承过(通过extends)。类型暗示(Type hinting)和instanceof的使用不能保持一个类的生命,因为这两个指令仅仅检查存在性:事实上,它们可能更加意味着一个死代码的标记。
  • 异常:一些类的异常特例:它们从来没有被抛出过或继承过。
  • 接口:它们被定义了,但是从来没有被用于一个类型暗示(Type hinting)或instanceof的指令。这个几乎完全是和类相反的。
  • Trait:死掉的Trait,没有用过或extended过。“用过”的意思是,通过“use”关键字,在一个类中运用。
  • 文件:没有被引入(直接地或通过autoload)过,也没有直接调用过(比如index.php)。当遵循一个类一个文件的通例(one-class-one-file convention),通过outlaid, 死掉的文件现在和死掉的类连起来了。

死代码的最后一个例子可能是一些代码片段(code snippet):一个没有名字的一组代码再也不觉得有任何道理。这些代码包括无法运行到的代码,在“break”后面的代码,或在一个返回(return)或die后面的代码,而且除非它是一个结构structure的定义(class,function.etc)或一个标签。这个也适用于基于上面的代码的所有条件: if ($x instanceof\deadClass)。

寻找死亡的代码是一个简单但费力的活动,因为它意味着在代码中搜索每一个定义,然后反复失败。另外一个耗时的部分是去分别是否相关的功能(feature)还有用。