修改軟體的藝術閱讀筆記及思考

江湖大俠發表於2018-12-18

利用一週零散的時間,閱讀了《修改軟體的藝術》點選這裡去購買這本書,在閱讀的過程中,結合以前的專案開發過程,深有體會和感觸。軟體開發不是一蹴而就的,必須遵循一些準則,在編寫的過程中,也要學會思考。

全書首先是對當前的開發模式進行了介紹,並闡述了其中的弊端和缺陷。傳統的瀑布式開發,讓軟體在後期進行修改現有功能和新增新功能的時候要付出的更多,導致更多的軟體是開發失敗的。一個軟體成功交付也不能表示這個軟體是成功的,除非交付之後沒有人使用,不需要修改。在瀑布式開發中,開發者可能在幾個月後才能看到它執行的結果,開發過程建立的一個測試模擬環境,已經脫離了軟體上下文。從而引出,我們就需要尋找一種新的思路和開發流程——敏捷開發。

可能是第一次接觸敏捷開發的思想,所以在閱讀中,一直想找到對這個詞的定義,想要能明白,敏捷開發的模式是如何的。但是書中並沒有明確定義,所以只能通過整體的介紹和書中提出的9條構建易維護程式碼的最佳實踐中總結了。

在如何做之前先問做什麼、為什麼做、給誰做

有時候開發者會把事情搞錯,不是程式碼出錯,而是 程式碼應該做的事情 沒搞清楚,這時就需要產品負責人回答所有的問題,並且把控正確的方向,即使很多時候都沒有絕對正確的答案。當產品負責人無法相應的時候,開發過程就會變得緩慢,開發者必須對他們所做的事情作出結果未知的揣測。

這裡說明了一名產品負責人的重要性,產品負責人是產品的權威人士,是溝通的中樞。 而開發者是要善於提問的,必須去思考,事無鉅細的提出問題。但是也不需要拿到全面需求、知道做什麼之前就開始編寫軟體,一邊開發一邊溝通,這樣也是很高效的,而且會明顯提高產品質量。

需求文件並非被使用者故事取代,而是被產品負責人和開發者之間的互動取代,被產品負責人和客戶之間的互動取代。使用者故事不能取代需求文件。(使用者故事)我們不需要那麼多細節,使用者故事保證我們不僅僅在計劃階段,而且在整個開發階段都保持對需求的關注。

給定特定輸入,應該得到特定的輸出。無論你是否使用自動化測試,把驗收標準和邊界情況寫在使用者故事卡片上都是好的辦法,可以用來提醒你需要處理哪些異常。

本章閱讀筆記:

從接觸軟體開發到現在,完整的帶了兩個專案,但是我都不敢說這兩個專案任何一個是成功的,雖然一個已經在天津市推廣執行,另外一個每天幾乎為1000名教職工提供服務。回顧整個開發過程,越發能體現出瀑布式開發的缺陷。在此之前,覺得專案時失敗的,追究其原因,大概是因為當時開發技術的低階和軟體思維正在形成中,不能很好的從全域性去思考專案,但是一直認為這種開發模式時正確的:先是前期的需求分析,然後就是設計,再然後就是實現、整合,跳過了測試,最後就是部署和上線。在上線之後,才發現,各種Bug,然後就苦逼的修改,正在吃飯的時候,一個電話過來,就要放下剛吃兩口的飯,跑到實驗室,開啟電腦“救火”;在新增新功能的時候,我都不願意去翻以前的程式碼了,總是那麼不堪入目。這兩個專案,顯然是失敗的,現在不敢修改,只能是像一個破舊的衣裳,縫縫補補。

雖然專案的開發是不理想的,但是,我還是從中學到了很多。閱讀本章的時候,特別是對產品負責人的職責描述時,深有體會,在兩個專案中,我都可以算是專案的負責人,負責把控整個專案的進度和方向,明顯會感覺到產品負責人的重要型。在我自己迷茫的時候,整個專案時停滯不前的,專案組成員的開發效率也是低下的;當我明確方向時,整個專案組都明確方向,困難一個一個被攻破,推進迅速。當然,這個其中,少不了專案組成員(開發者)的工作和思考。

在反思原有開發模式基礎上,通過本章學習到了:

  • 整個專案組需要明確做什麼、為什麼做、給誰做的思想
  • 嘗試用 使用者故事 替代文件需求。縮短需求分析的時間,多建立使用者故事模型。
  • 在開始做之前,要定製驗收標準和邊界情況,最好能做到自動化測試。

注:當前沒有發展成產品負責人的想法,只是在自己的專案中,可以通過實踐,驗證學得東西。

小批次構建

小批次構建讓每個任務都可以有在段時間(理想情況是四個小時)內完成,保證任務都滿足驗收標準,或者至少產生可觀察的結果。這有助於讓任務更容易預估、完成和驗收。

我們應該用什麼來衡量自己?

我們應該用對客戶的價值來衡量自己。

