天天看點

程式員的素質

 ------文章轉自  白楊 http://baiy.cn

        衆所周知,軟體和硬體工程師個體間存在巨大的生産力差異:經常有一個人一兩天就能做好的事情另一個人花一兩個月也沒能做到同樣好。

        Apple創始人史蒂夫·喬布斯曾在《In the Company of Giants》一書中接收采訪時提到:“一個最優秀的人完成工作的能力能抵50到100個一般水準的人”。而軟體工程領域也經常提到最好和最差的程式員之間的生産力差異超過1000倍,因為如果一個程式員産出的代碼中包含了很多bug,需要其它程式員花費大量時間去修正,那麼他的生産力是負值。

       在世界名著《人月神話》中,作者也明确地提出了類似的觀點:“最優秀和一般的軟體工程實踐之間的差距是非常大的,可能比其他工程領域中的差距都要大……”。除了團隊組織和項目管理等行政方面的問題;以及國文、數學、邏輯思維和形象思維等個人基礎素質以外,到底是哪些專業素養導緻了程式員個體間的巨大生産力差異呢?本文嘗試以由淺到深、由易到難的順序來讨論程式員的個體素質。

1. 基本技術素養

         基本素養包含了每個程式員都應當牢固掌握和充分了解的技能和知識。基本素養中又可以分為前景知識和背景知識兩種。

前景知識:今天的程式員主要通過使用程式設計語言書寫代碼來完成軟體開發活動,前景知識則特指程式員對其使用語言的掌握程度。這裡所說的“掌握”并不是僅僅能夠寫出合法的代碼就行了,而是對語言中各種實作細節的深度掌握。如果一位程式員未能透徹掌握他正在使用的程式設計語言,那他寫的程式就一定會或早或晚地出現一些至少在他看來有些“莫名其妙”的問題,比如:應用程式時常莫名其妙地崩潰、經常發生資源洩露、或是執行過某些操作時性能差的離譜等等。以C++語言為例:正如大部分C程式員都能毫不費力地将任意C源碼“肉眼翻譯”成僞彙編碼那樣,一個合格的C++程式員也應當可以做到将任意C++源碼“人腦編譯”為對應的僞彙編碼。但現實的情況是由于C++新增了模闆、虛函數、RTTI、虛基類、異常等大量進階特性,以至于能夠真正透徹掌握C++語言的程式員很少。除非完全不使用前文中列出的那些C++進階特性,僅把它作為一個“稍好的C”來使用。否則,對于任何一個職業程式員來說,貿然地使用一種尚不了解其底層(編譯器或VM)

實作細節的語言特性是一件很危險的事。類似的例子同樣适用于其它語言。不管是編譯型的(如:C/C++)還是解釋/虛拟機型的(如:PHP、Java以及JavaScript)程式設計語言,它們都有一些需要程式員事先透徹了解和掌握的實作細節。

背景知識:透徹地了解和掌握至少一門程式設計語言對任何一位職業程式員來講都是很重要的基礎素質,但僅僅做到這點仍然是遠遠不夠的。想要産出具備現實意義的産品,還需要具備足夠的背景知識。以下,我們将背景知識分成“公共背景”和“領域背景”兩類,分别進行讨論。

公共背景:公共背景包括了幾乎每個程式員都需要掌握的知識。從作業系統原理、編譯原理、資料結構、離散數學、計算機組成原理、網絡原理等理論性知識到各種接口和實際環境的運作時API(例如:對C/C++來講通常是作業系統API,對JavaScript來講通常是浏覽器提供的API)都是作為程式員必不可少的基礎。

領域背景:領域背景中包含了與具體問題域相關的背景知識,這些知識并不是每個程式員都需要掌握的,但對于涉及該領域的開發人員來講卻必不可少。例如:對于要實作音頻編輯器的程式員來說,至少具備起碼的聲學背景;MIDI編曲軟體的作者需要樂理和和聲方面的知識;OCR軟體的作者則要學習模式識别和圖像處理方面的理論等等。

2. 思維的條理性和連貫性

        對于一個程式員來說,從産品的設計之初到其中元件的逐一實作完成乃至最後的聯調。整個開發周期從始至終都需要保持清晰的條理和連貫的思維。以下準則有助于幫助程式員在開發過程中始終保持思維的條理性和連貫性。

