注重實效的程式員(The Pragmatic Programmer)
推薦一本好書 《The Pragmatic Programmer - From journeyman to master》
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
我們想要看到對所有權的自豪。“這是我編寫的,我對自己的工作負責。”你的簽名應該被視為品質的保證。當人們在一段代碼上看到你的名字時,應該期望它是可靠的、用心編寫的、測試過的和有文檔的,一個真正的專業作品,由真正的專業人員編寫。
一個注重實效的程式員。