身為程式開發人員,多少都有這種經驗:
線上系統出錯,原開發者已浪跡天涯,程式碼沒人熟,老闆面色猙獰問誰會修。
(遇到這種擦屁股的屎缺,同事們默契十足全都退了一步 )
老闆說「很好,想不到你剛進公司就想立此奇功!好好幹,公司不會虧待你的」...
你說「暗陰羊咧,陳近南是你?」「沒問題,這交給我!」(心中滿是狂奔的羚羊)
這類狀況跟修自己的 Bug 截然不同,有幾個特點:
- 狀況緊急必須限時修好,砍掉重練不是選項。
- 屬臨時救急,策略上不打算多花資源深入了解及翻修。例如: 有計劃另建新系統取代,舊系統已進入插管維生階段。
- 處理者對系統架構、程式碼全然不熟悉,時間壓力下難以全面了解,也不敢大幅更動,需以最小幅度修改解決問題為目標,是一種「微創手術」的概念。
- 最重要的一點,修復過程通常會五毒攻心氣血逆流,只求速速搞定:
「喵的,又不是我搞壞的,為什麼是我」
「暗!這是什麼鬼寫法啦?」
基於上述因素,修補者常會無視原本程式碼的明顯缺陷,陷入「讓修改幅度極小化」的迷思。最後,雖然只改一個字元就把問題修掉,但錯失了防範類似問題再次發生的機會,為下次爆炸埋入引信。
用一個範例來模擬情境。假設有個網頁程式由資料庫取得下拉選單選項,並取第二選項為預設值,前人的程式這麼寫:
ddlSource.Items.Clear();
ddlSource.Items.AddRange(
GetSourceOptions()
.Select(o => new ListItem(o.Value, o.Key)).ToArray());
ddlSource.SelectedIndex = 1;
有一天資料庫端忽然冒出新選項,原本的第二選項變成第三個,使用者抱怨送出表單的預設選項錯誤,後續作業大亂。
你被指派修復這個問題,千辛萬苦追程式碼找到問題點,基於「微創手術」的概念,所以...
ddlSource.SelectedIndex = 2;
改完收工,跟老闆回報問題修好了。
只改了一個字元就把Bug修好了,但,這是良好的修復方式嗎?
小天使說: 如果下回資料庫再被塞入一筆資料,是不是系統又要再壞一次? 又不知是哪個倒楣鬼被踢下來辛苦追 Code,找出這段再改一次。(說不定還是你)
小惡魔說: 程式又不是我寫成這樣,我只奉命修好它,再壞掉也不是我的責任。更何況,使用者說這個選項不太會動,這次異動是個意外,以後應該不會再動。
看似兩難情境,分析利弊後不難抉擇:「如果成本不高,你應該把它改成強韌不易出錯的版本」,理由很簡單:
- 依據墨菲定律,別人愈說不會修改,它愈可能被修改
- 踢到石頭跌倒,把石頭搬到路邊防止別人摔跤,累積陰德抵過扶老太太過街三次
- 「上回 XXX 改好過,這回又壞了」。就像修過的水管又漏水,就算主因是管線設計先天不良,你覺得倒楣鬼水電工會不會背負功夫差做事兩光的評價?
SelectedIndex = 2 寫法必須建立在「資料來源項目不變,順序固定」的前題上,在我眼裡脆弱得像玻璃,稍有風吹草動便碎裂一地。如果修改成本不高,改用防禦式設計可讓程式更強韌,不易因外部因素故障。這點也是有些人的程式三天兩頭故障,有些人的程式像大同電鍋一用數十年都不壞的關鍵之一。
基於預設選項順序可能不固定的考量,程式可以改成這樣:
ddlSource.Items.Clear();
var sources = GetSourceOptions()
.Select(o => new ListItem(o.Value, o.Key)).ToArray();
ddlSource.Items.AddRange(sources);
var defaultSource = sources.SingleOrDefault(o => o.Value == "A2");
if (defaultSource == null)
thrownew ApplicationException("GetSourceOptions未包含A2項目");
ddlSource.SelectedIndex =
sources.ToList().IndexOf(defaultSource);
透過 SingleOrDefault() 用 Value 值找出預設選項的順序,既使查詢結果大風吹,它也能自動選對預設項目。要是資料庫裡的預設項目不知何故被他X的誤刪,這段程式還能明確指出問題出在"GetSourceOptions未包含A2項目",而不是噴出莫名其妙的 NullReferenceException,是不是貼心多了?
更進一步,如果某一天,預設值他X的要從 A2 改成 A3(對,那個規格書說永遠固定的A2),只能挖出程式碼重新編譯才能調整。於是我們還可以把預設值改成由 web.config appSettings 決定:
staticstring defaultSourceValue =
System.Configuration.ConfigurationManager.AppSettings["DefaultSource"] ?? "A2";
//...略...
ddlSource.Items.Clear();
var sources = GetSourceOptions()
.Select(o => new ListItem(o.Value, o.Key)).ToArray();
ddlSource.Items.AddRange(sources);
var defaultSource = sources.SingleOrDefault(o => o.Value == defaultSourceValue);
if (defaultSource == null)
thrownew ApplicationException($"GetSourceOptions未包含{defaultSourceValue}項目");
ddlSource.SelectedIndex =
sources.ToList().IndexOf(defaultSource);
醬子,連改掉預設項目都不用重新編譯程式呢,是不是好捧捧?
原本只要 2 改成 3 就可以交差,多寫幾百個字元是比較費工,但說穿了仍在舉手之勞的範圍,多花不到10分鐘讓程式材質從玻璃升級到不鏽鋼,很划算。
擦屁股是苦差事沒人愛,你可以衛生紙抹一下交差,也可以搬出免治馬桶座洗個痛快,有人還會順便把脈開藥治好烙賽,
即使能做到什麼程度也與經驗能力相關,但最重要的是開發人員的心態。(Yo Yo,拎杯也有 Freestyle )
要成為別人眼中專業又可信賴的開發人員,先從擦得一手好屁股開始吧~