代碼整潔之道
程序員的職業素養
專業主義
清楚你要什么
擔當責任
首先,不行損害之事
- 不要破壞軟件功能
- 讓QA找不出任何問題
- 要確信代碼正常運行
- 自動化QA
- 不要破壞結構
職業道德
- 了解你的領域
- 專業軟件開發人員必須精通事項
- 設計模式
- 設計原則
- 方法
- 實踐
- 工件
- 專業軟件開發人員必須精通事項
- 堅持學習
- 練習
- 業精于勤。真正的專業人士往往勤學苦干,以求得自身技能的純屬精煉。
- 合作
- 學習的第二個最佳方法是與人合作
- 輔導
- 想迅速牢固地掌握某些事實和觀念,最好的辦法就是與你負責指導的人交流這些內容。這樣,傳道受業的同時,導師也會從中受益。
- 了解業務領域
- 每位專業軟件開發人員都有義務了解自己開發的解決方案所對應的業務領域
- 與雇主/客戶保持一致
- 雇主的問題就是你的問題
- 謙遜
說“不”
能就是能,不能就是不能。不要說“試試看”
因為只有敢于說“不”,才能真正做成一些事情
對抗角色
- 最關鍵的是要找到那個共同目標,而這往往有賴于協商
“為什么”重要嗎
- 有時候解釋很重要,但是事實更重要
高風險時刻
- 最要說“不”的是那些高風險的關鍵時刻。越是關鍵時刻,“不”字就越具價值
要有團隊精神
- 具備團隊精神,意味著恪盡職守,意味著當其他隊員遭遇困境時你要援手相助
- 有團隊精神的人會頻繁與大家交流,會關心隊友,會竭力做到盡職盡責
說“是”的成本
- 運作良好的團隊的經理和開發人員,會相互協商,直至達成共同認可的行動方案
- 有時候,獲取正確決策的唯一途徑,便是勇敢無畏地說出“不”字
說“是”
承諾用語
- 三步驟
- 口頭上說自己將會去做
- 心里認真對待做出的承諾
- 真正付諸行動
- 識別“缺乏承諾”的征兆
- 包含的用詞和短語
- 需要/應當
- 希望/但愿
- 讓我們
- 包含的用詞和短語
- 真正的承諾聽起來是怎樣的
- 你對自己將會做某件事做了清晰的事實陳述,而且還明確說明了完成期限。那不是指別人,而是說的自己。你談的是自己會去做的一項行動,而且,你不是可能去做,或是可能做到,而是必須做到
- 沒能做到“言必信,行必果”的一些原因
- 之所以沒成功,是因為我寄希望于某某去做這件事
- 之所以沒成功,是因為我不太確信是否真能完成得了
- 之所以沒成功,是因為有些時候我真的無能為力
學習如何說“是”
- “試試”的另一面
- “試試”是“可能做得到,也可能做不到”的意思,但是問題往往需要得到明確的回答,“能”或是“不能”,而不是“試試”這種含糊不清的答案
- 堅守原則
結論
- 專業人士不需要對所有請求都回答“是”。不過,他們應該努力尋找創新的方法,盡可能做到有求必應。當專業人士給出肯定回答時,他們會使用正式的承諾,以確保各方能明白無誤地理解承諾的內容
編碼
要精熟掌握每項技藝,關鍵都是要具備“信心”和“出錯感知”能力
做好準備
- 首先,代碼必須能夠正常工作
- 代碼必須能夠幫你解決客戶提出的問題
- 代碼必須要能和現有系統結合得天衣無縫
- 其他程序員必須能讀懂你的代碼
流態區
- 高效率狀態,這種狀態通常稱為“流態”,也叫“流態區”
- 進入流態區可以敲出更多的代碼,但是放棄了顧全全局,后面有可能會需要重新審視這些代碼
- 避免進入流態區
阻塞
- 遇到什么代碼也寫不出來,可以嘗試進行結對編程
調試
- 不管是否采納TDD或其他一些同等效果的實踐,衡量你是否是一名專業人士的一個重要方面,便是看你是否能將調試實踐盡量降到最低。絕對的零調試時間是一個理想化的目標,無法達到,但要將之作為努力方向
- 醫生不喜歡重新打開病人的胸腔去修復此前犯下的錯誤。律師不喜歡重新接手此前搞砸的案子。經常重新返工的醫生或律師會被認為不專業。同樣,制造出許多bug的軟件開發人員也不專業
保持節奏
- 軟件開發是一場馬拉松,而不是短跑沖刺
進度延遲
- 期望
- 一個特性完不成,就不要讓其他任何人對此抱有期望
- 盲目沖刺
- 不要經受不住誘惑盲目沖刺
- 必須明白告訴老板、團隊和利益相關方,讓他們不要抱有可以靠沖刺完成特性的這種期望
- 加班加點
- 需滿足三個條件
- 你個人能擠出這些時間
- 短期加班,最多加班兩周
- 你的老板要有后備預案,以防萬一加班措施失敗
- 需滿足三個條件
- 交付失誤
- 在程序員所能表現的各種不專業行為中,最糟糕的是明知道還沒有完成任務卻宣稱已經完成
- 定義“完成”
- 可以通過創建一個確切定義的“完成”標準來避免交付失誤
幫助
- 編程并非易事
- 編程很難,事實上,僅憑一己之力無法寫出優秀的代碼。即使你的技能格外高超,也肯定能從另外一名程序員的思考與想法中獲益
- 幫助他人
- 互相幫助是每個程序員的職責所在
- 作為專業人士,要以能夠隨時幫助別人為榮
- 接受他人的幫助
- 如果有人向你伸出援手,要誠摯接受,心懷感激地接受幫助并誠意合作
- 為了能夠實現高效編程,好的協作至為重要
- 輔導
- 輔導缺乏經驗的程序員是那些經驗豐富的程序員的職責
- 培訓課程無法替代,書本也無法替代。除了自身的內驅力和資深導師的有效輔導之外,沒有東西能將一名年輕的軟件開發人員更快地提升為敏捷高效的專業人士
- 因此,再強調一次,花時間手把手地輔導年輕程序員是資深程序員的專業職責所在。同樣道理,向資深導師尋求輔導也是年輕程序員的專業職責
測試驅動開發
TDD的三項法則
- 在編好失敗單元測試之前,不要編寫任何產品代碼
- 只要有一個單元測試失敗了,就不要再寫測試代碼;無法通過編譯也是一種失敗情況
- 產品代碼恰好能夠讓當前失敗的單元測試成功通過即可,不要多寫
TDD的優勢
- 確定性
- 如果這些測試全部通過,我就確信它可以隨時交付
- 缺陷注入率
- 勇氣
- 擁有一套值得信賴的測試,便可完全打消對修改代碼的全部恐懼
- 當程序員不再懼怕整理代碼時,他們便會動手整理!整潔的代碼更易于理解,更易于修改,也更易于擴展
- 文檔
- 單元測試即是文檔
- 設計
- 為了編寫測試,你必須找出將這個函數和其他函數解耦的辦法
- 測試先行的需要,會迫使你去考慮什么是好的設計
- 專業人士的選擇
TDD的局限
- 即使做到了測試先行,仍有可能寫出糟糕的代碼。沒錯,因為寫出的測試代碼可能就很糟糕
練習
專業人士都需要通過專門訓練提升自己的技能,無一例外
自身經驗的拓展
- 開源
- 保持不落伍的一種方式是為開源項目貢獻代碼,就像律師和醫生參加公益活動一樣
- 關于練習的職業道德
- 職業程序員用自己的時間來練習
結論
- 無論如何,專業人士都需要練習
- 他們這么做,是因為他們關心自己能做到的最好結果
- 更重要的是,他們用自己的時間練習,因為他們知道保持自己的技能不落伍是自己的責任,而不是雇主的責任
驗收測試
專業開發人員既要做好開發,也要做好溝通
需求的溝通
- 過早精細化
- 做業務的人和寫程序的人都容易陷入一個陷阱,即過早進行精細化
- 不確定原則
- 在工作中,有一種現象叫觀察者效應,或者不確定原則。每次你向業務方展示一項功能,他們就獲得了比之前更多的信息,這些新信息反過來又會影響他們對整個系統的看法
- 預估焦慮
- 需求是一定會變化的,所以追求那種精確性是徒勞的
- 評估可以而且必須基于不那么精確的需求,這些評估只是評估而已
- 遲來的模糊性
- 解決分歧的方案,是尋找各方都同意的關于需求的表述,而不是去解決爭端
- 即便客戶與程序員當面溝通,也可能出現因語境產生的模糊
驗收測試
- “完成”的定義
- 專業開發人員的“完成”只能有一個含義:完成,就是完成
- 完成意味著所有的代碼都寫完了,所有的測試都通過了,QA和需求方已經認可。這,才是完成
- 溝通
- 驗收測試的目的是溝通、澄清、精確化
- 開發方、業務方、測試方對驗收測試達成共識,大家都能明白系統的行為將會是怎樣
- 各方都應當記錄這種準確的共識
- 自動化
- 驗收測試都應當自動進行。原因很簡單:要考慮成本
- 額外工作
- 寫大量測試根本不是什么額外工作。寫這些測試是為了確定系統的各項指標符合要求
- 驗收測試什么時候寫,由誰來寫
- 在理想狀態下,業務方和QA會協作編寫這些測試,程序員來檢查測試之間是否有沖突或矛盾
- 通常,業務分析員測試“正確路徑”,以證明功能的業務價值
- QA則測試“錯誤路徑”、邊界條件、異常、例外情況,因為QA的職責是考慮哪些部分可能出問題
- 遵循“推遲精細化”的原則,驗收測試應該越晚越好,通常是功能執行完成的前幾天。在敏捷項目中,只有在選定了下一輪迭代或當前沖刺所需要的功能之后,才編寫測試
- 開發人員的角色
- 開發人員有責任把驗收測試與系統聯系起來,然后讓這些測試通過
- 測試的協商與被動推進
- 身為專業開發人員,與編寫測試的人協商并改進測試是你的職責
- 身為專業開發人員,你的職責是協助團隊開發出最棒的軟件。也就是說,每個人都需要關心錯誤和疏忽,并協力改正
- 驗收測試和單元測試
- 驗收測試不是單元測試。單元測試是程序員寫給程序員的,它是正式的設計文檔,描述了底層結構及代碼的行為。關心單元測試結果的是程序員而不是業務人員
- 驗收測試是業務方寫給業務方的(雖然可能最后是身為開發者的你來寫)。它們是正式的需求文檔,描述了業務方認為系統應該如何運行。關心驗收測試結果的是業務方和程序員
- 圖形界面及其他復雜因素
- 持續集成
- 立刻中止
- 保持持續集成系統的時刻運行是非常重要的
- 持續集成不應該失敗,如果失敗了,團隊里的所有人都應該停下手里的活,看看如何讓測試通過
- 立刻中止
結論
- 交流細節信息是件麻煩事
- 要解決開發方和業務方溝通問題,我所知道的唯一有效的辦法就是編寫自動化的驗收測試
測試策略
QA應該找不到任何錯誤
- QA也是團隊的一部分
- 需求規約定義者
- 特性描述者
自動化測試金字塔
- 單元測試
- 這些測試由程序員使用與系統開發相同的語言來編寫,供程序員自己使用
- 組件測試
- 系統的組件封裝了業務規則,因此,對這些組件的測試便是對其中業務規則的驗收測試
- 組件測試差不多可以覆蓋系統的一半。它們更主要測試的是成功路徑的情況,以及一些明顯的極端情況、邊界狀態和可選路徑
- 大多數的異常路徑是由單元測試來覆蓋測試的
- 在組件測試層次,對異常路徑進行測試并無意義
- 集成測試
- 這些測試只對那些組件很多的較大型系統才有意義
- 集成測試是編排性測試。它們并不會測試業務規則,而是主要測試組件裝配在一起時是否協調。它們是裝配測試,用以確認這些組件之間已經正確連接,彼此間通信暢通
- 系統測試
- 這些測試是針對整個集成完畢的系統來運行的自動化測試,是最終的集成測試
- 人工探索式測試
- 覆蓋率并非此類測試的目標
- 探索式測試不是要證明每條業務規則、每條運行路徑都正確,而是要確保系統在人工操作下表現良好,同時富有創造性地找出盡可能多的“古怪之處”
結論
- TDD很強大,驗收測試是表達和強化需求的有效方式
- 開發團隊要和QA緊密協作,創建由單元測試、組件測試、集成測試、系統測試和探索式測試構成的測試體系
- 應該盡可能頻繁地運行這些測試,提供盡可能多的反饋,確保系統始終整潔
時間管理
會議
- 拒絕
- 受到邀請的會議沒有必要全部參加
- 領導的最重要責任之一,就是幫你從某些會議脫身。好的領導一定會主動維護你拒絕出席的決定,因為他和你一樣關心你的時間
- 離席
- 如果會議讓人厭煩,就離席
- 選個合適的機會商量如何離席,并非不專業的做法
- 確定議程與目標
- 為了合理使用與會者的時間,會議應當有清晰的議程,確定每個議題所花的時間,以及明確的目標
- 立會
- 到場人依次回答3個問題
- 我昨天干了什么?
- 我今天打算干什么?
- 我遇到了什么問題?
- 到場人依次回答3個問題
- 迭代計劃會議
- 迭代計劃會議用來選擇在下一輪迭代中實現的開發任務
- 會議召開前必須完成兩項任務
- 評估可選擇任務的開發時間
- 確定這些任務的業務價值
- 會議的節奏應該足夠快,簡明扼要地討論各個候選任務,然后決定是選擇還是放棄
- 迭代回顧和DEMO展示
- 爭論/反對
- 技術爭論很容易走入極端。每一方都有各種說法來支持自己的觀點,只是缺乏數據。在沒有數據的情況下,如果觀點無法在短時間(5~30分鐘)里達成一致,就永遠無法達成一致。唯一的出路是,用數據說話
- 如果爭論必須解決,就應當要求爭論各方在5分鐘時間內向大家擺明問題,然后大家投票。這樣,整個會議花的時間不會超過15分鐘
注意力點數
- 選擇注意力點數充裕的時候編程,在注意力點數匱乏時做其他事情
- 睡眠
- 一覺醒來,注意力點數是最充裕的
- 咖啡因
- 對有些人來說,適量的咖啡因可以幫他們更有效地使用注意力點數
- 恢復
- 在你不集中注意力的時候,注意力點數可以緩慢恢復
- 肌肉注意力
- 搏擊、太極、瑜伽之類體力活動使用的注意力是不同的
- 定期訓練肌肉注意力,可以提升心智注意力的上限
- 輸入與輸出
- 關于注意力,另一重點是平衡輸入與輸出
時間拆分和番茄工作法
要避免的行為
- 優先級錯亂
- 專業開發人員會評估每個任務的優先級,排除個人的喜好和需要,按照真實的緊急程度來執行任務
死胡同
- 專業開發人員不會執拗于不容放棄也無法繞開的主意
- 保持開放的頭腦來聽取其他意見,所以即使走到盡頭,他們仍然有其他選擇
泥潭
- 比死胡同更糟的是泥潭
- 在泥潭中繼續前進的危害是不易察覺的
- 發現自己身處泥潭還要固執前進,是最嚴重的優先級錯亂
結論
- 專業開發人員會用心管理自己的時間和注意力
預估
什么是預估
- 問題在于,不同的人對預估有不同的看法。業務方覺得預估就是承諾。開發方認為預估就是猜測。兩者相同迥異
- 承諾
- 承諾是必須做到的
- 承諾是關于確定性的
- 預估
- 預估是一種猜測。它不包含任何承諾的色彩。它不需要做任何約定。預估錯誤無關聲譽
- 暗示性承諾
- 專業開發人員能夠清楚區分預估和承諾
- 只有在確切知道可以完成的前提下,他們才會給出承諾
- 專業開發人員會小心避免給出暗示性的承諾。他們會盡可能清楚地說明預估的概率分布
PERT
- O:樂觀預估
- N:標稱預估
- P:悲觀預估
預估任務
- 德爾菲法
- 亮手指
- 規劃撲克
- 關聯預估
- 三元預估
大數定律
- 把大任務分成許多小任務,分開預估再加總,結果會比單獨評估大任務要準確很多
結論
- 專業開發人員懂得如何為業務人員提供可信的預估結果,以便做出計劃
- 專業開發人員一旦做了承諾,就會提供確定的數字,按時兌現
- 對需要妥善對待的預估結果,專業開發人員會與團隊的其他人協商,以取得共識
壓力
避免壓力
- 承諾
- 有時有人會代我們做出承諾。比如業務人員可能在沒有事先咨詢我們的情況下就向客戶做出了承諾。發生這種事情時,出于責任感我們必須主動幫助業務方找到方法來兌現這些承諾,但是一定不能接受這些承諾
- 其中的差別至關重要。專業人士總會千方百計地幫助業務方找到達成目標的方法,但并不一定要接受業務方代為做出的承諾。
- 最終,如果我們無法兌現業務方所做出的承諾,那么該由當時做出承諾的人來承擔責任
- 保持整潔
- 快速前進確保最后期限的方法,便是保持整潔
- 臟亂只會導致緩慢!
- 讓系統、代碼和設計盡可能整潔,就可以避免壓力
- 危機中的紀律
- 如果在危機中依然遵循著你守持的紀律,就說明你確實相信那些紀律
- 當困境降臨時,也不要改變行為。如果你遵守的紀律原則是工作的最佳方式,那么即使是在深度危機中,也要堅決秉持這些紀律原則
應對壓力
- 不要驚慌失措
- 溝通
- 讓你的團隊和主管知道你正身陷困境之中
- 依靠你的紀律原則
- 當事情十分困難時,要堅信你的紀律原則
- 尋求幫助
結論
- 應對壓力的訣竅在于,能回避壓力時盡可能地回避,當無法回避時則勇敢直面壓力
協作
程序員與雇主
- 專業程序員的首要職責是滿足雇主的需求
- 專業程序員會花時間去理解業務
程序員與程序員
- 代碼個體所有
- 從技術角度來看,這無疑是一場災難
- 協作性的代碼共有權
- 專業開發人員是不會阻止別人修改代碼的
- 結對
- 最有效率且最有效果的代碼復查方法,就是以互相協作的方式完成代碼編寫
結論
- 也行我們不是因為通過編程可以和人互相協助才選擇從事這項工作的。但真不走運,編程就意味著與人協作。我們需要和業務人員一起工作,我們之間也需要互相合作
團隊與項目
只是簡單混合嗎
- 有凝聚力的團隊
- 形成團隊是需要時間的
- 有凝聚力的團隊通常有大約12名成員
- 7名程序員
- 2名測試人員
- 2名分析師
- 1名項目經理
- 發酵期
- 6個月至1年
- 團隊和項目,何者為先
- 專業的開發組織會把項目分配給已形成凝聚力的團隊,而不會圍繞著項目來組建團隊
- 如何管理有凝聚力的團隊
- 每個團隊都有自己的速度
- 團隊的速度,即是指在一定時間段內團隊能夠完成的工作量
- 項目承包人的困境
- 反對意見:這會讓項目承包人失去些安全感和權力
- 組建和解散團隊只是人為的困難,公司不應受到它的束縛。如果公司在業務上認為一個項目比另一個項目的優先級更高,應該要快速重新分配資源
結論
- 團隊比項目更難構建。因此,組建穩健的團隊,讓團隊在一個又一個項目中整體移動共同工作是較好的做法
輔導、學徒期與技藝
失敗的學位教育
- 在學校中所學的內容和在工作中發現的實際需要,這兩者之間通常會有巨大的差異
輔導
- 真正的“老師”、“大師”或是“導師”
- 能夠深入淺出地指導我跨過其中的溝溝壑壑
- 我可以在給他打下手完成一些小任務時觀察他的工作方式
- 他會對我的工作進行審查,指導我的早期工作
- 他會專門教導我建立正確的價值觀和反思內省的習慣
學徒期
- 軟件學徒期
- 大師
- 他們是那些已經領到過多個重要軟件項目的程序員
- 熟練工
- 他們還處在受訓期中,不過已能勝任工作,而且精力充沛。在職業生涯的當前階段,他們將會學習如何在團隊中卓越工作和成為團隊的領導者
- 學徒/實習生
- 畢業生會從學徒這一步開始他們的職業生涯。學徒沒有“自治權”,他們需要在熟練工的緊密督導下工作
- 學徒期至少應持續一年
- 大師
- 現實情況
- 上述這些描述是假設的一種十分理想化的狀況
- 觀念上最大的差別在于,專業主義價值觀和技術敏銳度需要進行不斷的傳授、培育、滋養和文火慢燉,直至其完全滲入文化當中
- 我們當前的做法之所以傳承無力,主要是因為其中缺失了資深人士輔導新人向其傳授技藝的環節
技藝
- 技藝是工匠所持的精神狀態
- 技藝的“模因”(meme)中包含著價值觀、原則、技術、態度和正見
- 技藝模因經由口口相傳和手手相承而來,需要由資深人士向年輕學徒殷勤傳授,然后再在學徒之間相互傳播
- 覺者覺人
- 你無法說服別人成為一名匠者,你無法說服他們去接受技藝模因
- 只要技藝模因可以被人觀察到,它便具有傳染性。因此只需讓技藝模因可以被他人觀察到即可
- 你自己首先要成為表率。你自己首先要成為能工巧匠,想人們展示你的技藝。然后,將剩余的事情交給技藝模因的自然運行之道即可
結論
- 學校并不會也無法傳授作為一名編程匠者所需掌握的原則、實踐和技能
- 指引下一代軟件開發人員成熟起來的重任無法寄希望于大學教育,現在這個重任已經落到了我們肩上