注重實效的程式設計師

一劍平江湖發表於2013-10-26


1、關心你的技藝 
Care About Your Craft 
除非你在乎能否漂亮地開發出軟體,否則其它事情都是沒有意義的。

2、思考!你的工作 
Think!About Your Work 
在你做某件事情的時候思考你在做什麼。不間斷地思考,實時地批判你的工作。這將佔據你的一些寶貴時間,酬勞則是更為活躍地參與你喜愛的工作、感覺到自己在掌握範圍日增的各種主題以及因感受到持續的進步而歡愉。從長遠來說,你在時間上的投入將會隨著你和你的團隊變得更為高效、編寫出更易於維護的程式碼以及開會時間的減少而得到回報。 

3、提供各種選擇,不要找蹩腳的藉口 
Provide Options,Don't Make Lame Excuses 
不要說事情做不到;要說明能夠做什麼來挽回局面。不要害怕提出要求,也不要害怕承認你需要幫助。

4、不要容忍破窗戶 
Don't Live With Broken Windows 
不要留著“破窗戶”(低劣的設計、錯誤的決策、或者糟糕的程式碼)不修。發現一個就修一個。如果沒有足夠的時間進行適當的修理,採取某種行動防止進一步的破壞,並說明情勢處在你的控制之下。 
如果你發現你所在團隊和專案的程式碼十分漂亮——編寫整潔、設計良好,並且很優雅,不會想成為第一個弄髒東西的人。

5、做變化的催化劑 
Be a Catalyst for Change 
你不能強迫人們改變。相反,要向他們展示未來可能會怎樣,並幫助他們參與對未來的創造。 
設計出你可以合理要求的東西,好好開發它。一旦完成,就拿給大家看,讓他們大吃一驚。然後說:“要是我們增加...可能就會更好。”假裝那並不重要。坐回椅子上,等著他們開始要你增加你本來就想要的功能。人們發現,參與正在發生的成功要更容易。讓他們瞥見未來,你就能讓他們聚集在你周圍。

6、記住大圖景 
Remember the Big Picture 
如果你抓一隻青蛙放進沸水裡,它會一下子跳出來。但是,如果你把青蛙放進冷水裡,然後慢慢加熱,青蛙不會注意到溫度的緩慢變化,會呆在鍋裡,直到被煮熟。 
不要像青蛙一樣。留心大圖景。要持續不斷地觀察周圍發生的事情,而不只是你自己在做的事情。

7、使質量成為需求問題 
Make Quality a Requirements Issue 
你所製作的系統的範圍和質量應該作為系統需求的一部分規定下來。讓你的使用者參與權衡,知道何時止步,提供足夠好的軟體

8、定期為你的知識資產投資 
Invest Regularly in Your Knowledge Portfolio

讓學習成為習慣。 持續投入十分重要。一旦你熟悉了某種新語言或新技術,繼續前進,學習另一種。 是否在某個專案中使用這些技術,或者是否把它們放入你的簡歷,這並不重要。學習的過程將擴充套件你的思維,使你向著新的可能性和新的做事方式擴充。思維的“異花授粉”十分重要;設法把你學到的東西應用到你當前的專案中。即使你的專案沒有使用該技術,你或許也能借鑑一些想法。例如,熟悉了物件導向,你就會用不同的方式編寫純C程式。 如果你自己找不到答案,就去找出能找到答案的人。不要把問題擱在那裡。

 

9、批判地分析你讀到的和聽到的 
Critically Analyze What You Read and Hear 
不要被供應商、媒體炒作、或教條左右。要依照你自己的看法和你的專案的情況去對資訊進行分析。

10、你說什麼和你怎麼說同樣重要 
It's Both What You Say and the Way You Say It

作為開發者,我們必須在許多層面上進行交流。我們的時間有很大部分都花在交流上,所以我們需要把它做好。 如果你不能有效地向他人傳達你的了不起的想法,這些想法就毫無用處。 知道你想要說什麼;瞭解你的聽眾;選擇時機;選擇風格;讓文件美觀;讓聽眾參與;做傾聽者;回覆他人。 交流越有效,你就越有影響力。

 

11、DRY原則——不要重複你自己 
DRY - Don't Repeat Yourself 
系統中的每一項知識都必須具有單一、無歧義、權威的表示。與此不同的做法是在兩個或更多地方表達同一事物。如果你改變其中一處,你必須記得改變其它各處。這不是你能否記住的問題,而是你何時忘記的問題。

