
軟件工程是一個相當耳熟能詳的名詞,軟件工程(Software Engineering)由來已久,亦因此程式設計師(Programmer)又稱為軟件工程師(Software Engineer)。 有讀過以前《Programmer 做到三十歲就要轉行?》的朋友,應該對軟件工藝(Software Craftsmanship)這個字不陌生,同時科啟學院其中一個創院價值,就是於香港資訊科技界推廣軟件工藝,於我們而言,軟件工藝代表的是軟件開發中創造的一面。
軟件工程
軟件開發真的是工程嗎?最容易理解的方法是看看軟件開發與其他工程範疇是否有類近之處:挑戰者號穿梭機因為一個密封圈失靈,就釀成意外。

軟件也會因為一個小錯誤,衍生出大問題,著名的Apple Goto fail Bug就造成全世界加密系統的漏洞,而其實查明原因,錯誤只是因為少了兩個標點符號:{ 及 }。
原本是這樣:
if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
goto fail;
goto fail;
if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
goto fail;
正確應該是這樣:
if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) {
goto fail;
goto fail;
}
if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
goto fail;
造成滿城風雨的Heartbleed bug也是一樣是很小很小的錯誤,也因為出問題的是加密組件,導 致後果嚴重。
這些事例,充份顯示軟件開發的工程一面,工程着重準確(precise),要求精細(detail-minded),講究可測量(measurability),在軟件工程中也有很多相對應的例子:
1. 效能調較(Performance Tuning)
2. 使用者測試(User Acceptance Testing)
3. 單元測試(Unit Testing)
4. 資訊保安(Cyber-security)
以上各項,都是典型軟件工程的例子,效能調較測試的是運行速度(Speed)、可負載量(Load);使用者測試也是根據清單逐個逐個測試,不論有任何一個細項不合格,都不能正式推出;單元測試更會計算測試覆蓋率(Coverage percentage),去判斷單元測試是否到位;資訊保安經常要做滲透測試 (Penetration Testing ),來判斷軟件是否安全。以上種種,都可以看到軟件開發有非常精細、準確的一面。
軟件工藝
資深軟件工程師經常會衝口而出,道:「段code寫得好靚!」或道:「段code寫得好核突!」這可奇怪了,為何會使用美感的形容詞如美或醜去形容一段代碼呢?因為軟件工程不只工程一面,還有非常具創造性的一方面。 以下是三段javascript 的代碼,意思上一模一樣,即使大家不一定懂得編程,但大家覺得那一個清晰,最美的呢?
第一個例子:
const msg = "Hello World!\nThis is my first code"
fs.writeFile('file.txt', msg, (err)=>{
if (err) {
console.log(err);
return;
}
fs.readFile('file.txt', (err, data)=>{
if (err) {
console.log(err);
return;
}
const content = data.toString();
fs.writeFile('file2.txt', content, (err)=>{
if (err) {
console.log(err);
return;
}
});
})
});
第二個例子:
const msg = "Hello World!\nThis is my first code";
fs.promises.writeFile('file.txt', msg)
.then(() => fs.promises.readFile('file.txt'))
.then((data) => {
const content = data.toString();
return fs.promises.writeFile('file2.txt', content);
})
.catch(err=> {
console.log(err);
return;
})
第三個例子:
const msg = "Hello World!\nThis is my first code";
async function run() {
try {
await fs.promises.writeFile('file.txt', msg);
const data = await fs.promises.readFile('file.txt');
content = data.toString();
await fs.promises.writeFile('file2.txt', content);
} catch(e) {
console.log(e);
}
}
run();
注:用console.log 是因為缺乏上文下理,無法處理異常狀況,如果在web Server的情況下就應該返回500 response code。
憑直覺論,大家可能有五花八門的選擇。但資深軟件工程師則有明顯偏好,選擇的不是2就是3,我個人則偏好3,因為意思上簡 潔明確。 其實這三個都是Javascript處理非同步(Asynchronous Code)的方法,第一個例子最古老,第二個例子較新,第三個例子則是新近才加進標準。