作者 | 李冬梅
審校 | 劉燕
史蒂夫·邁克康奈爾(Steve )被公認為軟件開發社區中的首要作者和發言人之一。他是 公司的首席軟件工程師。他所編著的圖書包括曾被《軟件開發》雜志授予優異產品震撼大獎的《代碼大全》和《快速軟件開發》,以及《軟件項目生存指南》和《專業軟件開發》等等。
他撰寫的無論是《代碼大全》還是《快速軟件開發》,主要聚焦的內容都是敏捷開發、高效開發,許多開發者在閱讀過兩本書后均表示 Steve 撰寫的書實用性很高,是歷久彌新、永不褪色的經典之作。
從第一版《代碼大全》到最新出版的《卓有成效的敏捷》,20 多年來,Steve 一直倡導敏捷開發、高效開發。不久前,InfoQ 有幸采訪到了 Steve,與他聊到了創作《代碼大全》的過程以及他對敏捷開發、編程復雜性、代碼可讀性等時下熱門話題的理解。
Steve 稱,在最初寫作時,他并沒有想過寫一本篇幅如此宏大、知識點覆蓋如此全面的書籍。據他回憶,為了完成《代碼大全》,他曾翻閱 80 多本書,但在翻閱書籍的過程中卻發現并沒有一本書能詳實關于軟件開發實踐的細節,于是 Steve 決定自己寫一本這樣的書來填補空白。
在書中,Steve 特別強調了要管理軟件的復雜性,Steve 認為大型編程項目極具挑戰,要管理成千上萬人寫的代碼,如果不對復雜性做出限制,那么項目的每個步驟都可能崩潰失控。
提及敏捷開發時,Steve 認為不能把敏捷單獨列出,也不把它當成獨立于編程之外的東西,它只是一種普通的編程方式,很大程度上已經成了良好實踐的統稱,不必過于糾結是否是敏捷。除了敏捷之外,另一種好的編程方式是寫出可讀強的代碼,Steve 表示,“能跑起來的代碼不一定是好代碼”,代碼是為人服務的,真正的好代碼得能隨時間推移而持續成功,這樣才能供他人處理和擴展而且可以更正,還要看他人能不能讀懂代碼內容。
以下為 InfoQ 與《代碼大全》作者的獨家訪談實錄,經編輯。
1 耗時三年半,寫完 900 多頁《代碼大全》
InfoQ:您最近都在忙什么?在關注哪些技術呢?
Steve:我最近在研究領導力方面的內容,我覺得很多軟件從業者,在職業生涯后期總會進入一種模式——會更多地開始扮演領導角色,我本人也是如此。就技術發展和技術趨勢而言,在第一版《代碼大全》出版以來,我覺得這些年間最讓人印象深刻的變化就是程序員們往往將大部分時間花在一種技術上或者說只關注一種編程語言。
當然,人們也會學習其他工具,但多數人確實可能每天都在用 C 或者 C++,如果再回溯得更古早些時候,也可能是 Fortran。到我完成第二版《代碼大全》時,人們將大部分時間花在同一種技術上的情況也仍然非常普遍。
即使是現在程序員們的工作技術棧,也總有種例行公事的感覺,他們只是在象征性地使用多種語言或者是把低級語言跟腳本語言混合使用。在《代碼大全》的序言部分,我說我認為程序員職業生涯中的分水嶺,就是開始學習第二語言,但時至今日我發現使用兩種語言才是普遍現象。這已經成為他們日常業務的一部分,甚至不只兩種語言,我不知道該不該稱其為趨勢,這種情況在最近這幾年已經屢見不鮮了。
InfoQ:您當初創作《代碼大全》的初衷是什么?第一版耗時多久完成的?中間發生過哪些令您印象深刻的事?
Steve:這本書大概花了我三年半的時間才完成,差不多 3500 個小時的全身心投入。我最初只打算寫篇雜志投稿,只想寫點編程風格和編程技巧之類的短文,于是我特意為此收集了一些資料。在寫第一版那會兒還沒有互聯網,所以不能靠搜索引擎來找到我想要的答案,而是得去大學、去學術圖書館里找資料。我花了很長的時間在巨大的館廊里跑來跑去,翻閱各種雜志,還得查看那些實體卡片目錄,然后再到相應的書架上找書。
我住的地方離華盛頓大學很近,所以我就去了那邊校區里至少三處不同的圖書館查閱資料。當時,我以為肯定已經有人寫了關于軟件開發實踐的書籍,只是我沒聽說過罷了。
當時,我大概翻閱了 80 多種不同的書籍和文章,隨著資料查得越來越多,研究越來越深入,我發現市面上與軟件編程實踐相關的書籍并不多,甚至沒有一本書是專門討論軟件編程實踐主題的,這讓我十分震驚。而在我翻閱眾多書籍的過程中,我積累了許多關于這方面的知識,所以我決定自己寫一本這樣的書來填補這一空白。
在這三年半期間,我用了大概一年半的時間收集資料、試寫部分章節,之后花了一年時間全職寫書,又用了近一年時間參與編輯。
在我心里,我深知軟件開發實踐內容的重要性,但當我寫這么一本書的時候,我發現很難向他人解釋書的內容,他們理解不了我在說什么、做什么。我甚至專門找了位朋友幫我從頭到尾審閱手稿,我覺得這樣邊寫邊給他看,理解起來應該很簡單。讓我感到意外的是,他并不能完全理解我在寫些什么,這也讓我意識到,很多對我自己顯而易見的問題對其他人來說很難理解,所以我寫這本書時盡量讓內容詳實、易懂。
在寫書的三年多時間里,讓我印象最深刻的就是為手稿和大綱收集同行評審意見。我跟出版商簽過合同 所以得先提供一份大綱,我就試寫了幾個章節交給五六個朋友審閱,他們的意見非常重要,我最終提交給出版商的素材至少是評審之前的兩倍。而在真正開始寫手稿時,內容接受了約 12 個人的審閱,那時還沒有互聯網,我就把紙質版手稿分章節寄給他們,收到的意見反饋同樣是手寫信件或電郵副本,而最終出版的第一版《代碼大全》的篇幅也從最初設想的 250 到 350 頁激增至 900 多頁。
回顧整段經歷,我體會到了同行評審的價值,時至今日,這些手寫信件和電郵對我來說都彌足珍貴。
InfoQ:這本書取得的成就是否滿足了您的預期?
Steve:在 1992 年向出版商提交第一版手稿時,我從沒想過 25 年后會在采訪中談寫書的感受,太奇妙了!
創作初期,我給自己定的目標沒有那么高,如果當時就想到要寫 900 多頁,可能就會頭大了。總之,雖然最終耗費了很大的工作量,但最終《代碼大全》的反饋很好,它的銷量遠超我的想象。當時我覺得最好的情況大概能賣 10 萬冊,但在被翻譯成不同語言和版本之后,它賣出了超 100 萬冊,這比我最樂觀的估計都要多得多。
2 編程語言沒有好壞之分
InfoQ:您在《代碼大全》書中曾提到,“與使用低級語言的程序員相比,使用高級語言的程序員可以獲得更高的生產力和質量。與匯編語言和 C 等低級語言相比,C++、Java、 和 Visual Basic 等語言在提高生產率、可靠性、簡單性和可理解性方面被認為提高了 5 到 15 倍”。但據 TIOBE 2022 年 10 月編程語言排名來看,Python、Java、C 成為了 TIOBE 榜單的前三甲,還是有很多人愿意使用 C 語言,對此現象您怎么看?
Steve:在我當初發表這條評論,或者引用程序員使用高級語言時效率更好的結果時,談論的主體其實都是通用語言,而實際上絕大多數語言也都屬于通用語言。所以我想要表達的是只要任務能用通用語言來完成,那使用高級通用語言要比使用低級通用語言的效率高。但之后多年當中,語言本身的專業化傾向越來越顯著,語言在特定用途上的表現也更好了,換言之,某些語言確實不再像過去那么“通用”了。
C 語言之所以還那么受歡迎,是因為一些從事硬件設備或其他類型的低級編程的程序員們確實還是很喜歡使用它。對于這類特定用途的編程來說,C 仍是個好工具。需要強調的是,我提到的高級編程語言可以提高效率這一概念的核心是為合適的工作選擇合適的語言,而不是單純地評價哪種語言是好或是壞。
我現在的觀點是,如果某種語言對于任務來說太高級了,那就得額外花時間搞清很多實現細節,這就跟設計目的錯位了。同樣地,如果選的語言對任務來說太低級,那就得額外做大量編碼才能完成工作,所以語言的優劣只體現為更適合哪種工作場景,而不是說一種語言天生就比另一種語言好。
InfoQ:第二版《代碼大全》和第一版最大的區別是什么?
Steve:第二版《代碼大全》確實跟第一版那會兒有不同。在寫第一版時,確實花了很長時間來打磨內容。面向對象編程其實更像是研究課題,而非具體實現,那時候敏捷理念還沒出現。到我寫第二版的時候,面向對象編程已經成為實用議題,而且在商業實踐中得到了廣泛應用,其普及度之高,甚至火出了圈,成為了一種可供人們參考的編程方式。另一方面,在編寫第二版時,敏捷的概念仍然很新,所以編寫第二版時的一大挑戰,就是決定要在多大程度上把敏捷設置成獨立議題,或者說把相關良好實踐整合到常規工作當中。
最終我決定不把敏捷單獨列出,也不把它當成獨立于編程之外的東西,我只談它的好處,視它為一種普通的編程方式,我覺得這個決定還挺明智的,我認為敏捷很大程度上已經成了良好實踐的統稱,這些實踐普遍有效而且包含或成功或失敗的文化迭代,這就是第一版與第二版之間發生的大變化。
具體來講,編程語言及其使用方式都與以往有了很大不同,我認為第二版中使用的編程語言到現在仍未過時,而且如今人們在用的語言數量則出現了爆炸式增長,這是實實在在的轉變。
3 軟件構建的核心就是管理復雜度
InfoQ:您身上有很多標簽,比如技術作家、演講者、企業家等,這些角色中您最喜歡哪一個,為什么?
Steve:我自己其實一直在程序員和作家這兩個角色間反復橫跳。從一線程序員到全職作家,單這個過渡期就有一年。在寫了一陣子書之后,我感覺自己愛寫書開始多于寫代碼。但到后面我又開始厭倦了,我迫切想要回去編程,當時我甚至感覺自己以后再不會寫書了。
在又編了幾年程之后,我覺得編程挺好,卻也懷念寫作,所以又開心地投入到第二本書中,邊寫邊覺得寫書才最快樂。但第二版寫到了一定階段,我又想起了編程的好,最終我意識到自己其實不太定性,喜歡反復橫跳。
但我也從編程中學到了很多寫編程書所用的知識,如果我僅僅是一名作家,那可能永遠沒機會驗證自己寫的東西能不能真正跑起來。所以不能說我單純喜歡寫作或者編程,這兩者對我來說是相輔相成,相互成就的。
InfoQ:您書中傳遞出來兩個很重要的觀點:1,軟件構建的核心就是管理復雜度;2,以程序員為本。先來說說第一個,軟件構建的核心就是管理復雜度,為什么會這樣認為,能舉個具體的例子嗎?寫代碼時該如何管理好復雜度,您有什么建議嗎?
Steve:沒錯,我在書里談了不少如何管理復雜性,因為我覺得只要了解編程的基礎知識,那小型編程項目其實都不困難。但大型編程項目卻極具挑戰,部分原因在于人員管理問題以及如何協調大量人員,這是一項極其困難的腦力工作。
實際上,軟件開發是極少數既需要協調人量人力又不具備實體的工程技術項目,建筑項目也很復雜而且涉及大量參與者,但他們面對的是有形的實體,所以能以物理形式進行跟蹤。同樣,橋梁隧道之類的工程項目也是如此,現場參與者雖然成百上千,但各個步驟或者施工環節確有切實的線索可抓。但在軟件中很多工作無形無相,所以除了我們自己的想象力外再無跟蹤事態的可靠線索。所以大型軟件項目因復雜性而失控的可能性遠超人類從事過的幾乎任何其他項目,其根源就是軟件項目的無形性,而且大型軟件項目很快就會超出個人的理解力上限,沒人能理解項目中的所有代碼,畢竟這是成百上千人的成果。
所以面對這些規模更大的項目,如果不對復雜性做出限制,那么項目的每個步驟都可能崩潰失控。以往,無數大型項目都在砸下巨量資金后失敗了,原因就是其過于復雜,已經無人能理解究竟發生了什么,就如同過于龐大的巨獸被自身重量所壓垮。
具體來講,復雜性其實貫穿于軟件開發的各個階段,復雜性在編碼過程中,因為如果底層代碼的質量不好,超大規模系統也可能就由此崩潰。所以必須立足底層,立足細節抓代碼質量,關注每個語句、例程和類,步步為營,以此為基礎才能擴大規模,同時繼續保持代碼質量,即在設計和架構層級控制復雜性,對復雜性控制要廣泛而深入地體現在軟件開發的各個階段。
4 “能跑起來的代碼不一定是好代碼”
InfoQ:您書中傳遞出來的第二個觀點是以程序員為本,您書中提到的“以程序員為本”,即代碼是為了人而寫的,不是為了機器,所以代碼要以程序員為本,為程序員服務,那么該如何提高代碼的可讀性呢?
Steve:很多科班出身的程序員尤其是很多初入職場的新手一直有個誤區,他們認為能跑得起來的代碼就是好代碼。但專業級別的商業編程有新的要求,代碼不能只跑通一次。學校作業能跑通一次就算合格,但商業代碼得能穩定運行多年,工作五年、十年、二十年,還得易于理解,這樣移交后,其他人也能處理后續,繼續移交,并仍然能保持穩定。第四、五、六個人都能接手,這明顯跟大學教育里的要求完全不同了。能跑通的代碼并不可靠,必須做得更好,否則你就不算是專業的程序員,真正的好代碼得能隨時間推移而持續成功,這樣才能供他人處理和擴展而且可以更正,還要看他人能不能讀懂代碼內容。
很多程序員認為自己的主要工作是跟計算機溝通,編寫計算機能懂的代碼就行。確實,但也不完全對,還有另一部分,就是通過代碼跟其他人溝通,這是一種非常重要的心理轉變。
有時候,甚至富有經驗的程序員也會犯錯,誤以為代碼能跑就萬事大吉。在我看來,代碼能跑通只是萬里長征的第一步,往上還有很多級,拾級而上才能讓代碼具有可理解性,可供他人接手處理。
5 是否敏捷并不重要,重要的是能解決問題
InfoQ:在您的書中,您大部分內容都聚焦在敏捷開發上,但似乎敏捷開發并不適合所有企業,您認為什么樣的團隊適合敏捷?
Steve:關于敏捷開發,我寫了一本新書《More Agile》。“(有效)”這個詞我是認真考量過的,我覺得關于敏捷開發有不少認知誤區,好像開發只有敏捷或者不敏捷之分。事實上,不能這樣簡單粗暴地定義敏捷開發。
現在,敏捷概念下集中了大量實踐。二十多年來,我們一直在討論實踐、討論敏捷,討論能不能支持敏捷原則和價值主張,但我覺得這些討論并沒有抓住重點,重點應該是真正用軟件和開發來達成某些目標,某些敏捷案例能夠支持業務目標,而某些則不能,所以與其硬去評判某個項目是否敏捷,倒不如考慮該向項目引入哪些元素來支持業務目標。如果我們開發的是移動應用,而且該應用的更新需求比較頻繁,那也許可以每周更新一次,有些組織的軟件每天可能更新幾百次,對于這類組織,敏捷就很有意義。但如果所開發的是嵌入式固件軟件,比如是那些需要安裝在飛機、航天器或者船舶上的設備,而且設備本身的更新頻率不會太高,我覺得這類軟件就不適合敏捷。
可靠性跟高質量是兩個不同的目標,而敏捷并不普遍適用。當然,也有一些辦法能把敏捷實踐元素引入到安裝到飛機上的軟件中,這時候它們的意義就不在于敏捷而只是種被證明能提高質量的有效辦法。到底是否是敏捷并不重要,重要的是這些實踐能否幫助項目達成目標。
InfoQ:“試圖通過增加測試量來提高軟件質量,就像試圖通過更頻繁地稱重自己來減肥一樣。你在上秤前吃的東西決定了你的體重,而你使用的軟件開發技術決定了測試會發現多少錯誤”,但不經過足夠的測試,如何判定軟件的質量呢?您認為軟件測試的量多少為最佳,是否有個標準?
Steve:我并不是說測試不重要,測試當然很重要,我想表達的是,按當時的軟件開發方式,人們更傾向于先編寫大量代碼后再統一做測試,那時候的測試屬于事后環節。
在之后的幾年中,開發模式發生了很大變化,測試開始更多融入到開發流程當中。所以現在來看那個比喻已經不適用了,更準確的說法應該是:我剛吃了一根胡蘿卜,就跑到秤上稱一下看看有什么變化,之后再吃一根胡蘿卜,再稱一下,這么做對于結果來說毫無影響。吃點東西就去稱體重的做法是荒謬的,但寫點代碼就去測確實是正確的軟件測試思路,更理想的思路甚至希望在編寫代碼前先編寫測試。
我在《代碼大全》第一版里分享過一個故事,故事說有個女人每天起床都會上秤,只要發現超重,她就只吃胡蘿卜和芹菜直到恢復到她理想的體重狀態,因為她每天都堅持這么做,所以她從不會超重太多,因為每次發現超重后她就會立刻糾正,這基本就是軟件測試的意義。只要測試夠頻繁就不會遇到太多麻煩。
6 編寫代碼的最快方法就是提高代碼質量
InfoQ:您提到過,“復制和粘貼是一種設計錯誤”,但其實很多代碼是通用的,我們如何能“既保證代碼的原創性又不去重復造輪子”呢?這中間的尺度該如何把握?
Steve:“復制和粘貼是一種設計錯誤”其實不是我提出來的,是其他開發伙伴提出來的,我很喜歡這句話。這話的意思是,如果你正在編寫代碼而且在復制和粘貼代碼片段,那你就錯過了深入剖析代碼的機會。當然,復制和粘貼也有其必要性,我自己有時候也會復制和粘貼,但我想強調的是希望大家在復制粘貼時能思考下到底要不要深入琢磨問題中的共性,這背后是不是有需要關注的規律?
技術發展至今,開發者們變得更加聰明,開發效率也更高效,他們已經習慣把經常調用的模塊打包起來以便隨時復用,這和簡單的復制粘貼還是不完全一樣的。我們現在的全球基礎設施令人贊嘆,它的根基就是大規模的軟件復制,這是很正常的現象,沒什么不對。
InfoQ:復制粘貼能提高一些編程效率,但如果想要在更短時間內寫出更高質量的代碼,程序員應該培養哪些技能或編程習慣?
Steve:長久以來的經驗表明,編寫代碼的最快方法就是提高代碼質量。1970 年,IBM 的一項經典研究發現,被要求盡快完成工作的編程團隊最容易寫出有缺陷的代碼,而且交付速度也最慢;被要求保證質量的團隊不光代碼質量好,交付也更及時。
所以這個問題的答案就很明確了:要重視代碼質量,質量就是結果。對于那些需要超級可靠的軟件,比如飛機的飛行控制軟件或者嵌入心臟起搏器或醫療設備的軟件,那就得通過更長的時間和額外的步驟才能保證這樣的質量水平。但對于常規的商業編程、應用編程而言,最有效的辦法就是盡量保證初始代碼的高質量,這樣就不用花太多時間調試和重寫。
很多程序員是花半個小時寫代碼然后花四個小時做調試,這明顯大有問題,如果程序員最初能花一個小時甚至更多時間對代碼“精雕細琢”,那也許后期的調試只需要半個小時且整體效果更好。
7 80% 的情況下沒必要重構代碼
InfoQ:《代碼大全》這本書主張開發者應跳過“先做再修正”(code and fix) 和“始于大設計”(Big Design Up Front)的瀑布模型,近年來這個模型有哪些變化嗎?您想通過這個模型向外傳遞的價值點是什么?
Steve:我寫這本書絕不是讓大家跳過瀑布模型,或者讓人們以特定某種方式做事,比如預先做整體設計,這本書想讓人們擺脫純編碼和純修復,把編程變成一件有趣的事。我認為有更多系統性的方法能幫助大家縮短代碼編寫時間,但這種方法不會是僵化的,人們不該耗費大量時間調試代碼、糾正代碼和修復錯誤代碼,我寫這本書的目標是幫助大家以一種能保證代碼質量的方式來編程,享受編程帶來的樂趣。
在敏捷方面,特別是在 20 年前首次開始討論敏捷時,我提出“提前做整體設計”其實是要受抨擊的。當時人們認為預先做整體設計不值得提倡,我自己對這個問題的看法也在逐漸發生變化。在我早期的職業生涯,特別是在寫初版《代碼大全》時,我確實覺得預先做整體設計是對的,那時這個理念并不盛行,大家做項目時只是在盡量做預估,在投身于細節之前至少先想想要干什么。
但多年來我意識到,不同開發者所掌握的技能組合確實差異很大,部分開發者更擅長以抽象方式設計之后再實現這些設計代碼,也有些開發者更擅長編寫代碼然后邊寫邊學,邊寫邊改。這兩種方式或許最終都能得令人滿意的結果,而這兩類開發者都無法照抄照搬對方的工作模式。
技術項目能否取得成功的重點在于人的思維是如何躍動的,所以如果某個程序員的思維是從抽象到具象的演繹,而另一個人的思維是從具象到抽象的歸納,那絕不能把兩人的思維模式強行互換。
略顯遺憾的是,過去二三十年間,編程行業一直想強迫每個人以相同的思維方式工作,這是注定要失敗的。
InfoQ:您曾提到,“將時間花在提供 80% 收益的 20% 的重構上”,這句話我們該怎樣理解?
Steve:說起重構,我曾在網上看到過一個帖子,說的是程序員的代碼能跑,就不用改,根本就不需要去碰它。但出于種種原因,它還是會出 bug,所以最后還是進行了重構。
但在我看來,重構其實沒什么必要。因為就算不改,代碼也能運行良好,這樣的代碼實際就如同黑盒,沒必要打開盒子換里面的東西,因為沒有改進的潛在價值,所以沒有動它的必要,相反,重構還可能搞出新的錯誤。
所以我說的二八定律其實是說,絕大多數更迭往往只涉及代碼庫中的很小一部分,只要將重構集中在實際變更的那部分代碼上就可以了,所以在更改代碼時,大家應該把更多精力放在提高代碼質量上,而不是單純為了改而改,重構應該有明確理由,隨便選段代碼來改沒什么意思。
8 編程年限增加,開發者越要重視質量設計
InfoQ:您認為不同階段的開發者該培養哪些技能?
Steve:程序員的從業生涯肯定會經歷幾個階段,在生涯早期需要努力跟編程語言或工具作斗爭。現在的情況跟我寫《代碼大全》時已經完全不同了,那時候,程序員在學校和公司里都在鼓搗語言和工具,而現在很多程序員從青少年時就起步了,很多孩子在 10 歲甚至更小的時候就在接受系統的編程教育,所以他們早早過了前面的階段。
過了編程的早期階段,我覺得下個階段應該是提高代碼質量,特別是重視質量設計。人們意識到哪怕代碼再好,如果質量設計不足,那寫好的代碼只能廢棄掉。再往后,程序員們會進入新的階段,他們開始意識到編碼是一種團隊行為,靠個人編寫的代碼是有極限的,依靠團隊編寫出優質的代碼才是關鍵。這就回到了我之前提到的觀點,編程中的溝通不只是跟計算機溝通,更多是跟其他人溝通。所以程序員們終將意識到編程不是一切、設計也不是一切,團隊合作同樣重要。
有個觀點叫康威定律,認為軟件系統的結構反映的是構建該系統的人類組織的結構,只要能重視這一點,人們就能在組織內良好協作,這種良好的協作特性也將反映在軟件當中。我也見到過那些因為團隊內部協作不足導致軟件開發失敗的例子,這樣的團隊中的成員們只關注開發任務,成員之間不做溝通,那軟件是不可能成功的。所以我認為軟件最終能否順利交付最關鍵的部分在于人。
今日好文推薦
活動推薦
大師帶路| 入門實戰課
現在地位如同 Linux,作為 IT 人不再是學不學的問題,而是什么時候開始學習、怎么學、學到什么程度的問題。
二十年技術老兵,通過 4 大項目 + 5 講實戰視頻,給你一套由淺入深的進階學習路徑,真正實現快速上手 K8s !
原價 ¥129,新用戶限時 5 折搶,優惠就這幾天,立即點擊閱讀原文或掃碼入手
*請認真填寫需求信息,我們會在24小時內與您取得聯系。