Wednesday, June 23, 2010

邁向高品質程式碼的12個步驟

軟體工程師永遠追求高品質的系統,但如何達到,參考一下下列文章吧

1. 你有使用原始碼控制系統嗎?
我使用過一些商用原始檔控制系統(source control packages),也用過免費的CVS,所以我可以告訴你CVS相當不錯!如果你沒有原始碼控制系統,一旦需要與程式設計人員合作就相當麻煩了。因為程式設計人員無法知道其他人做了什麼,也無法輕易回復成出錯前的狀態。而且原始碼控制系統還有另一個優點,就是原始碼會被簽出(check out)到每位程式設計人員的硬碟裡。我還沒看過哪個用了原始碼控制的專案會遺失大量程式的。

2. 你能用一個步驟建出所有結果嗎?
我的意思是:從最新的原始碼快照開始,要花多少步驟才能建立出貨用的軟體?好的團隊會有單一個腳本檔,只要執行這個腳本,就會從頭簽出所有檔案、編譯每一行程式、建立執行檔(包含所有不同版本、語言以及#ifdef組合)、製作安裝程式,並且產生最後要用的媒體格式:光碟片、網站下載或是其他各種格式。

如果這個程序不是只有一個步驟,就會容易出錯。另外,當出貨時程緊迫時,修正「最後一個」問題、製作最終執行檔等過程必須要快速完成。如果程式編譯和安裝檔製作等動作需要20個步驟才能完成,你一定會急瘋了,然後做出一些蠢事。

就是為了這一點,我的前一家公司把原本使用的WISE換成InstallShield。因為我們必須透過NT工作排程器,在夜間用腳本自動製作出安裝程式,由於WISE無法透過工作排程器半夜執行,因此就把它丟掉了(親切的WISE員工跟我保證,他們的最新版一定會支援夜間執行)。

3. 你有進行每日編譯嗎?
在使用原始碼控制工具時,有時程式設計人員會不慎提交(check in)某些內容而導致編譯失敗。舉例來說,某人新增了一個原始檔,整個程式在他的機器上都能正常編譯,可是卻忘記把新增的原始檔加到原始碼控制程式庫中。結果這位仁兄非常健忘且愉快地鎖上機器回家了,導致其他人無法做事,也只好很不爽地回家。

導致編譯失敗是非常糟糕的事情(又經常發生),這時每日編譯就很有幫助了,它能保證不會有漏網之魚。在大型的團隊中,要確保能立即修正編譯失敗的最佳方法就是每天下午(像是午餐時間)重新編譯。大家在午餐前盡可能的提交檔案,等大家用餐完畢編譯也已經完成。若結果顯示正常,很好!大家可以簽出最新版的原始碼繼續工作;如果有問題就將它搞定,而其他人還可以用前一版沒問題的程式繼續幹活。

Excel團隊有個規定,導致編譯失敗的人必須從此負責重新編譯的動作當作處罰,直到有其他人出錯為止。這能讓大家盡量避免造成編譯失敗,同時也是個讓大家輪流熟悉重新編譯的好方法。在我的文章:「每日編譯是你的好朋友」裡有更多每日編譯的資料。

4. 你有沒有問題(bug)資料庫?
無論如何,只要你在寫程式時(只有一個人寫也是一樣),沒有一套良好的資料庫列出程式中所有的問題,就一定會產生出品質低劣的程式碼。很多程式設計人員自認能把問題清單記在腦子裡,才怪!我從來沒辦法一次記住超過二或三個問題,而且會在第二天早上,或是趕著出貨時把它們全部忘掉。你一定要正式地記錄問題。

問題資料庫可大可小。一個最簡單有用的問題資料庫必須包含每個問題的下列資料:

● 重現問題的完整步驟。

● 應該看到的行為。

● 實際看到的(有問題的)行為。

● 被指派的負責人。

● 是否已修正。

如果你是因為覺得問題追蹤軟體太複雜才不追蹤問題,請建立五欄的簡單表格,填入上述資料,然後開始使用吧!想要深入瞭解問題追蹤,請參閱「無痛錯誤追蹤」一文。

5. 你會先把問題都修好之後,才寫新的程式嗎?
古早第一版的Microsoft Word for Windows被視為「死亡行軍」型的專案。進度一直處在落後的情況。整個團隊的工作時間長得離譜,專案卻一延再延三延,大家都承受無比的壓力。拖了幾年後,那個鬼東西終於上市了,微軟就把整個團隊送到Cancun(墨西哥著名海灘)渡假,然後再坐下來做深度反省。

他們發現產品經理過度堅持要維持「進度」,而程式設計人員只能匆匆經過編碼階段。而且正式的時程並未包含錯誤修正這個階段,於是寫出的程式碼非常糟糕。此外,也沒有人試圖要減少問題數量,而事實剛好相反!有位程式設計人員要寫支程式以計算一行文字的高度,結果他只寫了「return 12;」,並等問題報告出爐指出這個函數功能不對。於是,時程表變成一份等著被轉換成問題的功能列表,事後檢討時則稱之為「無窮錯誤法(Infinite defects methodology)」。

為了修正這個問題,微軟全面採用所謂的「零錯誤作法(Zero defects methodology)」。公司裡很多程式設計人員聽了都不禁竊笑,因為感覺就像是管理階層認為能用行政命令降低錯誤數量一樣。實際上,「零錯誤」是指無論何時都要先修正錯誤才能寫新程式。原因如下:

一般來說,愈晚修正錯誤,修正時所付出的成本(時間及金錢)愈高。舉例來說,當你打錯字或出現編譯器會發現的語法錯誤,就修正只是小事一樁。

若你的程式第一次執行出錯時,應該也能立即改正,因為整個程式還在你腦海裡。如果要為幾天前寫的程式除錯,應該需要回想一陣子吧!不過,當你重讀所寫的程式後,就會記起所有細節,並在適當時間內把問題修好。

若是要為幾個月前寫的程式除錯,那很有可能已經忘掉一大半,要修正簡直難上加難!或許,你正在替別人的程式除錯,而當事人遠在阿盧巴渡假,這時除錯的任務就像科學一樣:你得條理分明、小心翼翼地慢慢來,也無法確定要多久時間才能解決。另外,如果要為已出貨的程式除錯,修正問題的代價就更難以估算了!

這就是要立即修正問題的理由之一,因為這樣做能少花點時間。另一個理由是,寫新程式的時間遠比修正現有錯誤的時間容易估計。舉例來說,如果要你估計寫串列排序的程式需時多久,你應該能估算得相當準確;但假如你的程式在裝了Internet Explorer 5.5之後有問題,要估計需要多久才能修好,恐怕用猜的都猜不出來,因為你不知道(當然不知道)問題點在哪裡。要找出問題可能就要花上三天,但也可能兩分鐘內解決。

如果時程裡包含很多有待修正的問題,那麼這種時程是不可靠的。假如把已知的錯誤都修好了,剩下的就只有新程式了,那麼時程就會變得非常準確。

把錯誤數量維持在零還有另外一項優點,就是面對競爭時反應可以更快。有些程式設計人員認為這樣做可讓產品隨時推出。一旦競爭者推出某個殺手級新功能來搶客戶時,只要把該功能加上去,即可立即出貨,不必修正累積下來的大量問題。

6. 你有一份最新的時程表嗎?
在此要好好的談時程表。如果你的程式對公司非常重要,有太多理由可以說明,預知程式完成時間點有多麼重要。但程式設計人員是出了名的不愛訂定時程,他們會對著業務部門大吼:「該完成的時候就會完成了阿!」

但是問題不可能就這樣算了。業務人員有太多的規劃決策必須遠在程式出貨之前就做好決定,如:功能展示、商展、廣告等,而做決定的唯一方法就是定出時程,並且隨時更新。

擁有時程的另一個重點是,可以強迫自己決定要製作哪些功能,並剔除最不重要的功能,以避免功能過度膨脹(featuritis,又名scope creep)。

7. 你有寫規格嗎?
寫規格就像是使用牙線:大家都同意這是好事,卻沒有人真的這麼做。

我不知道原因,或許是大多數程式設計人員都討厭寫文件吧!所以當全是程式設計人員的團隊面對問題時,自然傾向用程式碼而非文件來表示。他們寧願跳進去寫程式,死也不願先寫規格。

在設計階段發現問題時,只要改幾行文字就能輕易修正。但等到程式寫出來之後,修正的代價就高多了,代價包含了情感(人們討厭拋棄程式碼)和時間,所以會抗拒修正問題。通常未依據規格製作的軟體,到最後的設計都很糟,而且進度完全無法控制。這似乎就是Netscape所發生的問題。它的前四版一團亂,結果管理階層還愚蠢地決定把程式丟掉重新開始。然後在Mozilla上又重蹈覆轍,製造出一個無法控制的怪物,耗了好幾年才進入alpha測試階段。

我的小秘方是把程式設計人員送去上密集的寫作課程,讓他們變得不那麼排斥寫作,就可以解決這個問題。另一個方法是雇用聰明的產品經理來寫規格。不管用哪一種方法,你都應該強制執行「沒有規格就不寫程式」這個簡單的規則。

8. 程式設計人員有沒有安靜的工作環境?
有大量的文件記載,為知識工作者提供空間安靜及隱私可以提昇產能。軟體管理經典著作《Peopleware》也詳盡地記載了這種產能上的增益。

其中的原理如下。我們都知道知識工作者進入「沉浸狀態」(flow,也稱作in the zone)時,工作效果是最佳的。這時他們會完全與環境脫離,全心專注在工作上。他們忘記時間並絕對專注地產出極佳成果。他們所有豐富的產出也都是在這個時候完成的。作家、程式設計人員、科學家,甚至是籃球員都會告訴你進入「沉浸」的情形。

問題是要進入沉浸並不是那麼容易。你可以試著計時,平均大概要15分鐘才能開始全速工作。一旦碰到累了或是那一天已經有很多創造性的成果,會根本無法進入沉浸,然後就只能看看網頁、玩玩俄羅斯方塊打混過完一天。

另一個問題就是沉浸不容易維持。噪音、電話、同事的中斷(特別是這一點)都會讓你脫離沉浸。假設有個同事問了一個問題只讓你中斷了一分鐘,實際上,卻會讓你完全脫離沉浸,必須再等半個小時才能回復生產力,導致整體產能都出問題了。如果你身在一個喧鬧的開放辦公環境中(就像某些網路公司最愛的類型),行銷部門在程式設計人員旁一直對著電話大喊,那麼你的產能就像一直被中斷的知識工作者一樣顛簸,永遠無法進入理想的「沉浸」。

這對程式設計人員來說是非常嚴重的事情。生產力的多寡在於能否於短期記憶體中處理大量的細節,而任何一種中斷都會讓這些細節完全消失。等你轉回來工作時,就會完全不記得任何細節(例如,正在使用的區域變數名稱,或是搜尋演算法寫到哪了),必須把剛才寫的東西找出來,於是速度就這樣放慢下來,直到你回復為止。

以下做個簡單的計算。就用剛才的說法:打斷程式設計人員的一分鐘,事實上是耗費了15分鐘的產能。假設有兩個程式設計人員Jeff和Mutt,把他們安排在一個標準呆伯特式牛棚相鄰的開放隔間中。Mutt忘了strcpy函數的Unicode版本拼法,他可以花30秒自己查出來,也可以花15秒問Jeff。由於對方就坐在旁邊,所以選擇問Jeff,於是Jeff就分心並損失了15分鐘的產能(但替Mutt省了15秒)。

現在把他們搬到兩間有牆有門的獨立房間裡。如果Mutt忘記那個函數的拼法,他可以花30秒查出來,也可以花45秒過去問Jeff(以典型程式設計人員的身材來說,要離開位置並不輕鬆),結果他就自己查了。於是Mutt損失30秒的產能,不過,卻替Jeff省下15分鐘!

9. 你有沒有用市面上最好的工具?
用編譯式語言撰寫程式得花一些時間進行編譯,可說是一般家用電腦還無法瞬間完成的事情之一。如果你的編譯過程超過幾秒,去找台最新、最棒的電腦可以替你省點時間;如果編譯需要超過15 秒,程式設計人員覺得無聊就會跑去看線上新聞 The Onion,然後陷在裡面耗掉幾個鐘頭的產能。

在單螢幕系統上替 GUI程式除錯並非絕不可能,但用起來很痛苦。當你在撰寫GUI程式時,或許使用兩台螢幕會讓你輕鬆許多。

大部分程式設計人員到最後都必須修飾圖示或工具列所用的圖,可是大部分的人都沒有一個好用的圖形編輯器。雖然用微軟的小畫家修圖實在令人不可思議,不過,卻是多數程式設計人員不得不做的事。

在我的前一家公司,系統管理員會一直傳送自動發送的垃圾信給我,抱怨我在伺服器上使用了超過「 220 MB」的硬碟空間。依據現在硬碟的價格,這點空間的費用還遠不及我所用的衛生紙。即使只花10分鐘清理目錄,也是產能的極大浪費。

一流的開發團隊並不會虐待他們的程式設計人員。工具不佳所引起的挫折感雖然很小,累積起來還是會讓程式設計人員心情超級不爽、外加脾氣暴躁。而心情不爽就等於是無生產力。

不過,程式設計人員也是很容易被酷炫、新潮的東西所賄賂,這比用加薪叫他們工作要便宜得多了!

10. 你有沒有測試人員?
如果你的團隊沒有專門的測試人員(至少每兩到三個程式設計人員要配一名),就可能會推出問題很多的產品,不然就是浪費錢,像是讓時薪 100美元的程式設計人員去做測試員(時薪 30美元)做的事。省下測試員的錢並不是真正的節省,這是非常明顯的道理,但我實在很驚訝很多人卻還認不清這一點。

11. 是否在面試時要求面試的對象試寫程式?
你會不請魔術師先表演幾招就直接雇用嗎?當然不會。你會不先試菜就決定自己婚宴的餐廳嗎?我很懷疑。

儘管如此,現在程式設計人員能否被錄用,都必須要看履歷是否與眾不同?是否與主考官相談甚歡?或是回答出只要查查文件就能知道的瑣碎問題(例如,CreateDialog()和DialogBox()間的差異是什麼?)。你根本不會管他們能否背得出幾百條有關程式設計的瑣事,你真正在意的是他們能否寫出程式。更糟的情況是問那種「啊!我懂了!」的腦筋急轉彎問題;就是那種知道答案時理所當然,可是光看題目時卻覺得莫名其妙。

拜託別再這樣做了!隨便你想怎麼面試都行,不過請記得一定要讓面試者寫些程式。

12. 是否進行走廊使用性測試?
「走廊使用性測試(Hallway usability)」是指在走廊攔住下一位經過的人,然後逼他試用你剛寫好的程式。如果能攔下五個人並且試用完成,就可以發現程式中95%應注意的使用性問題。

設計出良好的使用者介面並沒有想像中那麼困難,但是必須要能夠吸引客戶並購買產品,才是最重要的。你可以參閱我所寫的免費線上使用介面設計書,這是針對程式設計人員的入門書籍。

不過,處理使用者介面有一點最重要:如果你把程式展示給少數幾個人看(事實上,只要五或六個就夠了),就能快速地發現一般人會遇到的主要問題。在Jakob Nielsen的文章中有解釋原因。即使你的UI設計技巧不足,只要強硬逼自己實行不花太多時間的走廊使用性測試,就會讓你的UI水準大幅提昇。(摘錄整理自第三章)


後記
從2000年8月約耳測試問世以來,世界各地的開發人員寄了很多電子郵件來告知他們組織的分數。雖然測試結果的分佈相當平均,但大部分的回覆分數都 介約2或3之間。
 

聽說有很多開發人員受夠了無組織的「槍手(Gunslinger)」軟體開發實務,假如公司的約耳測試分數太低,便會拒絕到該公司工作。也曾聽過團體經理把約耳測試作為逐步改善團隊改善的程序,並且一路向上的好消息。

在這段期間,很多開發組織似乎走太遠了,已遠遠超過約耳測試所及,而進入了高級官僚動脈硬化的陣痛期。出現這種狀況時你一定會發現,因為人們花在會議準備上的時間遠比開發軟體還多,即使在約耳測試中拿到滿分12分,還是可能會因過多的政治問題和固定負擔而搞砸。以下這一段可別透露是我說的!早自1990年代我還在微軟工作時,所有證據都顯示,該公司因為組織過於龐大、內部政治鬥爭、官僚浪費等原因,幾乎已陷入停頓狀態。證據何在?就拿平板電腦來說,平板電腦只是個某個微軟中階經理會愛的玩意,它的設計似乎讓華盛頓Redmond的產品經理花一整天開會討論,卻是在會議室裡做自己的事,根本沒有參與。但這並不是微軟才有的現象。


如果你發現自己花太多時間在安裝及設置龐大的軟體方法系統、或命名「視覺化XXXX企業架構師」的軟體、甚至是在極致軟體製程開發和UML間反覆來回對團隊進行再教育,直到他們在開發軟體時,只會一直把頭轉來轉去不知所措 ,即使這份約耳測試成績再好,終究還是問題纏身。(摘錄整理自第三章)

Saturday, June 19, 2010

Java performance Issue

今天在 TheServeside 看一到一篇文章,剛好是我的專長,也讓我多知道一些有關 Java Performance 的偵測方式

其中最常發生的應該是 Database Call。我常常看到 Developer 寫 code 時,因為沒經驗的關係,把兩個 table 的資料放在 Loop 裡面寫,造成多餘的 Database Call

舉例而言, 要找出 product 的 vendor ,有經驗的 Developer 會用 join 的方式一次 Query 就將 Data 一次取出,但沒經驗的就會先取出所有 Product 後再 loop product 再取出 vendor。這個是所有沒經驗的 Developer 會做的事。所以只要出個類似的題目給 Developer 就可以看出程度如何了。

以下是從 TheServerSide 貼過來了,供大家參考

Top 10 Performance Problems taken from Zappos, Monster, Thomson and Co

by Andreas Grabner, Jun 15, 10

For a recent edition of the Swiss Computerworld Magazine we listed our Top 10 Performance Problems as we have seen them over the years when working with our clients. I hope this list is enlightening – and I’ve included follow-up links to the blogs to help better understand how to solve these problems:

#1: Too Many Database Calls

The problem we see the most are too many database query per request/transaction. There are 3 specific phenomena to witness

  1. More data is requested is than actually required in the context of the current transaction, e.g.: requesting all account information instead of those that we need to display on the current screen.
  2. The same data is requested multiple times. This usually happens when different components involved in the same transaction act independently from one another and each requests the same set of data. It is unknown what type of data has already been loaded in the current context so we end up with the same queries multiple times.
  3. Multiple queries are executed to retrieve a certain set of data. This is often a result of not taken full advantage of complex SQL statements or stored procedures to retrieve the data in one batch.

Further Reading: Blog on Linq2Sql Performance Issues on Database, Video on Performance Anti-Patterns

#2: Synchronized to Death

There is no question that synchronization is necessary to protect shared data in an application. Too often developers make the mistake to over-synchronize, e.g.: excessively-large code sequences are synchronized. Under low load (on the local developers workstation) performance won’t be a problem. In a high-load or production environment over-synchronization results in severe performance and scalability problems.

Further Reading: How to identify synchronization problems under load

#3: Too chatty on the remoting channels

Many libraries out there make remote communication seem like a piece of cake. There is hardly any difference for the developer to call a local vs. remote method. The lack of understanding of what is really going on under the remoting-hood makes people forget about things like latency, serialization, network traffic and memory usage that come with every remoting call. The easy way of using these technologies results in too many calls across these remoting boundaries and in the end causes performance and scalability problems.

Further Reading: Performance Considerations in Distributed Applications

#4: Wrong usage of O/R-Mappers

Object-Relational Mappers take a big burden off developers’ shoulders – loading and persisting objects in the database. As with any framework there usually are many configuration options to optimize the usage of the O/R Mapper for current application use cases. Faulty settings and incorrect usage of the framework itself too often results in unexpected performance and scalability problems within these frameworks. Make sure you make yourself familiar with all options and learn about the internals of these libraries that you rely on.

Further Reads: Understanding Hibernate Session Cache, Understanding the Query Cache, Understanding the Second Level Cache

#5: Memory Leaks

Managed runtime environments such as Java and .NET have the advantage of helping with memory management by offering Garbage Collectors. A GC, however, does not prevent memory leaks. Objects that are “forgotten” will stick around in memory and ultimately lead to a memory leak that may cause an OutOfMemoryException. It is important to release object references as soon as they are no longer needed.

Further Read: Understanding and finding Memory Leaks

#6: Problematic 3rd Party Code/Components

Nobody is writing all of the functionality of a new application on their own. We use existing 3rd party libraries to speed up our development process. Not only do we speed up our output – but we also increase performance risks introduced by these components. Even though most frameworks are well documented and have been thoroughly tested, there is no guarantee that these frameworks run as expected in every use case they are included. 3rd party code is often used incorrectly or in ways that have not been tested. It is therefore important to make an in-depth check of every framework before introducing it into your code.

Further Read: Top SharePoint Performance Mistakes

#7: Wasteful handling of scarce resources

Resources such as memory, CPU, I/O or the database are scarce. Wasteful handling of these resources results in lack of access to these resources by others and ultimately leads to performance and scalability issues. A good example: database connections that are kept open for too long. Connections must only be used for the time period they are really needed, e.g.: for a query – and then returned to the connection pool. We often see that connections are requested early on in the request handler and are not released until the very end which leads to a classic bottleneck situation.

Further Read: Resource Leak detection in .NET Applications

#8: Bloated web frontends

Thanks to high-speed web access many users have a better end-user experience in the World Wide Web. The downside of this trend is that many applications get packed with too much stuff – they become bloated – which ultimately leads to bad browsing behavior. Particularly users that do not yet have high-speed internet access suffer the most. Too many images that are too large; failure to use or incorrect usage of the browser cache; or overly-aggressive usage of JavaScript/AJAX – all result in performance problems in the browser. Following the existing Best Practices on Web Site Performance Optimization can solve most of these problems:

Further Read: How Better Caching would help speed up Frankfurt Airport Web Site

#9: Wrong Cache Strategy leads to excessive Garbage Collection

Caching objects in memory to avoid constant roundtrips to the database is one way to boost performance. Caching too many objects – or objects that are hardly ever used quickly changes the advantage of caching into a disadvantage due to higher memory usage and increased GC activity. Before implementing a caching strategy you have to figure out which objects to cache and which objects not to cache in order to avoid these types of performance and scalability problems:

Further Reads: Java Memory Problems, Identify GC Bottlenecks in Distributed Applications

#10: Intermittent Problems

Intermittent problems are hard to find. These are usually problems that occur with specific input parameters or only happen under certain load conditions. Full test coverage – functional as well as load and performance coverage – will uncover most of these problems early on before they become real problems for real users.

Further Reads: Tracing Intermittent Errors by Lucy Monahan from Novell, How to find invisible performance problems

(Bonus Problem) #11: Expensive Serialization

With remoting communication – such as Web Services, RMI or WCF – objects need to serialized by the caller in order to be transferred over the network. The callee on the other side needs to de-serialize the object before it can be used. Transformation therefore happens on both sides of the call resulting in some overhead while doing so. It is important to understand what type of serialization is required by both ends and what the optimal choice of serialization and transport type is. Different types of serialization have a different impact on performance, scalability, memory usage and network traffic.

Further Read: Performance Considerations in Distributed Applications

Monday, June 14, 2010

Blogger 被 China 封鎖

這個 Blogger Server 是屬於 Google 的,但不幸的,當你在 China 工作時,此 Blogger 是被封鎖住的。唯有透過 VPN 的方式連到其他國家才能將自己的心得寫到此 Blogger 上

Well,不管如何,就像病毒一樣,有人寫就一定有人解,自己想辦法克服,再寫上自己的心得,只要有空並且有料就行了