3W準則:我想每個程式員都應該遵循3W準則,即:每次在開始編寫代碼之前,都應當先搞清楚What、Why、How三個問題。其中“What”指我們要做什麼,也就是使用者的需求。“Why”表示為什麼我們要做這件事,這是對需求和架構的更深層了解。很多時候,隻有充分了解了“為什麼”以後,我們才能更好地完成設計(例如:使用更合适的算法和構架,以及在架構中預留恰當的接口等等)。最後“How”從設計層面指明了應當以何種架構、哪些模式和算法來最終建構出産品。值得指出的是,3W準則不但适用與對系統進行整體分析和設計,同樣也應當利用在子產品或元件等粒度更細的層面上——在開始着手編寫一個元件前,應當事先搞清楚這個元件的功能(What);需要在何處使用它、為什麼需要用到它、它的典型用例和工作上下文(Why);最後是使用何種架構、模式和算法來實作它(How)。本質上講,需求分析和總體設計的意義就是在系統的層面上回答以上三個問題。總體設計使開發人員能夠對待開發的産品在整體上有一個比較清晰的了解,知道目前正在實作

的元件處于系統中的什麼位置。在着手實作一個元件前,如果程式員能先在元件的層面上搞清楚這三個問題,那麼他就可以在一個目标、動機和實作方式都很清晰的狀态下開始工作。進一步說,在開始工作之前,清楚的知道自己要做什麼以及如何完成手上的工作對于任何行業的從業人員來說都是非常重要的。遺憾的是,相當多的程式員好像并不這麼認為。

        時刻保持清晰的邏輯:3W準則讓我們在動手前就清楚自己要做什麼以及如何做。但在具體實踐的時候我們還需要随時清楚自己正在做什麼?之前幾步分别做了哪些事?以及之後幾步還要幹點啥?隻有時刻了解自己“剛才”、“現在”、以及“将來”要做的事情,才能随時保持思維連貫和條理清晰。

以恰當的層次進行思考:太高的層次過于抽象,而過低的層次容易讓我們揪住某些細節問題不放。在合适的層次思考才能夠最有效地想出恰當的解決方案。什麼層次才算“恰當”沒有簡單的定論,這取決于産品的規模和正在解決的問題。但是在碰到棘手問題的時候換個層面來重新考量往往能夠事倍功半。

3. 高效的産品維護

        軟體産品的日常維護主要包含排錯、Workaround、功能變更、架構重構等活動。對于新手來說,花個幾天時間找“臭蟲”實屬常見(有句順口溜說的好:“鋤禾日當午,不如coding苦,對着C++,一調一下午……”)。提高産品維護的效率就在很大程度上提高了程式員的生産力(因為有更多的時間編寫新代碼)。

多年的經驗顯示,以下要點對于提高産品維護效率、以及提高産品品質都有着不可忽視的作用:

編碼規範:編碼規範對于軟體品質的影響怎麼強調都不過分。程式代碼作為一種文檔,首先是供人類閱讀的。好的編碼規範能夠有效地降低錯誤率、提高可讀性、以及極大地曾強代碼的可維護性。換個角度來看,代碼隻寫一次,但卻需要斷地進行閱讀、了解、修改等維護工作。沒人願意接手維護一大堆完全看不懂的代碼,在書寫時始終遵循一套完善的編碼規範則可以在很大程度上緩解這些問題。編碼規範對程式品質和可維護性方面的影響就像鞋對人類的影響一樣:都屬于效果巨大,但跟沒體會過的人很難描述清楚的事情——真是誰用誰知道。這可能也是為什麼那麼多公司都在強調它,同時那麼多程式員卻又不重視它的原因所在——光腳的不怕穿鞋的!

錯誤處理和日志:優秀的錯誤處理和日志記錄方式能節省大量排錯時間,同時揭示産品改進的重要線索。在另一本世界名著《Code Complete》中,作者憑借其多年的軟體行業經驗以及NASA、IBM等各大組織的長期研究報告得出結論:“在絕大多數項目中,最大規模的活動就是調試以及修改那些不能正常工作的代碼……消除軟體缺陷實際上是最昂貴且最耗時的軟體工作”。

這方面牽扯到的技術和技巧太多太雜,以至于沒有辦法給出一個總結性的描述。是以,這裡隻能以日常維護場景為例,舉一個簡單的例子: 試想一下,你有一個可以向系統syslog、網絡syslog server、磁盤檔案、Event Log Service等等各種日志目的實時發送日志消息的記錄器,它能夠工作在調用線程或獨立的線程/程序中,就算程序崩潰它也能夠捕捉到足夠詳細的錯誤資訊,即使整個系統崩潰了也不會丢失重要的日志消息。與此同時,你所有元件中的任何操作發生錯誤時,都會準确地将該操作的錯誤資訊、操作過程中産生錯誤的子產品資訊、該子產品調用的底層平台API以及這個API傳回的錯誤資訊等等的各級出錯詳細資訊都按照層次結構如實地記錄到日志中。在這樣的環境中,無論是排錯還是調優是不是都會點單很多呢? 正如前文所述,日志記錄和錯誤處理是個很大的話題,足以著書立說。這個例子也隻是揭示其冰山一角,從一個小小的側面來反映一套優秀的日志記錄和錯誤處理機制對排錯和性能分析之類的活動有多大幫助。