12、讓複用變得容易 
Make it Easy to Reuse 
你要做的是營造一種環境,在其中要找到並複用已有的東西,比自己編寫更容易。如果複用很容易,人們就會去複用。而如果不復用,你們就會有重複知識的風險。

13、消除無關事物之間的影響 
Eliminate Effects Between Unrelated Things 
我們想要設計自足(self-contained)的元件:獨立,具有單一、良好定義的目的。如果元件是相互隔離的,你就知道你能夠改變其中一個,而不用擔心其餘元件。只要你不改變元件的外部介面,你就可以放心:你不會造成波及整個系統的問題。 
你得到兩個主要好處:提高生產率與降低風險。

14、不存在最終決策 
There Are No Final Decisions 
沒有什麼永遠不變——而如果你嚴重依賴某一事實,你幾乎可以確定它將會變化。與我們開發軟體的速度相比,需求、用以及硬體變得更快。通過DRY原則解耦以及後設資料的使用,我們不必做出許多關鍵的、不可逆轉的決策。有許多人會設法保持程式碼的靈活性,而你還需要考慮維持架、部署及供應商整合等領域的靈活性。

15、用曳光彈找到目標 
Use Tracer Bullets to Find the Target 
曳光彈能通過試驗各種事物並檢查它們離目標有多遠來讓你追蹤目標。 
曳光彈程式碼含有任何一段產品程式碼都擁有的完整的錯誤檢查、結構、文件、以及自查。它只不過功能不全而已。但是,一旦你在系統的各元件之間實現了端到端(end-to-end)的連線,你就可以檢查你離目標還有多遠,並在必要的情況下進行調整。一旦你完全瞄準,增加功能將是一件容易的事情。

16、為了學習而製作原型 
Prototype to Learn 
任何帶有風險的事物。以前沒有試過的事物,或是對於最終系統極其關鍵的事物。任何未被證明的、試驗性的、或有疑問的事物。任何讓你覺得不舒服的東西。都可以通過製作原型來研究。比如:架構;已有系統中的新功能;外部資料的結構或內容;第三方工具或元件;效能問題;使用者介面設計等等。 
原型製作是一種學習經驗,其價值並不在於所產生的程式碼,而在於所學到的經驗教訓。

17、靠近問題領域程式設計 
Program Close to The Problem domain 
計算機語言會影響你思考問題的方式,以及你看待交流的方式。用你的使用者的語言進行設計和編碼。

18、估算,以避免發生意外 
Estimate to Avoid Surprises 
在著手之前先進行估算。你將提前發現潛在的問題。 
1)要選擇能反映你想要傳達的精確度的單位; 
2)基本的估算訣竅:去問已經做過這件事情的人; 
3)理解提問內容; 
4)根據對問題的理解,建立粗略、就緒的思維模型骨架; 
5)把模型分解為元件,找出描述這些元件怎樣互動的數學規則,確定每個元件的引數; 
6)給每個引數指定值,找出哪些引數對結果的影響最大,並致力於讓它們大致正確; 
7)進行多次計算,改變關鍵引數的值,然後根據那些參數列達你的答案; 
8)在被要求進行估算時說的話:“我等會回答你”。

19、通過程式碼對進度表進行迭代 
Iterate the Schedule with the Code 
實行增量開發。追蹤你的估算能力,提煉對迭代次數、以及在每次迭代中可以包含的內容的猜想。提煉會變得一次比一次好,對進度表的信心也將隨之增長。你將給予管理部門你所能給予的最精確的進度估算。

20、用純文字儲存知識 
Keep Knowledge in Plain Text

保證不過時; 槓桿作用:每一樣工具,都能夠在純文字上進行操作; 更易於測試; 你需要確保所有各方能夠使用公共標準進行通訊。純文字就是那個標準。

 

21、利用命令shell的力量 
Use the Power of Command Shells 
GUI環境通常受限於它們的設計者想要提供的能力。當你想要快速地組合一些命令,以完成一次查詢或某種其他的任務時,命令列要更為適宜。多使用你的命令shell,你會驚訝它能使你的生產率得到怎樣的提高。

