Skip to main content

軟件工程及軟件工藝

· 8 min read
戈頓
Developer & Co-founder of Tecky Academy

軟件工程是一個相當耳熟能詳的名詞,軟件工程(Software Engineering)由來已久,亦因此程式設計師(Programmer)又稱為軟件工程 師(Software Engineer)。 而有讀過以前《Programmer 做到三十歲就要轉行?》的朋友,應該對軟件工藝(Software Craftsmanship)這個字不陌生,同時科啟 學院其中一個創院價值,就是於香港資訊科技界推廣軟件工藝,於我們而言,軟件工藝代表的是軟件開發中創造的一面。

軟件工程

軟件開發真的是工程嗎?最容易理解的方法是看看軟件開發與其他工程範疇是否有類近之處:挑戰者號穿梭機因為一個密封圈失靈,就釀成意外。

Challenger Accident

Source:https://www.google.com/url?sa=i&source=images&cd=&cad=rja&uact=8&ved=2ahUKEwjFwsruy7ffAhURfnAKHVM1AO8QjRx6BAgBEAU&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DHHjp4cz3KJ4&psig=AOvVaw2DZK3RuxHUiJchlxjiAKPc&ust=1545710871178637

軟件也會因為一個小錯誤,就會 有大問題,著名的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 ),來判斷軟件是否安全。以上種種,都可以看到軟件開發有非常精細、準確的一面。

軟件工藝

筆者按於2020-11-12: 有讀者認為之前的例子不能反映軟件工藝之分野,只是反映了不同版本的Node.js處理非同步之做法,因此筆者決定更改此例子,以闡術筆者之想法。

資深軟件工程師經常會用到寫得「美妙」、「很醜」等形容詞,這可奇怪了,為何會使用美感的形容詞如美或醜去形容一段程式碼 碼呢?因為軟件工程不只工程一面,還有非常具創造性的一方面。 以下是三段Python 函數的程式碼,所運行的算法是非常相似的,都是嘗試找出所有在n以下0以上之整數的畢氏定理整數組合(Pythagorean Triplets),即使大家不一定懂得編程,但大家覺得那一個最為簡潔明確呢?

第一個例子用傳統迴圈(Loop)做法:

def pythagoras(n):
triples = []
for x in range(1,n+1):
for y in range(1,n+1):
for z in range(1,n+1):
if x**2 + y**2 == z**2:
triples.append((x,y,z))
return triples

第二個例子運用了列表推導式(list comprehension):

def pythagoras(n):
return [(x,y,z)
for x in range(1,n+1 )
for y in range(1,n+1 )
for z in range(1,n+1 )
if x**2 + y**2 == z**2
]

第三個例子運用了產生函數推導式(generator comprehension):

from itertools import permutations
def pythagoras(n):
return ((x,y,z)
for (x,y,z) in permutations(range(1,n+1),3)
if x**2 + y**2 == z**2
)

這三個方法有何分別呢?且等筆者一一解說:

  • 第一個方法運用最傳統的for迴圈及if,本身較容易理解,但寫出來感覺上較累贅,而且也多了許多縮排(Indentation)
  • 第二個方法運用了列表推導式(List Comprehension),是 Python一個較獨有之功能,開發者可以用一個類似寫數學中的集合(Set)的方法去構建列表(List)
  • 第三個方法更進一步,運用了Generator,也就是整個運算是Lazy的,所謂Lazy ,就是會等待我們使用一個函數next()才會計算下一個整數組合,另一方面又用了permutations的function ,避免使用了nested loop。也加上了x + y > z 去避免搜尋不可能是整數組合之數字。

以下是一個使用的例子:

from itertools import permutations
def pythagoras(n):
return ((x,y,z)
for (x,y,z) in permutations(range(1,n+1),3)
if x**2 + y**2 == z**2
if x + y > z
);

>>> gen = pythagoras(10)
>>> next(gen)
(3, 4, 5)
>>> next(gen)
(4, 3, 5)
>>> next(gen)
(6, 8, 10)
>>> next(gen)
(8, 6, 10)
>>> next(gen)

所以,就算你輸入一個很大的n例如pythagoras(1000),效能也會較好,因此可以判定方法三比方法二及一都要好

因為寫程式碼要求其實不只正確那麼簡單,還要着重易讀(Readable)、易寫(Writable)、高效能(Performant)。著名軟件工程師Harold Abelson曾言:

Programs must be written for people to read, and only incidentally for machines to execute

所謂美的代碼,正是兼具易讀(Readable)、易寫(Writable)而又準確的代碼。 軟件工藝所追求,正是重視軟件工程師本身編程的技藝,寫美的代碼,建立好的軟件。

宣言

美國一群軟件工程師曾經提出一個相當精彩的宣言,去綜合軟件工藝的意涵:

As aspiring Software Craftsmen we are raising the bar of professional software development by practicing it and helping others learn the craft. Through this work we have come to value:

Not only working software, but also well-crafted software

Not only responding to change, but also steadily adding value

Not only individuals and interactions, but also a community of professionals

Not only customer collaboration, but also productive partnerships

謹以此四句,送給一直堅持寫好代碼的軟件工程師,共勉之。