高效程序員的45 個習慣

原文來自:http://blog.csdn.net/turingbooks/archive/2009/11/21/4840453.aspx

 

【45個習慣】

1    做事

2    快速修復變成了快速流沙

3    對事不對人

4    排除萬難,奮勇前進

5    跟蹤變化

6    對團隊投資

7    懂得丟棄

8    打破砂鍋問到底

9    把握開發節奏

10  讓客戶做決定

11  讓設計指導開發,而不是操縱開發

12  合理地使用技術

13  保持可以發佈

14  提早集成,頻繁集成

15  提早實現自動化部署

16  頻繁地演示獲得用戶反饋

17  使用短迭代,增量發佈

18  固定的價格就意味着背叛承諾

19  守護天使

20  先用它再實現它

21  不同環境,就有不同問題

22  自動驗收測試

23  度量真實的進度

24  傾聽用戶的聲音

25  代碼要清晰地表達意圖

 

代碼要清晰地表達意圖

—— 高效程序員的 45 個習慣之 習慣25

 

“可以工作而且易於理解的代碼挺好,但是讓人覺得聰明更加重要。別人給你錢是因爲你腦子好使,讓我們看看你到底有多聰明。”
 

Hoare 談軟件設計

C.A.R. Hoare

設計軟件有兩種方式。一種是設計得儘量簡單,並且明顯沒有缺陷。另一種方式是設計得儘量複雜,並且沒有明顯的缺陷。

我們大概都見過不少難以理解和維護的代碼,而且(最壞的是)還有錯誤。當開發人員們像一羣旁觀者見到 UFO 一樣圍在代碼四周,同樣也感到恐懼、困惑與無助時,這個代碼的質量就可想而知了。如果沒有人理解一段代碼的工作方式,那這段代碼還有什麼用呢?

 

開發代碼時,應該更注重可讀性,而不是隻圖自己方便。代碼被閱讀的次數要遠遠超過被編寫的次數,所以在編寫的時候值得花點功夫讓它讀起來更加簡單。實際上,從衡量標準上來看,代碼清晰程度的優先級應該排在執行效率之前。

 

例如,如果默認參數或可選參數會影響代碼可讀性,使其更難以理解和調試,那最好明確地指明參數,而不是在以後讓人覺得迷惑。

 

在改動代碼以修復 bug 或者添加新功能時,應該有條不紊地進行。首先,應該理解代碼做了什麼,它是如何做的。接下來,搞清楚將要改變哪些部分,然後着手修改並進行測試。作爲第 1 步的理解代碼,往往是最難的。如果別人給你的代碼很容易理解,接下來的工作就省心多了。要敬重這個黃金法則,你欠他們一份情,因此也要讓你自己的代碼簡單、便於閱讀。

 

明白地告訴閱讀程序的人,代碼都做了什麼,這是讓其便於理解的一種方式。讓我們看一些例子。

 

coffeeShop.PlaceOrder(2);

 

通過閱讀上面的代碼,可以大致明白這是要在咖啡店中下一個訂單。但是, 2 到底是什麼意思?是意味着要兩杯咖啡?要再加兩次?還是杯子的大小?要想搞清楚,唯一的方式就是去看方法定義或者文檔,因爲這段代碼沒有做到清晰易懂。

 

所以我們不妨添加一些註釋。

 

coffeeShop.PlaceOrder(2 /* large cup */);

 

現在看起來好一點了,但是註釋有時候是用來對寫得很差的代碼進行補償的(見第 105 頁中習慣 26 用代碼溝通

 

Java 5 .NET 中有枚舉值的概念,我們不妨使用一下。使用 C# ,我們可以定義一個名爲 CoffeeCupSize 的枚舉,如下所示。

 

public enum CoffeeCupSize

{

  Small,

  Medium,

  Large

              }

接下來就可以用它來下單要咖啡了。

 

        coffeeShop.PlaceOrder(CoffeeCupSize.Largxe);

 

這段代碼就很明白了,我們是要一個大杯 [① ] 的咖啡。


作爲一個開發者,應該時常提醒自己是否有辦法讓寫出的代碼更容易理解。下面是另一個例子。

 

Line 1   public int compute(int val)

     -  {

     -     int result = val << 1;

     -    //... more code ...

     5     return result;

     -  }

3 行中的位移操作符是用來幹什麼的?如果善於進行位運算,或者熟悉邏輯設計或彙編編程,就會明白我們所做的只是把 val 的值乘以 2

 

      PIE 原則

      所寫的代碼必須明確表達你的意圖,而且必須富有表現力。這樣可以讓代碼更易於被別人閱讀和理解。代碼不讓人迷惑,也就減少了發生潛在錯誤的可能。代碼要清晰地表達意圖。

 

但對沒有類似背景的人們來說,又會如何 —— 他們能明白嗎?也許團隊中有一些剛剛轉行做開發、沒有太多經驗的成員。他們會撓頭不已,直到把頭髮抓下來 ] 。代碼執行效率也許很高,但是缺少明確的意圖和表現力。

 