22、用好一種編輯器 
Use a Single Editor Well 
選一種編輯器,徹底瞭解它,並將其用於所有的編輯任務。如果你用一種編輯器進行所有的文字編輯活動,你就不必停下來思考怎樣完成文字操縱:必需的鍵擊將成為本能反應。編輯器將成為你雙手的延伸;鍵會在滑過文字和思想時歌唱起來。這就是我們的目標。

23、總是使用原始碼控制 
Always Use Source Code Control

總是。即使你的團隊只有你一個人,你的專案只有一週時間;確保每樣東西都處在原始碼控制之下。 原始碼控制是你的工作的時間機器——你能夠回到過去。 把整個專案置於原始碼控制系統的保護之下具有一項很大的、隱蔽的好處:你可以進行自動的和可重複的產品構建。

 

24、要修正問題,而不是發出指責 
Fix the Problem,Not the Blame 
要接受事實:除錯就是解決問題,要據此發起進攻。Bug是你的過錯還是別人的過錯,並不是真的很有關係。它仍然是你的問題。

25、不要恐慌 
Don't Panic 
做一次深呼吸,思考什麼可能是bug的原因。

要總是設法找出問題的根源,而不只是問題的特定表現; 蒐集所有的相關資料; 開始修正bug的最佳途徑是讓其可再現; 使你的資料視覺化; 跟蹤:觀察程式或資料結構隨時間變化的狀態; 找到問題的原因的一種非常簡單、卻又特別有用的技術是向別人解釋它。你只是一步步解釋程式碼要做什麼,常常就能讓問題從螢幕上跳出來,宣佈自己的存在。

 

26、“Select”沒有問題 
"Select" Isn't Broken 
Bug有可能存在於OS、編譯器、或是第三方產品中——但這不應該是你的第一想法。有大得多的可能性的是,bug存在於正在開發的應用程式碼中。與假定庫本身出了問題相比,假定應用程式碼對庫的呼叫不正確通常更有好處。即使問題確實應歸於第三方,在提交bug報告之前,你也必須先消除你的程式碼中的bug。

27、不要假定,要證明 
Don't Assume it - Prove It 
不要因為你“知道”它能工作而輕易放過與bug有牽連的例程或程式碼。證明它。在實際環境中——使用真正的資料和邊界條件——證明你的假定。

28、學習一種文字操作語言 
Learn a Text Manipulation Language 
你用每天的很大一部分時間處理文字,為什麼不讓計算機替你完成部分工作呢? 
應用示例:

資料庫schema維護; Java、C#屬性(Property)訪問; 測試資料生成。

 

29、編寫能編寫程式碼的程式碼 
Write Code That Writes Code 
程式碼生成器能提高你的生產率,並有助於避免重複。

30、你不可能寫出完美的軟體 
You Can't Write Perfect Software 
這刺痛了你?不應該。把它視為生活的公理,接受它,擁抱它,慶祝它。因為完美的軟體不存在。在計算機簡短的歷史中,沒有一個人曾經寫出過一個完美的軟體。你也不大可能成為第一個。除非你把這作為事實接受下來,否則你最終會把時間和精力浪費在追逐不可能實現的夢想上。

31、通過合約進行設計 
Design with Contracts 
什麼是正確的程式?不多不少,做它宣告要做的事情的程式。用文件記載這樣的宣告,並進行校驗,是按合約設計(簡稱DBC)的核心所在。 
這裡,強調的重點是在“懶惰”的程式碼上:對在開始之前接受的東西要嚴格,而允諾返回的東西要儘可能少。 
使用DBC的最大好處也許是它迫使需求與保證的問題走到前臺來。在設計時簡單地列舉輸入域的範圍是什麼、邊界條件是什麼、例程允諾交付什麼——或者,更重要的,它不允諾交付什麼——是向著編寫更好的軟體的一次飛躍。不對這些事項作出陳述,你就回到了靠巧合程式設計,那是許多專案開始、結束、失敗的地方。

32、早崩潰 
Crash Early 
死程式不說謊。 
當你的程式碼發現,某件被認為不可能發生的事情已經發生時,你的程式就不再有存活能力。從此時開始,它所做的任何事情都會變得可疑,所以要儘快終止它。死程式帶來的危害通常比有問題的程式要小得多。