如果一個使用者故事是複雜型的,那麼通常只有一個原因:其中有很多未知因素,我們處理複雜型使用者故事的方式是“分離已知和未知”。我們不斷地在未知的範圍內進行迭代,未知的範圍越來越小,直至消失。把未知和已知分離時分割包含未知因素的故事的第一步。一旦某些事無被標記為未知,就將其封裝。

探索未知事物的時候需要做的兩件事:

  • 把未知變為已知。
  • 把未知進行封裝。如果能把大的未知問題隱藏起來,那就隱藏起來,以後再處理(這裡有封裝的意思)。

更短的反饋迴路: 反饋的迴路可能有多個。與客戶交流、迭代或者功能開發後期進行演示、編譯器反饋、自動測試、回顧會議、程式碼審查等。

優秀的軟體開發實踐需要在構建不同組成部分的同時保證軟體的完整性,而且讓各個部分儘可能獨立。試著通過定義良好的介面來移除組建間的依賴。如果必須有某些依賴,讓後來的故事依賴於之前的故事。一個故事應該時完成一個單一目的,或者一個目的可檢驗的某個方向(單一職責)。

本章學習到了:

  • 封裝未知,將大問題分割成小問題。(雖然一直也是這樣做的)
  • 故事的單一職責。
  • 小批次構建是有必要的。

持續整合

持續整合時一種在構建時期而非釋出前進行整合的實踐,降低軟體開發中的風險,同時做為一種反饋機制,對開發者有這重要的意義。

定義完成:

  • 完成。寫完一個功能,可以在開發環境中執行,在他的機器上可以得到一定結果,
  • 完整完成。在開發機器中正常執行,且經過整合。
  • 完美完成。在開發機器上正常執行,經過整合,清晰且健壯。

構建軟體時間,將新的功能簽入版本庫中,所有開發者都在同一分支上工作,且版本庫中除原始碼之外,還有管理構建的其他檔案,包括配置檔案、資料庫模型、測試程式碼和測試指令碼、第三方庫、安裝指令碼、文件、設計圖例、用例、UML圖例等。在測試系統中,完整複製生產環境。

編寫出優秀的單元測試,並且僅僅測試那些需要測試的程式碼,而不測試那些可能用到的程式碼。

應該保持隨時整合。持續的進行整合,儘快從頭到尾完成一個使用者故事。

構建敏捷設施的7個策略:

  • 用版本庫管理一切
  • 一次點選全部構建
  • 持續整合
  • 為任務定義驗收標準
  • 編寫可測試的程式碼
  • 保證必要的測試覆蓋率
  • 即時修復失敗的構建

協作

我們擁有最寶貴的資源就是彼此

結對程式設計並非輪流使用計算機,而是讓兩個頭腦解決同一個問題,比各自單獨工作更迅速而且質量更好。結對程式設計可以防止團隊成員過於專門化,並且幫助團隊達成共識。

結對程式設計的配對方式: - 根據開發者的強項和缺點來配對 - 讓最有經驗的開發者和最少的結對 - 隨機配對

一個想法在由你的腦海輸入到計算機之前,必須要經過其他人的手

夥伴程式設計:

在每一天最後的一個小時裡,你和你的夥伴對一天的開發進行審查。是結對程式設計的前身,安全嘗試工作。

程式碼審查:

每當一個開發者完成了某一功能,他就可以向其他成員講解他是如何編寫的。 設計和程式碼的審查應該首先指出設計思路並說明為何選擇這種設計。

回顧會議:

團隊聚在一起,每個人都有機會發言,意思都差不多,我們做了什麼,是如何做的,為什麼做,下一次怎麼做的更好等。

重要的一點:

並不是所有人都適合結對,也不是所有的任務都適合結對。

除了結對程式設計之外,還有穿刺、群戰、圍攻、夥伴程式設計。通過加強學習、分享知識,可以改進我們的團隊乃至整個產業。

本章學習到了:

對結對程式設計還是十分看好的,覺得這是能在很大程度上減少bug的方式。我有且只有一次這樣的開發過程,那次連續一下午的程式碼編寫中有種一氣和成的爽快感覺。在開發“班車預約系統”的時候,那是一個核心模組,十分重要,當思路理清之後,老師就坐在我旁邊,我邊寫著邊講這為什麼那麼做。雖然程式碼的整潔度不夠,但是實現了基本的功能。整個編寫的過程中,竟然沒有出現幾個bug,每當bug快要出現的時候,老師總是追問,為什麼這樣做,不這樣做。這樣一思考,就避免了bug的誕生。當時沒有覺得這種開發方式有什麼獨特,當閱讀完本章之後,覺得十分可以在團隊中實施。

編寫整潔的程式碼

物件應該具有定義良好的屬性、專一的職責、隱藏的實現,它應該控制自己的狀態,而且只應該被定義一次。

  • 高質量的程式碼應該內聚:

