過了幾年又重新看過這本書才慢慢能感受到裡面說的
Uncle Bob 的書真的每個階段看都會有很不一樣的感覺
很值得買回來收 xD
幾年前第一次看這本書的時候
寫程式大多只是交作業,頂多就跟其他的同學一起交大一點的作業或開發一些小專案
能不要被雷(或不要雷別人)就很不錯了
當時記錄下的摘要也就沒什麼內容([Book] Clean Coder)
到了現在。加減算是做過稍微大一點的專案,也帶過一些小專案
重新看過這本書,就慢慢理解了一點 Uncle Bob 想說的
關於本書
- 什麼是軟體專業人士?
- 軟體專業人士如何行事?
- 軟體專業人士如何處理衝突,「應對」緊湊的開發進度表?如何和不切實際的管理人員打交道?
- 軟體專業人士何時應該說「不」?怎麼說?
- 軟體專業人士如何「應對」壓力?
第一章:專業主義
擔當責任
-
專業人士如果犯了錯,只好自己收拾殘局
- 專業主義就代表著「擔當責任」
-
你說的沒錯。軟體發展太複雜了,不可能沒什麼Bug。但很不幸,這個理由並不能為你開脫。人體太複雜了,不可能完全理解,但醫生仍誓言不傷害病人。如果他們不拿人體的複雜性做託辭,我們又怎麼能拿上述理由,開脫自已的責任呢?
- 程式難免出現 bug ,但這不意味著你不用對他們負責;沒人能寫出完美的軟體,但這不代表你不用對不完美負責。
- 每次 QA 找出問題時,或者更糟糕的是「用戶找出問題」時,你都該震驚羞愧,並決心以此為戒。
自動化測試
- 寫一些隨時都能執行的「單元測試」,然後盡可能地多執行
- 要測試多少程式碼呢? → 當然是全部!全部都要測!
- 但有些程式碼不是很難測嗎?
- 沒錯。但之所以難測,是因為設計時沒考慮如何測試
- 唯一的解決辦法就是要設計「易於測試的程式碼」
結構
- 想證明軟體易於修改,唯一的辦法就是「做些實際的修改」
- 如果發現這些修改不如想像中簡單,就應該改進設計,讓後續的改變更簡單
職業道德
- 你應該計畫每週的60小時中
- 40小時給雇主
- 20小時給自己 (大約每天 3 小時)
- 不為雇主工作,為自己的職業發展工作
了解你的領域
- 每個專業軟體發展人員至少必須精通的事項
- Design patterns
- GOF
- POSA
- Design principles
- SOLID
- Methods
- XP
- Scrum
- Lean
- Kanban
- Waterflow
- 結構化分析
- 結構化設計
- and etc.
- Disciplines
- TDD
- 物件導向設計
- 結構化程式設計
- CI
- Pair Programming
- Artifacts
- UML
- DFD (Data Flow Diagram)
- 結構圖
- Petri net
- 狀態遷移圖表
- 流程圖
- 決策表
- Design patterns
學習
- 自我學習
- 讀書, 關注 blog, tweets, 參加 conference, 訪問用戶群, 參與讀書會 and etc.
- 協作
- 輔導
- 專業人士會「視輔導新人為己任」,他們不會放任未經輔導的新手胡打亂撞
了解業務領域
- 如果撰寫財務系統,你就該對財務領域有所了解
- 你未必要成為該領域的專家,但你仍需要勤勉,付出相當的努力來認識業務領域。
- 最不專業的做法是,只簡單地按照規格說明來撰寫程式碼,但卻對於那些業務為什麼需要做那樣的規格定義不求甚解。相反的,你應該對該領域有所瞭解,能辨別、執行規格說明書的錯誤
第二章:説「不」
Do or do not ! There is no try!
—— Yoda
- 專業人士敢於說明真相而不屈從於權勢。專業人士有勇氣對他們的經理說「不」。
- 難道不該照你老闆說的去做嗎?
- 不該。只要你是一名專業人士,那就不該
- 奴隸沒有權利說「不」。勞工或許也對說「不」有所顧慮。但是專業人士應該懂得說「不」
- 難道不該照你老闆說的去做嗎?
- 你的經理要求你在明天之前完成登入頁面,這就是他在追求和捍衛的一個目標,那是他的工作職責。如果你明知第二天之前不可能完成登入頁面,嘴上卻說「好的,我會試試看」,那麼便是你失職了。這時候,唯一盡責的方式便是說「不,這不可能」
- 可能得最好結果是你和你的經理共同追求的目標。最關鍵的是要找到那個「共同目標」,而這往往有賴於「協商」。
- 「為什麼」重要嗎?
- 如果你的主管恰好有「技術背景」和「好脾氣」去傾聽理解,這些解釋也許有用
- 另一種狀況是,主管會不認同這樣的結論或做法不對,可能會說出不用做完整的測試和程式碼審核
- 有時候,提供太多細節,只會變成一個口令一個動作的管理方式。
第三章:説「是」
- 作出承諾包含三步驟
- 口頭上說自己將會去做
- 心裡認真對待自己所做出的能諾
- 真的付諸行動去做
識別「缺乏承諾」的徵兆
- need / should
- 我們需要把這工作做完
- 我需要減肥
- 有人應當負責去推動這件事
- hope / wish
- 希望明天我能完成這個任務
- 希望改天我們能見宴嗎
- 但願我有時間做這件事
- 但願電腦更快點
- Let's (而不是「讓我」)
- 讓我們回頭見
- 讓我們把這工作做完
真正的承諾聽起來是怎樣的
- 我將在...之前...
- e.g., 我將在星期二之前完成這個任務
- 你對自己會做某件事做了清晰的事實陳述,而且還明確說明了完成期限
- 之所以沒成功,是因為我寄望於某某人去做這件事
- 你只能承諾自己能完全掌控的事
- 如果最終目標依賴於他人,那麼你就應該採取些具體行動以接近最終目標
- 之所以沒成功,是因為有些時候我真的無能為力
- 如果你無法兌現承諾,那麼最重要的就是「儘早向你承諾對象發聲預警,越快越好,越早越好」。
- 如果你不儘早告訴他人可能的問題,就錯失了讓他們幫你兌現能諾的機會。
第四章:寫程式
做好準備
寫程式是件累人的事,你必須做到
- 讓程式碼能正常工作。理解當前要解決的問題和如何解決,並且確保程式碼忠實的依循解決方案
- 幫客戶解決問題
- 很多時候,客戶提出的需求其實並不能真正解決他們自己的問題。這有賴於你去發現這些問題並與客戶交流,以確保能滿足客戶的真實需求。
- 程式碼必須和現有系統整合,並妥善管理好各種相依關係,不能讓系統僵化
- 寫程式時必須遵循穩工程原則
-
其他的程式設計師必須能讀懂你的程式碼
- 這包括寫好註解、精心淬煉程式碼,這可能是程式設計師最難精通的一項。
-
感到疲勞或心煩意亂,千萬別寫程式
- 奉獻精神和職業素養更主要的意義在於「遵守紀律原則」而非成為「長時間的工作狂」
- 要確保自己已經幾睡眠、健康和生活方式調整到最佳狀態,這樣才能做到每天的8小時工作時間內全力以赴
創意輸入
- 「創意輸出」依賴於「創意出入」 → 廣泛閱讀
保持節奏、知道何時應該離開一會
- 軟體開發是一場馬拉松,不是短跑衝刺
- 沒解決這個問題不能回家
- 噢不,你可以回家,而且是應該回家!
- 創意和智力來自於大腦的高速運轉,當你感到疲勞時,它們就不翼而飛了
進度延遲
- 即使是最優秀的程式設計師、最敬業的員工,也不能避免碰到延遲
- 管理延遲的要訣就是早期檢測和保持透明
- 根據目標定期衡量進度,使用三個考慮到各種因素的期限,不要把預估和期望混淆在一起
- 樂觀預估, 常態預估, 悲觀預估
- 不要經受不住誘惑盲目衝刺
- 你無法更快的寫完程式碼。試圖這麼做,最終只會讓自己變得更慢,同時也只能製造出一堆混亂,讓其他人也慢下來
交付失敗
- 程式設計師所能表現的不專業中,最糟糕的就是,明知道還沒有完成任務卻宣稱已經完成
- 明確定義「完成」
- 最好的方法是讓業務分析師和測試人員建立一套自動化的驗收測試,只有完全通過這些驗收測試,開發任務才能算已經完成
幫助
- 作為專業人士,你要以能隨時幫助別人為榮
- 你的工作不可能重要到你不能花一丁點時間來幫助別人
- 這並不是說你不需要獨處的時間 → 直接、禮貌的讓人知道某個時間區段不希望受到干擾,其餘的時段敞開大門樂於幫助他人
- 要學會如何請求幫助
- 輔導
- 花時間親自輔導手底下的年輕程式設計師,是資深程式設計師的「專業職責所在」
- 同樣的道理,向資深導師尋求輔導,也是年輕程式設計師的「專業職責」
第五章:測試驅動開發
TDD 的三大法則
- 在撰寫一個單元測試(測試失敗的單元測試)前,不可撰寫任何產品程式
- 只撰寫剛好無法通過的單元測試,不能編譯也算無法通過
- 只撰寫剛好能通過當前測試失敗的產品程式
TDD 的優勢
- 確定性
- 缺陷注入率
- 勇氣
- 擁有一套值得信賴的測試,便可完全打消對修改程式碼的全部恐懼。當看見糟糕的程式碼時,就可以放手整理
- 文件
- 單元測試就是文件。他們描述了系統的最底層設計細節
- 設計
- 基於測試先行的需要,會迫使你去思考什麼才是好的設計
- 與採用測試先行方式編寫的測試程式碼比起來,後寫的測試在深度和捕捉錯誤的靈敏度方面要遜色很多
第六章:練習
自身經驗的拓展
- 老闆通常只會限定一種語言、一種平台,以及程式設計師工作目標的專業領域。這樣會導致經驗不夠豐富的程式設計師,其領域和思維都被侷限
- 程式設計師發現,面對這個行業的週期性變化造成的新局面,自己並沒有做好準備
- 保持不落伍的一種方法是為 open source project 貢獻程式碼
- 嘗試對自己不習慣的語言、平台、領域做出貢獻
第七章:驗收測試
- 定義驗收測試
- 「業務方與開發方合作編寫的測試」,其目的在於確認需求已經完成了
- 「完成」意味著
- 所有程式碼都寫完了
- 所有的測試都通過了
- QA 和需求方已經認可
測試的協商與被動推進
身為專業開發人員,「與撰寫測試的人協商並改進測試」是你的職責。絕不能被動接受測試,更不能對自己說:「噢,測試就是這麼要求的,我又得這麼做。」
層級 | 對象 | |
---|---|---|
單元測試 | 系統內部 | 呼叫特定類別方法 |
驗收測試 | 系統外部 | 通常在 API 或 UI 層級進行 |
結論
細節交流是件麻煩事。尤其開發方和業務方交流關於程式的細節時,更是如此。通常,各方握手言歡,以為其他人都明白自己的意思。雙方以為取得了共識,然後帶著截然不同的想法離開,這種事屢見不顯。
要解決開發方和業務方的溝通問題,我 (Uncle Bob) 所知道的唯一有效辦法是「編寫自動化的驗收測試」。
第八章:測試策略
職位 | 測試面向 |
---|---|
業務人員 | 正常路徑測試 (happy-pathtest) |
QA | 包含極端狀況 (corner)、邊界條件 (boundary)的異常路徑測試(unhappy-path) |
自動化測試金字塔
測試 | 覆蓋率 | 測試介面 |
---|---|---|
人工探索式測試 | ~5% | |
系統測試 | ~10% | gui |
整合測試 | ~20% | api |
元件測試 | ~50% | api |
單元測試 | ~100% |
- 單元測試
- 目的: 在最低層次上定義系統
- 先寫測試,在寫程式碼
- 這些應該作為 CI 的一部分執行,以確保程式設計師的程式碼意圖沒有遭到破壞
- 目的: 在最低層次上定義系統
- 元件測試
- 需要使用合適的 mocking 或 test-doubling,解開「系統和其他元件」的耦合
- 由 QA 和業務人員編寫,開發人員提供輔助
- 整合測試
- 目的: 確認系統架構層面結構正確
- 只對元件很多的較大型系統才具有意義
- 由系統架構師或 lead designers 來編寫
- 系統測試
- 針對「整個整合完畢的系統」來執行的自動化測試
- 不會直接測試業務規則,而是測試系統是否以正確組裝完畢
- 應包含產能測試和性能測試
- 由系統架構師和 technical leads 來編寫
- 人工探索式測試
- 目的: 驗證預期行為時,探索系統預期以外的行為
第九章:時間管理
會議
關於會議,有兩條真理
- 會議是必須的
- 會議浪費了大量的時間
離席
- 仔細管理自己的時間是你的責任。如果你發現參加某個會議是在浪費時間,就應當想個禮貌的辦法出來
- 顯然,你不該大喊「這會議真讓人厭煩」,沒有必要採取粗魯的辦法。可以選個適當時機來問問大家,你的出席是否必要。你可以解釋說,自己抽不出更多的時間用於這場會議,問問有沒有辦法加快討論,或者另選時間
- 繼續參加對你沒有太多意義的會議,是不專業的行為
爭論 / 反對
- 如果觀點無法在短時間(5 ~ 30 分鐘)內達成一致,就永遠無法達成一致
- 唯一的解決方法是「去取得資料,讓資料來說話」
- 「既然其他人想要這樣做,就這麼做吧」這可能是非專業的行為中最糟糕的了,千萬千萬不要這麼做
- 如果你同意了,就必須拿出行動來
專注力 Manna
- 專注力是稀有的資源
- 如果你用光了自己的專注力 Manna,必須花一個小時或更多時間做不需要專注力的事情來補充它
- 專業開發人員會安排好他們的睡眠,保證清晨有飽滿的專注力 Manna 去上班
- 肌肉專注力有助於改善新制專注力,而不僅僅是簡單恢復
- 定期訓練肌肉專注力,可以提升心智專注力的上限
要避免的行為
- 優先順序錯亂
- 無論什麼原因,我們都可以找到辦法逃避真正的工作。提高某項任務的優先順序,之後就有藉口延後真正急迫的任務
- 專業開發人員會評估每項任務的優先順序,排除個人的喜好和需求,按照真實的緊急程度來執行任務
死胡同
- 專業開發人員不會執著於不容放棄也無法繞開的 idea。他們會保持開放的頭腦來聽取其他建議,所以即便走到盡頭,他們仍然有選擇
第10章:預估
- 承諾 v.s. 預估
- 承諾是必須做到的
- 預估是一種「猜測」,不帶任何承諾的色彩。之所以要預估,是因為不知道要花多少時間
PERT (Program Evaluation and Review Technique)
- 三元分析法
- O: Optimistic Estimate
- 一切都很順利的完成時間
- N: Nominal Estimate
- 一般來說的完成時間
- P: Pessimistic Estimate
- 遇到各種意外地完成時間
- $ \mu = \frac{O+4N+P}{6} $
- 任務期望完成的時間
- $ \sigma = \frac{P-O}{6} $
- 任務完成機率分布的標準差
- O: Optimistic Estimate
大數法則
- 把大任務分成許多小任務,分開預估再加總,結果會比單獨評估大任務要精確得多
第11章:壓力
避免壓力
承諾
- 避免對「沒有把握達成最後期限的工作」做出「承諾」
- 業務方總是期望能夠拿到這些承諾,因為他們想消除風險。我們要做的就是日風險量會,必將他們陳述給業務方,這樣他們就能做好相對的準備。
- 有時,有人會代我們做出承諾
- 出於責任感,我們必須主動協助找到方法來兌現這些承諾,但是一定不能接受這些承諾
危機中的紀律
- 如果在危機中你改變了行為,就說明你並不是真的相信常規行為中的紀律
- 如果在平時你會注意程式碼整潔,但在危機時刻,你卻會產出髒亂的程式碼,就說明你並不真正相信混亂會導致速度下降
應對壓力
- 不要驚慌失措
- 溝通
- 讓你的團隊或主管知道你身陷困境。告訴他們你為走出困境置地的最佳計畫。請求他們的支援與指引。避免製造意料之外的詫異
- 依靠你的紀律原則
- 依靠那些你已經知道確實有效的東西 - 你平時遵守的紀律
- 尋求幫助
- Pair Programming
第12章:協作
- 專業程式設計師的主要職責是滿足雇主的需求
- 這意味著要含你的經理們、業務分析師們、測試工程師們和其他團隊成員有良好的協作,並且深刻理解業務項目
- 你需要理解「手上正在撰寫的程式碼,其業務價值是什麼」,了解雇用你的企業將如何從你的工作中獲得回報
- 「需要長時間努力思考一個問題」、「任務極為簡單、和另一個人一起工作變成一種浪費」時單獨工作是正確的
- 但一般來說和其他人緊密協作、大部分時間採取 Pair Programming 是最好的作法
- 程式設計意味著「與人協作」
- 我們需要和業務人員一起工作,我們之間也需要互相合作
第13章:團隊與專案
- 形成有凝聚力的團隊是需要時間的
- 可能需要6個月,甚至是1年
- 團隊成員首先要建立關係。他們需要學習如何互相協作,需要瞭解彼此的癖好、強項、弱項,最終才能凝聚成團隊
- 有凝聚力的團隊通常有12名成員,多可以有20人,最少可以只有3人
- e.g., 12名
- 7名程式設計師
- 2名測試人員: 編寫「自動化測試」確認程式正確性
- 2名分析師: 開發「需求」,為需求編寫「自動化測試」確認業務價值正確
- 1名專案經理: 跟蹤團隊的「進度」,確保成員理解「專案時間表」和「優先順序」
- e.g., 12名
- 團隊比專案更難建置
- 組織穩健的團隊,讓團隊在一個又一個專案中整體移動、共同工作是較好的做法
- 團隊有了凝聚力,但卻因為專案結束了便將這樣的團隊解散,是極為荒謬可笑的做法
第14章:輔導、學徒期與工藝典範
- 學校能夠傳授的是電腦程式設計的理論
- 但學校並不會、也無法傳授作為一名程式設計工匠所需要掌握的原則、實踐和技能
- 這些東西只有經由師徒個體間多年的細心監督和輔導才能獲得
- 但學校並不會、也無法傳授作為一名程式設計工匠所需要掌握的原則、實踐和技能
Appendix A: 工具
- Source code control
- e.g., git
- IDE / Editor
- e.g., vi, emacs, IntelliJ, TextMate
- 問題追蹤
- e.g., Pivotal Tracker, Lighthouse, wiki, Board (To-Do/ Doing/ Done)
- CI
- e.g., Jenkins
- 單元測試工具
- 快速便捷的執行測試
- 在通過或失敗要有清楚的視覺提示
- 對於測試進度也要有清楚的視覺提示
- 避免 test cases 之間的彼此通訊
- 讓編寫測試變得容易
- 元件測試工具
- 理想的情況是「業務分析師和QA」能夠使用這些工具來編寫規約
- e.g, FitNesse, RobotFX, Cucumber
- 整合測試工具
- e.g., Selenium, Watir
- UML / MDA