用位移做乘法,是在對代碼進行不必要且危險的性能優化。 result=val*2 看起來更加清晰,也可以達到目的, 而且對於某種給定的編譯器 來說 ,可能效率更高(積習難改,見第 34 頁的習慣 7 )。不要表現得好像很聰明似的,要遵循 PIE 原則:代碼要清晰地表達意圖。

 

要是違反了 PIE 原則,造成的問題可就不只是代碼可讀性那麼簡單了 —— 它會影響到代碼的正確性。下列代碼是一個 C# 方法,試圖同步對 CoffeeMaker MakeCoffee() 方法進行調用。

 

Public void MakeCoffee()

{

    lock(this)

    {

      // ... operation

    }

}

 

這個方法的作者想設置一個臨界區( critical section —— 任何時候最多隻能有一個線程來執行 operation 中的代碼。要達到這個目的,作者在 CoffeeMaker 實例中聲明瞭一個鎖。一個線程只有獲得這個鎖,才能執行這個方法。(在 Java 中,會使用 synchronized 而不是 lock ,不過想法是一樣的。)

 

對於 Java .NET 程序員來說,這樣寫順理成章,但是其中有兩個小問題。首先,鎖的使用影響範圍過大;其次,對一個全局可見的對象使用了鎖。我們進一步來看看這兩個問題。

 

假設 Coffeemaker 同時可以提供熱水,因爲有些人希望早上能夠享用一點伯爵紅茶。我想同步 GetWater() 方法,因此調用其中的 lock(this) 。這會同步任何在 CoffeeMaker 上使用 lock 的代碼,也就意味着不能同時製作咖啡以及獲取熱水。這是開發者原本的意圖嗎?還是鎖的影響範圍太大了?通過閱讀代碼並不能明白這一點,使用代碼的人也就迷惑不已了。

 

同時, MakeCoffee() 方法的實現在 CoffeeMaker 對象上聲明瞭一個鎖,而應用的其他部分都可以訪問 CoffeeMaker 對象。如果在一個線程中鎖定了 CoffeeMaker 對象實例,然後在另外一個線程中調用那個實例之上的 MakeCoffee() 方法呢?最好的狀況也會執行效率很差,最壞的狀況會帶來死鎖。

 

讓我們在這段代碼上應用 PIE 原則,通過修改讓它變得更加明確吧。我們不希望同時有兩個或更多的線程來執行 MakeCoffee() 方法。那爲什麼不能爲這個目的創建一個對象並鎖定它呢?

 

Private object makeCoffeeLock = new Object();

 

Public void MakeCoffee()

{

    lock (makeCoffeeLock)

    {

      // ... operation

    }

}

 

這段代碼解決了上面的兩個問題 —— 我們通過指定一個外部對象來進行同步操作,而且更加明確地表達了意圖。

 

在編寫代碼時,應該使用語言特性來提升表現力。使用方法名來傳達意向,對方法參數的命名要幫助讀者理解背後的想法。異常傳達的信息是哪些可能會出問題,以及如何進行防禦式編程,要正確地使用和命名異常。好的編碼規範可以讓代碼變得易於理解,同時減少不必要的註釋和文檔。

 

要編寫清晰的而不是討巧的代碼
向代碼閱讀者明確表明你的意圖。可讀性差的代碼一點都不聰明。
 
切身感受

應該讓自己或團隊的其他任何人,可以讀懂自己一年前寫的代碼,而且只讀一遍就知道它的運行機制。

平衡的藝術

  • 現在對你顯而易見的事情,對別人可能並不顯然,對於一年以後的你來說,也不一定顯然。不妨將代碼視作不知道會在未來何時打開的一個時間膠囊。
  • 不要明日復明日。如果現在不做的話,以後你也不會做的。
  • 有意圖的編程並不是意味着創建更多的類或者類型。這不是進行過分抽象的理由。
  • 使用符合當時情形的耦合。例如,通過散列表進行松耦合,這種方式適用於在實際狀況中就是松耦合的組件。不要使用散列表存儲緊密耦合的組件,因爲這樣沒有明確表示出你的意圖。

 


 

 


[ ]      對星巴克的粉絲來說,這是指 venti

[ ]   沒錯,那不是一塊禿頂,而是一個編程機器的太陽能電池板。

 

26  用代碼溝通

27  動態評估取捨

 

動態評估取捨

——高效程序員的45個習慣之習慣27

“性能、生產力、優雅、成本以及上市時間,在軟件開發過程中都是至關重要的因素。每一項都必須達到最理想狀態。”
 

可能曾經身處這樣的團隊:管理層和客戶 將很大一部分注意力都放在應用的界面展示上。也有這樣的團隊,其客戶認爲性能表現非常重要。在團隊中,你可能會發現,有這樣一個開發主管或者架構師,他會 強調遵守“正確”的範式比其他任何事情都重要。對任何單個因素如此獨斷地強調,而不考慮它是否是項目成功的必要因素,必然導致災難的發生。

 

強調性能的重要性情有可原,因爲惡劣的 性能表現會讓一個應用在市場上鎩羽而歸。然而,如果應用的性能已經足夠好了,還有必要繼續投入精力讓其運行得更快一點嗎?大概不用了吧。一個應用還有很多 其他方面的因素同樣重要。與其花費時間去提升千分之一的性能表現,也許減少開發投入,降低成本,並儘快讓應用程序上市銷售更有價值。

 

舉例來說,考慮一個必須要與遠程 Windows 服務器進行通訊的 .NET Windows 應用程序。可以選擇使用 .NET Remoting 技術或 Web Services 來實現這個功能。現在,針對使用 Web Services 的提議,有些開發者會說:“我們要在 Windows 之間進行通信,通常此類情況下,推薦使用 .NET Remoting 。而且, Web Services 很慢,我們會遇到性能問題。”嗯,一般來說確實是這樣。

 

然而,在這個例子中,使用 Web Services 很容易開發。對 Web Services 的性能測試表明 XML 文檔很小,並且相對應用程序自己的響應時間來講,花在創建和解析 XML 上的時間幾乎可以忽略不計。使用 Web Services 不但可以在短期內節省開發時間,且在此後團隊被迫使用第三方提供的服務時, Web Services 也是個明智的選擇。

 

Andy 說。。。
過猶不及
我曾經遇到這樣一個客戶,他們堅信可配置性的重要性,致使他們的應用有大概10 000個可配置變量。新增代碼變得異常艱難,因爲要花費大量時間來維護配置應用程序和數據庫。但是他們堅信需要這種程度的靈活性,因爲每個客戶都有不同的需求,需要不同的設置。
可實際上,他們只有19個客戶,而且預計將來也不會超過50個。他們並沒有很好地去權衡。

 

考慮這樣一個應用,從數據庫中讀取數據,並以表格方式顯示。你可以使用一種優雅的、面向對象的方式,從數據庫中取數據,創建對象,再將它們返回給 UI 層。在 UI 層中,你再從對象中拆分出數據,並組織爲表格方式顯示。除了看起來優雅之外,這樣做還有什麼好處嗎?

 

也許你只需要讓數據層返回一個 dataset 或數據集合,然後用表格顯示這些數據即可。這樣還可以避免對象創建和銷燬所耗費的資源。如果需要的只是數據展示,爲什麼要創建對象去自找麻煩呢?不按書上說的 OO 方式來做,可以減少投入,同時獲得性能上的提升。當然,這種方式有很多缺點,但問題的關鍵是要 多長個心眼兒 ,而不是總按照習慣的思路去解決問題。

 

總而言之,要想讓應用成功,降低開發成本與縮短上市時間,二者的影響同樣重要。由於計算機硬件價格日益便宜,處理速度日益加快,所以可在硬件上多投入以換取性能的提升,並將節省下來的時間放在應用的其他方面。

 

當然,這也不完全對。如果硬件需求非常龐大,需要一個巨大的計算機網格以及衆多的支持人員才能維持其正常運轉(比如類似 Google 那樣的需求),那麼考慮就要向天平的另一端傾斜了。

 

但是誰來最終判定性能表現已經足夠好,或是應用的展現已經足夠“炫”了呢?客戶或是利益相關者必須進行評估,並做出相關決定(見第 45 頁中 習慣 10 )。如果團隊認爲性能上還有提升的空間,或者覺得可以讓某些界面看起來更吸引人,那麼就去諮詢一下利益相關者,讓他們決定應將重點放在哪裏。

 

沒有適宜所有狀況的最佳解決方案。你必須對手上的問題進行評估,並選出最合適的解決方案。每個設計都是針對特定問題的 —— 只有明確地進行評估和權衡,才能得出更好的解決方案。

 

沒有最佳解決方案 (No best solution)

 
動態評估權衡
考慮性能、便利性、生產力、成本和上市時間。如果性能表現足夠了,就將注意力放在其他因素上。不要爲了感覺上的性能提升或者設計的優雅,而將設計複雜化。
 

切身感受

即使不能面面俱到,你也應該覺得已經得到了最重要的東西 —— 客戶認爲有價值的特性。

平衡的藝術

  • 如果現在投入額外的資源和精力,是爲了將來可能得到的好處,要確認投入一定要得到回報(大部分情況下,是不會有回報的)。真正的高性能系統,從一開始設計時就在向這個方向努力。
  • 過早的優化是萬惡之源。
  • 過去用過的解決方案對當前的問題可能適用,也可能不適用。不要事先預設結論,先看看現在是什麼狀況。

 

28  增量式編程

29  保持簡單

30  編寫內聚的代碼

31  告知,不要詢問

32  根據契約進行替換

33  記錄問題解決日誌

 

記錄問題解決日誌

—— 高效程序員的 45 個習慣之習慣33

“在開發過程中是不是經常遇到似曾相識的問題?這沒關係。以前解決過的問題,現在還是可以解決掉的。”
 

面對問題(並解決它們)是開發人員的一種生活方式。 當問題發生時,我們希望趕緊把它解決掉。如果一個熟悉的問題再次發生,我們會希望記起第一次是如何解決的,而且希望下次能夠更快地把它搞定。然而,有時一 個問題看起來跟以前遇到的完全一樣,但是我們卻不記得是如何修復的了。這種狀況時常發生。

 

不能通過 Web 搜索獲得答案嗎?畢竟互聯網已經成長爲如此令人難以置信的信息來源,我們也應該好好加以利用。從 Web 上尋找答案當然勝過僅靠個人努力解決問題。可這是非常耗費時間的過程。有時可以找到需要的答案,有時除了找到一大堆意見和建議之外,發現不了實質性的解決方案。看到有多少開發人員遇到同樣的問題,也許會感覺不錯,但我們需要的是一個解決辦法。

 

要想得到更好的效果,不妨維護一個保存曾遇到的問題以及對應解決方案的日誌。這樣,當問題發生時,就不必說:“嘿,我曾碰到過這個問題,但是不記得是怎麼解決的了。”可以快速搜索以前用過的方法。工程師們已經使用這種方式很多年了,他們稱之爲 每日日誌 daylog )。

 

