代碼有一個死亡日期
代碼有一個死亡日期

代碼是如何死亡的

代碼出生,成長,成熟,衰老然後死亡。它可能被視作一個生命體,就像我的花園裏的鬱金香一樣。把這樣一個圖景放到代碼上有點可怕:小小的筆誤出現了,一點點代碼消失了,常量隨着時間的推移改變着它們的值(代碼也有通貨膨脹嗎?),一部分代碼長出來了沒有任何道理。這樣一個樣子,那麼難怪代碼會有一個壽終正寢的時候。

既然已經再沒有人用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)還有用。