33、如果它不可能發生,用斷言確保它不會發生 
If It Can't Happen,Use Assertions to Ensure That It Won't 
斷言驗證你的各種假定。在一個不確定的世界裡,用斷言保護你的程式碼。 
不要用斷言代替真正的錯誤處理。斷言檢查的是決不應該發生的事情。

34、將異常用於異常的問題 
Use Exceptions for Exceptional Problems 
異常表示即使的、非區域性的控制轉移——這是一種級聯的(cascading)goto。異常應保留給意外事件。那些把異常用作其正常處理的一部分的程式,將遭受所有可讀性和可維護性問題的折磨。這些程式破壞了封裝:通過異常處理,例程和它們的呼叫者被更緊密地耦合在一起。

35、要有始有終 
Finish What You Start 
只要可能,分配某資源的例程或物件也應該負責解除其分配。

36、使模組之間的耦合減至最少 
Minimize Coupling Between Modules

編寫“羞怯”的程式碼; 函式的得墨忒耳(Demeter)法則規定,某個物件的任何方法都應該只呼叫屬於以下情形的方法: 
1)它自身; 
2)傳入該方法的任何引數; 
3)它建立的任何物件; 
4)任何直接持有的元件物件。 物理解耦。

 

37、要配置,不要整合 
Configure,Don't Integrate 
細節會弄亂我們整潔的程式碼——特別是如果它們經常變化。把它們趕出程式碼。當我們在與它作鬥爭時,我們可以讓我們的程式碼變得高度可配置和“軟和”——也就是,容易適應變化。 
要用後設資料(metadata)描述應用的配置選項:調諧引數、使用者偏好(user preference)、安裝目錄,等等。

38、將抽象放進程式碼,細節放進後設資料 
Put Abstractions in Code,Details in Metadata 
但我們不只是想把後設資料用於簡單的偏好。我們想要儘可能多地通過後設資料配置和驅動應用。我們的目標是以宣告方式思考(規定要做什麼,而不是怎麼做),並建立高度靈活和可適應的應用。我們通過採用一條一般準則來做到這一點:為一般情況編寫程式,把具體情況放在別處——在編譯的程式碼庫之外。 
也許你在編寫一個具有可怕的工作流需求的系統。動作會根據複雜的(和變化的)商業規則啟動和停止。考慮在某種基於規則的系統(即專家系統)中對它們進行編碼,並嵌入到你的應用中。這樣,你將通過編寫規則、而不是修改程式碼來配置它。

39、分析工作流,以改善併發性 
Analyze Workflow to Improve Concurrency 
時間是軟體架構的一個常常被忽視的方面。時間有兩個方面對我們很重要:併發(事情在同一時間發生)和次序(事情在時間中的相對位置)。 
我們在編寫程式時,通常並沒有把這兩個方面放在心上。當人們最初坐下來開始設計架構,或是編寫程式碼時,事情往往是線性的。那是大多數人的思考方式——總是先做這個,然後再做那個。但這樣思考會帶來時間耦合:方法A必須總是在方法B之前呼叫;同時只能執行一個報告;在接收到按鈕點選之前,你必須等待螢幕重畫。“嘀”必須在“嗒”之前發生。 
這樣的方法不那麼靈活,也不那麼符合實際。 
我們需要容許併發,並考慮解除任何時間或者次序上的依賴。

40、用服務進行設計 
Design Using Services 
實際上我們建立的並不是元件,而是服務——位於定義良好的、一致的介面之後的獨立、併發的物件。 
通過把你的系統架構成多個獨立的服務,你可以讓配置成為動態的。

41、總是為併發進行設計 
Always Design for Concurrency 
首先,必須對任何全域性或靜態變數加以保護,使其免於併發訪問,現在也許是問問你自己、你最初為何需要全域性變數的好時候。此外,不管呼叫的次序是什麼,你都需要確保你給出的是一致的狀態資訊。 
在被呼叫時,物件必須總是處在有效的狀態中,而且它們可能會在最尷尬的時候被呼叫。你必須確保,在任何可能被呼叫的時刻,物件都處在有效的狀態中。這一問題常常出現在構造器與初始化例程分開定義的類中(構造器沒有使物件進入已初始化狀態)。 
一旦你設計了具有併發要素的架構,你可以靈活地處理應用的部署方式:單機、客戶-伺服器、或是n層。

42、使檢視與模型分離 
Separate Views from Models 
也就是常說的MVC模式(Model-View-Controller)。