不要在同一處跌倒兩次                     Don't get burned twice
 

可以選擇符合需求的任何格式。下面這些條目可能會用得上。

  • 問題發生日期。
  • 問題簡述。
  • 解決方案詳細描述。
  • 引用文章或網址,以提供更多細節或相關信息。
  • 任何代碼片段、設置或對話框的截屏,只要它們是解決方案的一部分,或者可以幫助更深入地理解相關細節。

要將日誌保存爲可供計算機搜索的格式,就可以進行關鍵字搜索以快速查找細節。圖 7-1 展示了一個簡單的例子,其中帶有超鏈接以提供更多信息。

7-1  帶有超鏈接的解決方案條目示例

如果面臨的問題無法在日誌中找到解決方案,在問題解決之後,要記得馬上將新的細節記錄到日誌中去。

 

要共享日誌給其他人,而不僅僅是靠一個人維護。把它放到共享的網絡驅動器中,這樣其他人也可以使用。或者創建一個 Wiki ,並鼓勵其他開發人員使用和更新其內容。

 

維護一個問題及其解決方案的日誌。

保留解決方案是修復問題過程的一部分,以後發生相同或類似問題時,就可以很快找到並使用了。

 

切身 感受

解決方案日誌應該作爲思考的一個來源,可以在其中發現某些特定問題的細節。對於某些類似但是有差異的問題,也能從中獲得修復的指引。

