儘早償還技術債

Author Avatar
Yish 10月 22, 2016
  • 在其他設備中閱讀本文章

在建置產品時很多人會有這樣子的想法:功能先行,架構與代碼雖然不好看,但是他是正常運作的 it’s working!

這個想法是合理的、靠譜的,畢竟在時程壓力之下,只要能解決都是好方法,白貓黑貓,抓得到老鼠的就是好貓,但是程式開發上會有所謂的腐爛期,說到這裡一定會覺得很奇怪,程式又不是食物,怎麼會有腐爛的問題呢?

所謂的腐爛,是指一旦經過一段時間,或者是不在這個專案上了,再度回來時看到的時候是否會認得程式本身表達的意思?網上其實很多大神都在推廣重構這件事情,就是為了讓代碼能夠可讀性高、好維護,因為他們的經驗告訴他們,如果你不盡快償還這些所謂的腐爛代碼、技術債,你將會花費更多的時間來維護他們,舉例來說:

1
2
3
foreach($rows as $row) {
$row[] = $row['created_at'];
}

這代碼看起來很好明白,就是抽出資料當中的時間並且個別放到變數內,但實際上也沒這麼美好,突然資料架構變化了,必須透過雙層迴圈才能取得相對應的資料,也就是會變成下面這個樣子:

1
2
3
4
5
foreach($all_rows as $rows) {
foreach($rows as $row) {
$row[] = $row['created_at'];
}
}

然後接著又有更多奇妙的需求出現,導致於整段代碼呈現類似這樣子的狀況:

1
2
3
4
5
6
7
8
9
10
11
12
13
foreach($all_rows as $rows) {
foreach($rows as $row) {
if ($row['created_at'] > Carbon::now()) {
//...
}
else if ($row['created_at'] < Carbon::now()) {
//...
}
else {
//...
}
}
}

到這裡還算是簡單的,一眼掃過還算是可以看得懂,但如果整段程式都是充斥著這樣子的代碼真的會讓人很崩潰。

當看到這段代碼時,第一個步驟應該是先將可讀放在第一順位,將 $all_rows, $row, $key 改成相對應資料變數,像是這樣子:

1
2
3
4
5
foreach($package as $products) {
foreach($products as $product) {
//
}
}

看起來比上面好懂一些了,至少我目前知道原始資料是一大包,然後必須透過兩層迴圈才能取得各個產品資料。接下來看了一下內部的規則,是要透過建立時間來區別哪些產品已經到期、哪些是快到期、哪些是剛製造的產品,如果他像這樣子可讀性就會提高許多:

1
2
3
4
5
foreach($package as $products) {
foreach($products as $product) {
$this->classify($product);
}
}

接下來雙層的迴圈看起來還是很不好看,如果能夠像這樣子的話…

1
2
3
4
5
$this->filter($package, function($products) {
$this->map($products, function($product) {
return $this->classify($product);
});
});

到這裡相比之前的雙層迴圈和各個判斷式相比之前可讀性提高許多,首先我會有一大包資料 $package,我要從中取出 $products 的資料,接著我將 $products 分別拆解 map 成獨立資料,最後做分類 classify 清晰的程式結構,維護起來也是相當輕鬆,由於每個方法都只單獨做一件事情(SRP),所以如果突然又改了需求說要多一個上市兩年後的分類產品資料,也只需要修改 classify 方法。

說到這裡其實技術債本身並不是可怕的,可怕的是之後的你,或者是你的小夥伴看不懂你的代碼,造成腐爛、維護成本的提高才是問題所在,要記住的事情是:不可能一次就把代碼寫到完美、架構漂亮,持續的改善並重構才是最佳的做法,代碼是寫給人讀的,不是只求功能正常即可,如果寫了一本書沒人看得懂也是白寫的,當寫完一段代碼之後不仿拿給其他人讀讀看,並加以改進會讓自己的思維更上一層樓。