模型。表示目標物件的抽象資料模型。模型對任何檢視或控制器都沒有直接的瞭解。 檢視。解釋模型的方式。它訂閱模型中的變化和來自控制器的邏輯事件。 控制器。控制檢視、並向模型提供新資料的途徑。 
通過鬆解模型與檢視/控制器之間的耦合,你用低廉的代價為自己換來了許多靈活性。

 

43、用黑板協調工作流 
Use Blackboards to Coordinate Workflow 
用黑板協調完全不同的事實和因素,同時又使各參與方保持獨立和隔離。 
現代的分散式類黑板(blackboard-like)系統,比如JavaSpaces和T Spaces。

44、不要靠巧合程式設計 
Don't Program by Coincidence

總是意識到你在做什麼。 不要盲目地程式設計。試圖構建你不完全理解的應用,或是使用你不熟悉的技術,就是希望自己被巧合誤導。 按照計劃行事。 依靠可靠的事物。如果你無法說出各種特定情形的區別,就假定是最壞的。 為你的假定建立文件。“按合約程式設計”有助於澄清你頭腦中的假定,並且有助於把它們傳達給別人。 不要只是測試你的程式碼,還要測試你的假定。 為你的工作劃分優先順序。 不要做歷史的奴隸。不要讓已有的程式碼支配將來的程式碼。 
所以下次有什麼東西看起來能工作,而你卻不知道為什麼,要確定它不是巧合。

 

45、估算你的演算法的階 
Estimate the Order of Your Algorithms 
在你編寫程式碼之前,先大致估算事情需要多長時間。

46、測試你的估算 
Test Your Estimates 
對演算法的數學分析並不會告訴你每一件事情。在你的程式碼的目標環境中測定它的速度。

47、早重構,常重構 
Refactor Early,Refactor Often 
在需要時對程式碼進行重寫、重做和重新架構。要剷除問題的根源。不要容忍破窗戶。 
關於重構,詳見Martin Fowler的《重構》一書。

48、為測試而設計 
Design to Test 
在你還沒有編寫程式碼時就開始思考測試問題。測試驅動開發?

49、測試你的軟體,否則你的使用者就得測試 
Test Your Software,or Your Users Will 
測試是技術,但更是文化。一點預先的準備可以大大降低維護費用、減少客戶服務電話。

50、不要使用你不理解的嚮導程式碼 
Don't Use Wizard Code You Don't Understand 
嚮導很了不起。只需要點選一個按鈕,回答一些簡單的問題,嚮導就會自動為你生成骨架程式碼(skeleton code)。但如果你使用嚮導,卻不理解它製作出的所有程式碼,你就無法控制你自己的應用。你沒有能力維護它,而且在除錯時會遇到很大的困難。

51、不要蒐集需求——挖掘它們 
Don't Gather Requirements - Dig for Them 
需求很少存在於表面上。它們深深地埋藏在層層假定、誤解和政治手段的下面。

52、與使用者一同工作,以像使用者一樣思考 
Work with a User to Think Like a User 
要了解系統實際上將如何被使用,這是最好的方法。開採需求的過程也是開始與使用者群建立和諧的關係、瞭解他們對你正在構建的系統的期許和希望的時候。

53、抽象比細節活得更長久 
Abstractions Live Longer than Details 
“投資”於抽象,而不是實現。抽象能在來自不同的實現和新技術的變化的“攻擊”之下存活下去。

54、使用專案詞彙表 
Use a Project Glossary 
如果使用者和開發者用不同的名稱指稱同一事物,或是更糟,用同一名稱指稱不同事物,這樣的專案很難取得成功。

55、不要在盒子外面思考——要找到盒子 
Don't Think Outside the Box - Find the Box 
在遇到不可能解決的問題時,問問自己以下問題:

有更容易的方法嗎? 你是在設法解決真正的問題,還是被外圍的技術問題轉移了注意力? 這件事情為什麼是一個問題? 是什麼使它如此難以解決? 它必須以這種方式完成嗎? 它真的必須完成嗎? 
很多時候,當你設法回答這些問題時,你會有讓自己吃驚的發現。很多時候,對需求的重新詮釋能讓整個問題全部消失。 
你所需要的只是真正的約束、令人誤解的約束、還有區分它們的智慧。

 