平衡的藝術

  • 記錄問題的時間不能超過在解決問題上花費的時間。要保持輕量級和簡單,不必達到對外發布式的質量。
  • 找到以前的解決方法非常關鍵。使用足夠的關鍵字,可以幫助你在需要的時候發現需要的條目。
  • 如果通過搜索 Web ,發現 沒人 曾經遇到同樣的問題,也許搜索的方式有問題。
  • 要記錄發生問題時應用程序、應用框架或平臺的特定版本。同樣的問題在不同的平臺或版本上可能表現得不同。
  • 要記錄團隊做出一個重要決策的原因。否則,在 6~9 個月之後,想再重新回顧決策過程的時候,這些細節就很難再記得了,很容易發生互相指責的情形。

 

34  警告就是錯誤

35  對問題各個擊破

 

對問題各個擊破

——  高效程序員的 45 個習慣之習慣35

“逐行檢查代碼庫中的代碼確實很令人恐懼。但是要調試一個明顯的錯誤,只有去查看整個系統的代碼,而且要全部過一遍。畢竟你不知道問題可能發生在什麼地方,這樣做是找到它的唯一方式。”
   

單元測試(在第 76 頁, 5 章)帶來的積極效應之一,是它會強迫形成代碼的分層。要保證代碼可測試,就必須把它從周邊代碼中解脫出來。如果代碼依賴其他模塊,就應該使用 mock 對象,來將它從其他模塊中分離開。這樣做不但讓代碼更加健壯,且在發生問題時,也更容易定位來源。