如果內聚的類只代表一種事物,那麼這個事物應該是可以被命名,但是不是它們的名字而是它們的行為決定了它們代表什麼。

  • 高質量的程式碼是鬆散耦合的
  • 高質量的程式碼是封裝良好的:

它隱藏了實現細節,由外而內程式設計,根據所做的事情命名服務隱藏它是物和封裝的。在軟體中,你不知道的事情對你無害。用什麼來隱藏怎麼。

  • 高質量的程式碼時自主的:

    它管理自己的職責。如果發現某個方法過分依賴另外一個類的資料,那就應該將這個方法置於這個類中。

  • 高質量的程式碼時無冗餘的:

    程式碼中的冗餘是試圖在不同的地方做相同的事情,無論做的是什麼,冗餘不僅僅是形式的重複,而且還是對意圖的重複。

不要讓完美成為優秀的敵人,在軟體中無法實現完美的。

程式碼的集體所有權以為著團隊中,每個人都可以維護任意部分的程式碼,即使並不是他們編寫的。團隊應該確定共同的程式碼規範、統一領域模型和開發實踐、使用同樣的詞彙來描述設計。

重構是編寫程式碼的關鍵步驟,而且貫穿整個開發過程。在編寫程式碼和新功能完成時進行重構。

程式碼審查:學習他人風格,不斷學習軟體開發。閱讀他人的程式碼,編寫程式碼,不斷練習。

測試先行

單元測試並不是測試整個使用者故事,而是測試相對來說更小的單元。單元測試也可以當作內部文件來使用。這裡的單元是指一個行為單元:一個對立的、可驗證的行為。它必須對系統產生可觀察的影響。而不和系統的其他行為耦合。一個單元代表一個行為,如果行為不變,測試也不應該變化。

測試先行開發

開發者先針對一個功能編寫測試,然後實現那個功能讓測試通過。你只需要編寫測試覆蓋的程式碼,這樣測試覆蓋率永遠時百分之百。測試也是程式碼,是系統中的一部分。

在敏捷軟體開發中,並非在一開始就得到全部的需求,而是一邊設計一邊整理需求,雖然這樣很容易作出一些錯誤的選擇,但是迭代構建也比事前確定所有需求要高效的很多。

如果使用得當,測試先行開發可以幫助開發者編寫容易測試和容易維護的程式碼,但是如果使用不恰當,TDD會成為累贅而非資源。

通過本章學習:

  • 測試先行的重要行
  • 就目前所說的這些理論,必須在開發中去嘗試,在實踐中體會真理。

用測試描述行為

最開始編寫測試的時候還沒有可以測試的程式碼。接下來開始編寫能讓測試通過所需的最簡單的實現程式碼。下一步清理程式碼提升質量。週期迴圈。

所有失敗的測試都是因為某些已知原因而失敗的,而不會因為其他原因失敗,而那個測試是系統中唯一可以由此原因導致失敗的測試。換句話說 測試應該是獨一無二的。

如果程式碼有多重執行路徑,意味著程式碼的邏輯也會更復雜,所以應該用測試來覆蓋程式碼。每個程式碼執行路徑得到不同的結果,所以需要針對每一個路徑進行單元測試。

但是單元測試不能測試一系列正確順序的呼叫或其他的類似場景,這裡引入工作流測試,工作流所測試用所謂的模擬物件進行測試。模擬物件是真實物件的替代。

在查詢Bug的時候,我會嘗試如構建那些可以得到bug在哪裡(或者不會在哪裡)的資訊場景,然後逐步縮小程式碼範圍,直到找到問題。(這是積累出來的經驗)

最後實現設計

持續性開發:

  • 刪除死程式碼

死程式碼是指那些被註釋掉或者不再被呼叫而永遠得不到執行的程式碼,除了干擾其他開發者之外,它完全沒有意義。刪了它

  • 保持名稱更新
  • 集中決策
  • 抽象

將編碼和清理分開,當作不同的任務。只關注實現行為,讓測試通過,這樣編碼更容易。將物件的建立和物件的使用分離。

用“見名知意”的命名取代註釋描述程式碼,應該用註釋來描述為什麼要做這些事,而不是做什麼。程式碼本身應該說明自己在做什麼。

重構遺留程式碼

重構是指在不改變外部行為的前提下對程式碼的內部結構進行重組或重新包裝。

修改程式碼的風險和成本都很高,所以要謹慎行事。對已有程式碼新增測試。

  • 以支援修改為目的重構
  • 以開閉原則為目的重構
  • 以提高可修改性為目的重構

不用試圖在一開始就找到最佳設計,我們可以隨著重構來不斷改進設計。

何時進行重構:

  • 當關鍵程式碼維護不善的時候
  • 當唯一理解程式碼的人沒空的時候
  • 當有資訊可以揭示更好的設計的時候
  • 當修改bug的時候
  • 當需要新增新功能的時候
  • 當需要為遺留程式碼寫文件的時候
  • 當從夠比重寫容易的時候

從遺留程式碼中學習

相關文章