56、傾聽反覆出現的疑慮——等你準備好再開始 
Listen to Nagging Doubts - Start When You're Ready 
你的一生都在積累經驗與智慧。當你面對一件任務時,如果你反覆感覺到疑慮,或是體驗到某種勉強,要注意它。你可能無法準確地指出問題所在,但給它時間,你的疑慮很可能就會結晶成某種更堅實的東西,某種你可以處理的東西。軟體開發仍然不是科學。讓你的直覺為你的表演做出貢獻。

57、對有些事情“做”勝於“描述” 
Some Things Are Better Done Than Described 
你應該傾向於把需求蒐集、設計、以及實現視為同一個過程——交付高質量的系統——的不同方面。不要掉進規範的螺旋,在某個時刻,你需要開始編碼。

58、不要做形式方法的奴隸 
Don't Be a Slave to Formal Methods 
如果你沒有把某項技術放進你的開發實踐和能力的語境中,不要盲目地採用它。

59、昂貴的工具不一定能製作出更好的設計 
Expensive Tools Do Not Produce Better Designs 
小心供應商的炒作、行業教條、以及價格標籤的誘惑。在考察工具的產出時,試著不要考慮它值多少錢。

60、圍繞功能、而不是工作職務進行組織 
Organize Around Functionality,Not Job Functions 
把你的人劃分成小團隊,分別負責最終系統的特定方面的功能。讓團隊按照個人的能力,在內部自行進行組織。 
但是,只有在專案擁有負責的開發者、以及強有力的專案管理時,這種途徑才有效。創立一組自行其是的團隊並放任自流,是一種災難性的處方。 
要記住,團隊是由個體組成的。讓每個成員都能以他們自己的方式閃亮。

61、不要使用手工流程 
Don't Use Manual Procedures 
shell指令碼或批處理檔案會一次次地以同一順序執行同樣的指令。我們可以自動安排備份、夜間構建、網站維護、以及其他任何可以無人照管地完成的事情。讓計算機去做重複、庸常的事情——它會做得比我們更好。我們有更重要、更困難的事情要做。

62、早測試,常測試,自動測試。 
Test Early.Test Often.Test Automatically. 
與呆在書架上的測試計劃相比,每次構建時執行的測試要有效得多。

63、要等到通過全部測試,編碼才算完成 
Coding Ain't Done 'Til All the Tests Run 
就是這樣。

64、通過“蓄意破壞”測試你的測試 
Use Saboteurs to Test Your Testing 
在單獨的軟體副本上故意引人bug,以檢驗測試能夠抓住它們。

65、測試狀態覆蓋,而不是程式碼覆蓋 
Test State Coverage,Not Code Coverage 
確定並測試重要的程式狀態。只是測試程式碼行是不夠的。即時具有良好的程式碼覆蓋,你用於測試的資料仍然會有巨大的影響,而且,更為重要的是,你遍歷程式碼的次序的影響可能是最大的。

66、一個bug只抓一次 
Find Bugs Once 
一旦測試員找到一個bug,這應該是測試員最後一次找到它。此後自動測試應該對其進行檢查。

67、把英語當作又一種程式語言 
Treat English as Just Another Programming Language 
像你編寫程式碼一樣編寫文件:遵守DRY原則使用後設資料MVC自動生成,等等。

68、把文件建在裡面,不要拴在外面 
Build Documentation In,Don't Bolt It On 
與程式碼分離的文件不太可能被修正和更新。使用像JavaDoc和NDoc這樣的工具,我們可以根據原始碼生成API級的文件。 
文件和程式碼是同一底層模型的不同檢視,但檢視是唯一應該不同的東西。

69、溫和地超出使用者的期望 
Gently Exceed Your Users' Expectations 
要設法讓你的使用者驚訝。請注意,不是驚嚇他們,而是要讓他們高興。要理解使用者的期望,然後給他們的東西要多那麼一點。給系統增加某種面向使用者的特性所需的一點額外努力將一次又一次在商譽上帶來回報。

70、在你的作品上簽名 
Sign Your Work 
我們想要看到對所有權的自豪。“這是我編寫的,我對自己的工作負責。”你的簽名應該被視為質量的保證。當人們在一段程式碼上看到你的名字時,應該期望它是可靠的、用心編寫的、測試過的和有文件的,一個真正的專業作品,由真正的專業人員編寫。 
一個注重實效的程式設計師

相關文章