否則,發生問題時有可能無從下手。也許可以先使用調試器,逐行執行代碼,並試圖隔離問題。也許在進入到感興趣的部分之前,要運行多個表單或對話框,這會導致更難發現問題的根源。你會發現自己陷入整個系統之中,徒然增加了壓力,而且降低了工作效率。

大型系統非常複雜 —— 在執行過程中會有很多因素起作用。從整個系統的角度來解決問題,就很難區分開,哪些細節對要定位的特定問題產生影響,而哪些細節沒有。

答案很清晰:不要試圖馬上了解系統的所有細節。要想認真調試,就必須將有問題的組件或模塊與其他代碼庫分離開來。如果有單元測試,這個目的就已經達到了。否則,你就得開動腦筋了。

比如,在一個時間緊急的項目中(哪個項目的時間不緊急呢 Fred George 發現他們面對的是一個嚴重的數據損毀問題。要花很多精力才能知道哪裏出了問題,因爲開發團隊沒有將數據庫相關的代碼與其他的應用代碼分離開。他們無法將問題報告給軟件廠商,當然不能把整個代碼庫用電子郵件發給人家!

於是,他們倆開發了一個小型的原型系統,並展示了類似的症狀;然後將其發送給廠商作爲實例,並詢問他們的專家意見,使用原型幫助他們對問題理解得更清晰。

而且,如果他們 無法 在原型中再現問題的話,原型也可以告訴他們可以工作的代碼示例,這也有助於分離和發現問題。

識別複雜問題的第一步,是將它們分離出 來。既然不可能在半空中試圖修復飛機引擎,爲什麼還要試圖在整個應用中,診斷其中某個組成部分的複雜問題呢?當引擎被從飛機中取出來,而且放在工作臺上之 後,就更容易修復了。同理,如果可以隔離出發生問題的模塊,也更容易修復發生問題的代碼。

    分離原型                          Prototype to isolate

可是,很多應用的代碼在編寫時沒有注意到這一點,使得分離變得特別困難。應用的各個構成部分之間會彼此糾結:想把這個部分單獨拿出來,其他的會緊隨而至。 在這些狀況下,最好花一些時間把關注的代碼提取出來,而且創建一個可讓其工作的測試環境。

對問題各個擊破,這樣做有很多好處:通過將問題與應用其他部分隔離開,可以將關注點直接放在與問題相關的議題上;可以通過多種改變,來接近問題發生的核心 —你不可能針對正在運行的系統來這樣做。可以更快地發現問題的根源所在,因爲只與所需最小數量的相關代碼發生關係。

隔離問題不應該只在交付軟件之後才着手。在構建系統原型、調試和測試時,各個擊破的戰略都可以起到幫助作用。

對問題各個擊破
在解決問題時,要將問題域與其周邊隔離開,特別是在大型應用中。

切身感受

面對必須要隔離的問題時,感覺就像在一個茶杯中尋找一根針,而不是大海撈針。

平衡的藝術

  • 如果將代碼從其運行環境中分離後,問題消失不見了,這有助於隔離問題。
  • 另一方面,如果將代碼從其運行環境中分離後,問題 還在 ,這也有助於隔離問題。
  • 二分查找 的方式來定位問題是很有用的。也就是說,將問題空間分爲兩半,看看哪一半包含問題。再將包含問題的一半進行二分,並不斷重複這個過程。
  • 在向問題發起攻擊之前,先查找你的解決問題日誌

 

36  報告所有的異常

37   提供有用的錯誤信息

 

提供有用的錯誤信息

——  高效程序員的 45 個習慣之習慣37

“不要嚇着用戶,嚇程序員也不行。要提供給他們乾淨整潔的錯誤信息。要使用類似‘用戶錯誤。替換,然後繼續。’這樣讓人舒服的詞句。”

      當應用發佈並且在真實世界中得到使用之後,仍然會發生這樣那樣的問題。比如計算模塊可能出錯,與數據庫服務器之間的連接也可能丟失。當無法滿足用戶需求時,要以優雅的方式進行處理。

 類似的錯誤發生時,是不是隻要彈出一 條優雅且帶有歉意的信息給用戶就足夠了?並不盡然。當然了,顯示通用的信息,告訴用戶發生了問題,要好過由於系統崩潰造成應用執行錯誤的動作,或者直接關 閉(用戶會因此感到困惑,並希望知道問題所在)。然而,類似“出錯了”這樣的消息,無法幫助團隊針對問題做出診斷。用戶在給支持團隊打電話報告問題時,我 們希望他們提供足夠多且好的信息,以幫助儘快識別問題所在。遺憾的是,用很通用的錯誤消息,是無法提供足夠的數據的。

針對這個問題,常用的解決方案是記錄日誌:當發生問題時,讓應用詳細記錄錯誤的相關數據。錯誤日誌最起碼應該以文本文件的形式維護。不過也許可以發佈到一個系統級別的事件日誌中。可以使用工具來瀏覽日誌,產生所有日誌信息的 RSS feed ,以及諸如此類的輔助方式。

 記錄日誌很有用,可是單單這樣做是不夠的:開發人員認真分析日誌,可以得到需要的數據;但對於不幸的用戶來說,起不到任何幫助作用。如果展示給他們類似下圖 中的信息,他們還是一點頭緒都沒有 —— 不知道自己到底做錯了什麼,應該怎麼做可以繞過這個錯誤,或者在給技術支持打電話時,應該報告什麼。   

如果你注意的話,在開發階段就能發現這個問題的早期警告。作爲開發人員,經常要將自己假定爲用戶來測試新功能。要是錯誤信息很難理解,或者無助於定位錯誤的話,就可以想想真正的用戶和支持團隊,遇到這個問題時會有多麼困難了(見圖 7-2 )。


7-2  無用的異常信息

 

例如,假定登錄 UI 調用了應用的中間層,後臺向數據訪問層發送了一個請求。由於無法連接數據庫,數據訪問層拋出一個異常。這個異常被中間層用自己的異常包裹起來,並繼續向上傳遞。那麼 UI 層應該怎麼做呢?它至少應該讓用戶知道發生了系統錯誤,而不是由用戶的輸入引起的。

 接下來,用戶會打電話並且告訴我們他無法登錄。我們怎麼知道問題的實質是什麼呢?日誌文件可能有上百個條目,要找到相關的細節非常困難。

 實際上,不妨在顯示給用戶的信息中提供更多細節。好比說,可以看到是哪條 SQL 查詢或存儲過程發生了錯誤;這樣可以很快找到問題並且修正,而不是浪費大把的時間去盲目地碰運氣。不過另一方面,在生產系統中,向用戶顯示數據連接問題的特定信息,不會對他們有多大幫助。而且有可能嚇他們一跳。

 一方面要提供給用戶清晰、易於理解的問題描述和解釋,使他們有可能尋求變通之法。另一方面,還要提供具備關於錯誤的詳細技術細節給用戶,這樣方便開發人員尋找代碼中真正的問題所在。

 下面是一種同時實現上述兩個目的方式:圖中顯示了清晰的錯誤說明信息。該錯誤信息不只是簡單的文本,還包括了一個超鏈接。用戶、開發人員、測試人員都可以由此鏈接得到更多信息,如圖7- 3 、圖 7-4 所示。

 

 

圖7-3 帶有更多細節鏈接的異常信息

 

7-4  供調試用的完整詳細信息

 

進入鏈接的頁面,可以看到異常(以及所 有嵌套異常)的詳細信息。在開發時,我們可能希望只要看到這些細節就好了。不過,當應用進入生產系統後,就不能把這些底層細節直接暴露給用戶了,而要提供 鏈接,或是某些訪問錯誤日誌的入口。支持團隊可以請用戶點擊錯誤信息,並讀出錯誤日誌入口的相關信息,這樣支持團隊可以很快找到錯誤日誌中的特定細節。對 於獨立系統來說,點擊鏈接,有可能會將錯誤信息通過電子郵件發送到支持部門。

除了包括出現問題的詳細數據外,日誌中記錄的信息可能還有當時系統狀態的一個快照(例如 Web 應用的會話狀態)。  

使用上述信息,系統支持團隊可以重建發生問題的系統狀態,這樣對查找和修復問題非常有效。   

錯誤報告對於開發人員的生產率,以及最終的支持活動消耗成本,都有很大的影響。在開發過程中,如果定位和修復問題讓人倍受挫折,就考慮使用更加積極主動的錯誤報告方式吧。調試信息非常寶貴,而且不易獲得。不要輕易將其丟棄。   

展示有用的錯誤信息
提供更易於查找錯誤細節的方式。發生問題時,要展示出儘量多的支持細節,不過別讓用戶陷入其中。
區分錯誤類型
程序缺陷。 這些是真正的 bug ,比如 NullPointerException 、缺少主鍵等。用戶或者系統管理員對此束手無策。

環境問題。 該類別包括數據庫連接失敗,或是無法連接遠程 Web Services 、磁盤空間滿、權限不足,以及類似的問題。程序員對此沒有應對之策,但是用戶也許可以找到變通的方法,如果提供足夠詳細的信息,系統管理員應該可以解決這些問題。

用戶錯誤。 程序員與系統管理員不必擔心這些問題。在告知是哪裏操作的問題後,用戶可以重新來過。

通過追蹤記錄報告的錯誤類型,可以爲受衆提供更加合適的建議。

切身感受

錯誤信息有助於問題的解決。當問題發生時,可以詳細研究問題的細節描述和發生上下文。

平衡的藝術

  • 像“無法找到文件”這樣的錯誤信息,就其本身而言無助於問題的解決。“無法打開 /andy/project/main.yaml 以供讀取”這樣的信息更有效。
  • 沒有必要等待拋出異常來發現問題。在代碼關鍵點使用斷言以保證一切正常。當斷言失敗時,要提供與異常報告同樣詳細的信息。
  • 在提供更多信息的同時,不要泄露安全信息、個人隱私、商業機密,或其他敏感信息(對於基於 Web 的應用,這一點尤其重要)。
  • 提供給用戶的信息可以包含一個主鍵,以便於在日誌文件或是審覈記錄中定位相關內容。

[ ]    有些安全敏感的信息不應該被暴露,甚至不可以記錄到日誌中去,這其中包括密碼、銀行賬戶等。

 

38  安排有規律的會面時間

39  架構師必須寫代碼

 

架構師必須寫代碼

——   高效程序員的 45 個習慣之習慣39

“我們的專家級架構師Fred會提供設計好的架構,供你編寫代碼。他經驗豐富,拿的薪水很高,所以不要用一些愚蠢的問題或者實現上的難點,來浪費他的時間。” 

軟件開發業界中有許多掛着架構師稱號的人。作爲作者的我們,不喜歡這個稱號,原因如下: 架構師 應該負責設計和指導,但是許多名片上印着“架構師”的人配不上這個稱號。作爲架構師,不應該只是畫一些看起來很漂亮的設計圖,說一些像“黑話”一樣的詞彙,使用一大堆設計模式 —— 這樣的設計通常不會有效的。

不可能在PowerPoint 幻燈片中進行編程                You can’t code in PowerPoint

這些架構師通常在項目開始時介入,繪製各種各樣的設計圖,然後在重要的代碼實現開始之前離開。有太多這種“ PowerPoint 架構師”了,由於得不到反饋,他們的架構設計工作也不會有很好的收效。

 一個設計要解決眼前面臨的特定問題,隨着設計的實現,對問題的理解也會發生改變。想在開始實現之前,就做出一個很有效的詳細設計是非常困難的(見第 48 頁上的實踐 11 )。因爲沒有足夠的上下文,能得到的反饋也很少,甚至沒有。設計會隨着時間而演進,如果忽略了應用的現狀(它的具體實現),要想設計一個新的功能,或者完成某個功能的提升是不可能的。

作爲設計人員,如果不能理解系統的具體細節,就不可能做出有效的設計。只通過一些高度概括的、粗略的設計圖是沒有辦法達成對系統的理解的。

這就像是嘗試僅僅通過查看地圖來指揮一場戰役 —— 一旦開打,僅有計劃是不夠的。戰略上的決策也許可以在後方進行,但是戰術決策 —— 影響成敗的決策 —— 需要對戰場狀況的明確瞭解。

可 逆 性

“程序員修煉之道”叢書中指出 不存在所謂的最終決策 。沒有哪個決策做出之後,就是板上釘釘了。實際上,就時間性來看,不妨把每個重要的決策,都看作沙上堆砌的城堡,它們都是在變化之前所做出的預先規劃。

新系統的設計者  Donald E. Knuth
新系統的設計者必須要親自投入到實現中去。

 正像 Knuth 說的,好的設計者必須能夠捲起袖子,加入開發隊伍,毫不猶豫地參與實際編程。真正的架構師,如果不被允許參與編碼的話,他們會提出強烈的抗議。

有一句泰米爾諺語說:“只有一張蔬菜圖無法做出好的咖喱菜。”與之類似,紙上的設計也無法產生優秀的應用。設計應該被原型化,經過測試,當然還有驗證 —— 它是要進化的。實現可用的設計,這是設計者或者說架構師的責任。

Martin Fowler 在題爲“ Who needs an Architect ?” 的文章中提到:一個真正的架構師“……應該指導開發團隊,提升他們的水平,以解決更爲複雜的問題”。他接着說:“我認爲架構師最重要的任務是:通過找到移除軟件設計不可逆性的方式,從而去除所謂架構的概念。”增強 可逆性 是注重實效的軟件實現方式的關鍵構成部分。

要鼓勵程序員參與設計。主力程序員應該試着擔任架構師的角色,而且可以從事多種不同的角色。他會負責解決設計上的問題,同時也不會放棄編碼的工作。如果開發人員不願意承擔設計的責任,要給他們配備一個有良好設計能力的人。程序員在拒絕設計的同時,也就放棄了思考。

優秀的設計從積極的程序員那裏開始演化
積極的編程可以帶來深入的理解。不要使用不願意編程的架構師——不知道系統的真實情況,是無法展開設計的。

切身感受

架構、設計、編碼和測試,這些工作給人的感覺就像是同一個活動 —— 開發 —— 的不同方面。感覺它們彼此之間應該是不可分割的。

平衡的藝術

  • 如果有一個首席架構師,他可能沒有足夠的時間來參與編碼工作。還是要讓他參與,但是別讓他開發在項目關鍵路徑上的、工作量最大的代碼。
  • 不要允許任何人單獨進行設計,特別是你自己。

 

40  實行代碼集體所有制

41  成爲指導者

42  允許大家自己想辦法

 

允許大家自己想辦法

—— 高效程序員的 45 個習慣之習慣42

“你這麼聰明,直接把乾淨利落的解決方案告訴團隊其他人就好了。不用浪費時間告訴他們爲什麼這樣做。”

“授人以魚,三餐之需;授人以漁,終生之用。”告訴團隊成員解決問題的方法,也要讓他們知道如何解決問題的思路,這也是成爲指導者的一部分。

     瞭解上個實踐 —— 成爲指導者 —— 之後,也許有人會傾向於直接給同事一個答案,以繼續完成工作任務。要是隻提供一些指引給他們,讓他們自己想辦法找到答案,又會如何?

 這並不是多麼麻煩的事情;不要直接給出像“ 42 ”這樣的答案,應該問你的隊友:“你有沒有查看在事務管理者與應用的鎖處理程序之間的交互關係?”

     這樣做有下面幾點好處。

  • 你在幫助他們學會如何解決問題。
  • 除了答案之外,他們可以學到更多東西。
  • 他們不會再就類似的問題反覆問你。
  • 這樣做,可以幫助他們在你不能回答問題時自己想辦法。
  • 他們可能想出你沒有考慮到的解決方法或者主意。這是最有趣的 —— 你也可以學到新東西。

如果有人還是沒有任何線索,那就給更多提示吧(或者甚至是答案)。如果有人提出來某些想法,不妨幫他們分析每種想法的優劣之處。如果有人給出的答案或解決方法更好,那就從中汲取經驗,然後分享你的體會吧。這對雙方來說都是極佳的學習經驗。

作爲指導者,應該鼓勵、引領大家思考如何解決問題。前面提到過亞里士多德的話:“接納別人的想法,而不是盲目接受,這是受過教育的頭腦的標誌。”應該接納別人的想法和看問題的角度,在這個過程中,自己的頭腦也得到了拓展。

如果整個團隊都能夠採納這樣的態度,可以發現團隊的知識資本有快速的提升,而且將會完成一些極其出色的工作成果。

給別人解決問題的機會
指給他們正確的方向,而不是直接提供解決方案。每個人都能從中學到不少東西。

切身感受

感覺不是在以填鴨式的方式給予別人幫助。不是有意掩飾,更非諱莫如深,而是帶領大家找到自己的解決方案。

平衡的藝術

  • 用問題來回答問題,可以引導提問的人走上正確的道路。
  • 如果有人真的陷入膠着狀態,就不要折磨他們了。告訴他們答案,再解釋爲什麼是這樣。

 

43  準備好後再共享代碼

44  做代碼複查

45  及時通報進展與問題

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章