環境和工具:熟練掌握開發編輯、分析統計、調試跟蹤等工具。優秀的開發、分析、和調試環境可以節省大量時間。盡量避免在不熟悉或者較“艱苦”的環境下分析和調試程式。當然,有時也會有一些不可避免的情況出現。比如:程式的最終目标平台是基于MIPS架構的NetBSD,但開發人員最熟悉,各類工具最齊全的開發環境确實 x86平台下的Windows系統。對此,我們的解決方案是:實作一套與跨平台的應用支撐架構,這個支援架構向上封裝所有平台相關的功能,為上層應用提供統一的,平台無關的API。然後使用該支撐架構在Windows(x86)環境完成應用的開發、分析和調試工作,然後在NetBSD(MIPS)平台上重新編譯并釋出。

其它因素:産品維護的成本還會受到很多其它因素的影響。比如在設計之初以3W原則精心衡量過的方案可以避免很多後期不必要的變更;比如重用已經經過驗證的可靠元件不但節省了開發成本,而且也避免了重新實作産生的缺陷;再比如經驗、設計思想和品味對設計産生的影響等等。

4. 标準化和重用

        标準化本質上就是為重用做準備。想要擁有大量标準的可重用元件需要長時間的積累。當然,我們這裡提到的“标準化”并不僅僅是指ISO/IEC之類的國際标準或者GB之類的國家标準。實際上,小到公司甚至是個人也可以有自己的内部标準(其實編碼規範就屬于這種内部标準之一)。C++之父Bjarne Stroustrup曾經說過:産品開發就是重用已有标準元件、實作新的标準元件、然後将它們粘接起來的過程。

想要自己實作一套完備的标準元件庫當然需要長時間的積累,但如果利用其它人已經實作的

現成庫呢?我們知道,當今的開源時代,能夠免費取用的第三方功能庫數不勝數。但是使用第三方庫的成本也不低。具體表現在幾方面:首先,要用好它,你需要閱讀大量代碼和文檔;其次,掌握一個庫不光是會使用即可,一旦把它用到自己的産品裡,那麼當它發生問題時你要修正、當它不滿足要求時你要修改和重構、當它缺少功能時你要添加……也就是說,一旦在自己的産品中使用了一套第三方庫,那麼維護它就是你的義務,而要維護一大堆代碼,前提是你需要先讀懂這些代碼。最後,第三方庫的架構和品質不一定滿足你的要求,而且這個問題可能到最後才會被發現。

例如:由于架構的限制,Windows IOCP機制無法被在Boost庫asio元件良好地支援。如果你的産品使用了asio元件,并且在開發的最後階段才考慮需要提供針對Windows平台的支援,那麼你将會面臨艱難選擇。再比如:你需要一套跨平台C/C++架構,但Boost、Mozilla NSPR、Apache APR、GNU Common C++ 等均無法滿足你的需求(舉個最簡單的例子:他們都支援對線程設定CPU粘滞屬性)這也使你面臨兩難的選擇:是選擇其中一個庫,大規模的修改它以滿足你的功能需求,并且忍受這個實作從架構到編碼規範中所有你看不順眼的地方,同時還要日複一日地将官方更新檔合并到你自己的私人分支。還是索性從頭開始建立自己的庫?

當然,無論你如何選擇,這些代價都是值得的。因為我們知道,程式員的生産力基本等同于他在機關時間内産出的有效代碼行數與其中每行代碼平均表達能力的乘積。即:

生産力=有效代碼行數×每行代碼的表達能力

大量使用标準元件不光獲得了由于元件被反複使用因而比較成熟的優點,而且也使得每行代碼的平均表達能力大大提高(比如:标準元件通過成千上萬行代碼封裝好的一個功能,使用一行代碼即可調用),這就使得程式員的生産力能夠以乘積的形式增長。

5. 經驗和設計思想

        經驗和個人品味是永遠也無法被替代的。Stroustrup曾說:任何一個成功的産品中都必然充斥着其作者的味道。這話雖然有一定誇張的成分,但也不可否認,設計者的經驗和品味會極大地影響産品的最終品質。而設計思想其實是來自于經驗的總結和升華,當然其中也糅合了個人的品味與喜好。是以,設計思想隻有成熟和幼稚的差別,而成熟的設計思想之間很難有好壞之分——成熟的思維各有各的老練之處,但所有人幼稚起來則是大同小異的。

6. 人品(态度)

        這标題看起來有點怨天尤人的意思。但我們這裡提到的人品既不是傳統上的“忠孝禮義仁智信”,也不是現代網絡用語中“運氣”的意思。這裡主要是指程式員的責任感和追求完美的态度,還有樂于與人交流的生活方式。竊以為缺乏這幾樣東西的人無論如何都不可能成為真正優秀的程式員。

總結

繼續閱讀