
Andy Hunt 曾多次指出,軟體開發的兩大核心活動是學習與溝通。作者在此基礎上進一步主張:學徒期的核心主題就是學習,而成功學徒最顯著的特質就是展現出卓越的學習能力。
學徒對於用技能取代無知有著強烈的渴望。然而面對工作的複雜性與龐大的資訊量,這絕非易事。除了學習具體技能(Concrete Skills)之外,學徒還必須學會如何學習——因為即使晉升為熟練工(journeyman),學習的需求也不會消失。大師級工匠的特質之一,是願意擱置在特定領域辛苦累積的專業知識,去學習全新的事物。對於走在漫漫長路(The Long Road)上的人來說,學習是一項永無止境的活動。
本章的模式在整個職業生涯中都適用,但由於學徒期特別強調學習,因此盡早應用這些模式至關重要。Expand Your Bandwidth 是加速成長的基礎活動,它促進了其他幾個模式的實踐,例如 Breakable Toys、Use the Source 以及 Practice, Practice, Practice。接下來是較軟性的自我發現模式,從 Reflect As You Work 出發,延伸到 Record 和 Share What You Learn。而 Create Feedback Loops 與 Learn How You Fail 則是你帶入學徒期之後歲月的關鍵模式——要成為熟練工乃至大師,你必須善於建立回饋迴圈,也必須深刻了解自己的弱點。
Expand Your Bandwidth#
擴展你的頻寬
學習我們不知道的事情,往往比做我們已經知道的事情更重要。 —— Jim Highsmith
情境#
你已具備一組基本技能。
問題#
你對軟體開發的理解過於狹隘,只聚焦於日常工作中接觸到的低階細節。
解法#
你一直透過吸管穩定地飲水,但學徒期中有些時候,你必須打開消防水管,從軟體開發領域可用的大量資訊中猛烈汲取。擴展你吸收新資訊的能力是學徒的關鍵步驟——你需要發展出高效吸收、理解、記憶並應用新資訊的紀律與技巧。
這個模式不只是讀一本關於不熟悉領域的書,而是在多個維度上尋求新知識和新體驗。具體做法包括:
- 訂閱部落格聚合器,追蹤跨技術領域的 alpha geeks(這些人不一定是最好的程式設計師,但他們往往能比多數人早數年察覺新趨勢)
- 在社群媒體上追蹤軟體界的思想領袖,關注他們正在做什麼
- 訂閱中等流量的線上郵件論壇,嘗試透過重現問題來回答別人的提問
- 加入新成立的在地技術社群,主動自我介紹並提供協助
- 說服雇主送你參加技術研討會;即使無法參與,也可以閱讀投影片、收看演講錄影
- 讀完一本書後,寫信給作者表達感謝並提問——作者和業界名人通常樂於與讀者交流
- 善用線上的學術課程、Podcast 和免費影片資源
知道何時擴展頻寬與知道如何擴展同等重要。有些人可能沉迷於收集資訊而忘了實際動手寫軟體。對學徒而言,這個模式是達成目的的手段——它會加速你的學習,但也會減慢開發速度。若持續超過數個月,將出現邊際效益遞減。
行動#
在下個月內參加一場在地使用者聚會。研究一個你想參加的相關全國性研討會,開始閱讀其中一位講者的書,讀完後寫信給作者提問。
Practice, Practice, Practice#
練習、練習、再練習
我們所認識的大師並非只為了變得更好才投入技藝。事實是,他們熱愛練習——也因此他們確實變得更好。 —— George Leonard
情境#
你想在現有能力上精進,也想在新領域培養具體技能。
問題#
日常的程式設計工作不允許你透過犯錯來學習——你彷彿隨時都在舞台上表演。
解法#
撥出時間在無干擾、無壓力的環境中練習你的技藝——沒有發布日期、沒有生產事故、沒有打斷。
理想狀況下,你會採用 K. Anders Ericsson 研究中描述的刻意練習(deliberate practice):導師根據你的強弱項指定練習、用客觀指標評分、再設計更有挑戰的練習。但現實中,學徒往往必須自行達成同樣的效果。
Dave Thomas 從武術借用了 Code Kata 的概念——一種預先編排的動作序列,用來幫助學生將基本功內化。Kata 在沒有對手的情況下執行,強調流暢、力量、速度與控制。Laurent Bossavit 在巴黎更進一步建立了 Coder’s Dojo——一個定期公開表演 Code Kata 的場所。
練習的關鍵要點:
- 短回饋迴圈:如果練習時沒有獲得定期回饋,你可能只是在強化壞習慣
- 每次略有不同:練習的目的不是磨練記憶,而是發現即使是最簡單活動中的細微差異
- Practice makes permanent, not perfect:小心你練習的內容,不斷檢視確保沒有陷入陳舊
- 從經典書籍(如 Programming Pearls、Etudes for Programmers)中尋找練習題,這些書專注於演算法與資料結構的基本功
行動#
找一道略難於你輕鬆解決程度的練習題。從零開始,每週解一次,持續四週,觀察你的解法如何演進。這告訴你關於自身程式設計強弱項的什麼資訊?基於這些認知,找到或設計一道能對你的能力產生可衡量影響的新練習題,如此反覆。
Breakable Toys#
可拋棄的玩具
除非你真的熱愛一件事,否則你無法把它做好;而如果你熱愛 hacking,你就不可避免地會做自己的專案。 —— Paul Graham
情境#
經驗建立在失敗之上,程度不亞於(甚至大於)成功。
問題#
你的工作環境不允許失敗,但失敗往往是學習任何事物的最佳途徑。唯有嘗試大膽的事、從失敗中學習、再次嘗試,我們才能成長為有能力面對困難問題的人。
解法#
透過設計和建造玩具系統來為失敗預留空間——這些系統使用與工作相同的工具集,但規模小得多。
就像三球雜耍者如果從不嘗試五球就永遠無法進步,軟體開發者也需要一個安全的空間來犯錯。Breakable Toys 就是這樣的安全空間。
實踐這個模式時,讓你的系統與學徒生活相關且有用:
- 個人 Wiki:可以用來記錄所學,也能教你 HTTP、REST、解析、快取、全文搜尋、資料庫與並行處理
- 遊戲:如 Tetris、井字遊戲(用每種新學的語言寫一個遊戲是很好的練習)
- 部落格軟體、IRC 客戶端等
這些玩具專案的核心價值在於:
- 你是唯一(或最重要的)使用者,因此環境是安全的
- 你可以嘗試可能導致災難性失敗的想法和技術
- 隨著你帶著它們換工作,有些會成為你工匠精神的活見證
- 它們可能是業界標準工具的簡單重新實作,讓你更深入理解該工具的設計考量
Linus Torvalds 在 1991 年發布的那則經典 Usenet 貼文——「我正在做一個免費的作業系統(只是個嗜好,不會像 GNU 那樣大而專業)」——就是 Breakable Toys 模式最著名的案例。那個「玩具作業系統」後來成為了 Linux。
記住它們是玩具,應該是有趣的。如果不有趣,在最初的熱情消退後它們就會被擱置。
行動#
用你最喜歡的工具建造世界上最簡單的 Wiki,同時維持最高品質標準。初始版本只需要一個能查看和編輯純文字檔的簡單介面。隨著時間推移,加入更多功能,找到有趣的方式讓你的 Wiki 與其他既有實作有所不同。讓你的專業興趣引導方向——不要被現有實作束縛。
Use the Source#
善用原始碼
準備成為程式設計師的最佳方式是寫程式,以及研讀其他人寫的優秀程式。 —— Bill Gates
情境#
開源世界中,新手的問題常被一句話回答:「Use the source, Luke.」這表達了一個基本真理:程式碼是最終仲裁者。程式設計師的意圖如果與程式碼不一致,那就是無關的。唯有閱讀程式碼,才能真正理解一個系統。
問題#
沒有良好實踐的範例可供研究和效仿,Practice, Practice, Practice 只會讓你不自知的壞習慣更加根深蒂固。如果你從未穿過別人的鞋子走一英里,你可能會以為所有鞋子裡本來就該有石頭。
解法#
主動尋找並閱讀別人的程式碼。從你每天使用的應用程式和工具開始。
閱讀程式碼的實踐方法:
- 從原始碼控制系統下載當前版本,檢視其歷史並追蹤未來進展
- 檢查程式碼結構,思考為什麼檔案是這樣佈局的
- 嘗試重構程式碼,理解開發者為什麼做出那些選擇
- 當你遇到強烈不同意的決定時,問自己:開發者是否知道你不知道的事?這是否是需要重構的遺留設計?
- 加入使用 Code Review 或 Pair Programming 的團隊——這些實踐創造了安全閱讀他人程式碼的環境
工作中的程式設計師花在閱讀程式碼的時間遠多於撰寫程式碼。訓練自己在這項佔據大部分工作時間的任務上做得更好,長期而言能帶來更大回報。
透過閱讀各種好的、壞的、平庸的程式碼,你會:
- 學習特定語言社群的慣用法和細微差異
- 培養從程式碼中推斷作者意圖的能力
- 累積一個從他人程式碼中蒐集的技巧工具箱
- 能夠解決別人認為不可能的問題——因為他們缺少你的工具箱
Bill Gates 曾說:「測試程式設計能力最好的方法之一,是給程式設計師大約 30 頁程式碼,看他能多快讀懂。」能夠直接從程式碼中快速吸收知識的人,會成為更好的程式設計師——因為他們的老師是每一位程式設計師所寫的每一行程式碼。
行動#
挑選一個演算法上有深度的開源專案(例如 Subversion、Git 或 Mercurial)。瀏覽其原始碼,記下你不熟悉的演算法、資料結構和設計思路。寫一篇部落格文章描述該專案的架構,強調你學到的新想法。思考這些想法是否能應用在你的日常工作中。
Reflect As You Work#
邊做邊反思
自我審視很困難,但我相信我們能從研究失敗中學到的比從成功中更多。 —— Norm Kerth
情境#
任何合理稱職的人都會隨著時間被推上晉升階梯。如果你不採取措施為這種提升做準備,可能會突然發現自己成為 Peter Principle 的受害者——被提升到你的「不稱職層級」。
問題#
隨著年資和專案經驗增加,你發現自己在等待某個頓悟,彷彿它會神奇地讓你變得「有經驗」。
解法#
成為軟體開發的反思型實踐者(reflective practitioner)。這意味著定期內省你的工作方式:
- 你的實踐是新穎的、創新的,還是過時的?
- 對團隊習以為常的事情,提出疑問
- 如果當前工作有特別痛苦或愉快的地方,問自己它是怎麼變成這樣的,如何改善?
目標是從每次經驗中榨取最大的教育價值——將它拆解再以新的方式重組。
一個實用的技巧是 Personal Practices Map——有意識地寫下你做的事情以及它們之間的連結。透過反覆使用這個技巧,你可以看到自己實踐的變化,例如從「從不使用除錯器」到「測試驅動除錯」到「在實作複雜演算法時刻意使用不變量」。
不要把「有經驗」當作目標。經驗只表示你存活下來了,不代表你學到了多少。在某些產業中,很容易把同一年的經驗重複十次而沒有顯著進步。更糟的是,這有時會變成反經驗(anti-experience)——每多一年只是強化了你的壞習慣。你的目標應該是變得熟練(skilled)而非「有經驗」(experienced)。
行動#
為你的工作習慣畫一張 Personal Practices Map。專注於那些很久沒有改變的實踐之間的連結。問自己:如果發現其中一個實踐實際上是反效果的,你的地圖會如何改變?仔細檢視其中一個實踐,找出是否有其他方式能達到相同目標。
Record What You Learn#
記錄所學
你不應低估寫作本身的力量……寫作讓你退後一步,思考一個問題。 —— Atul Gawande
情境#
你一再學到相同的教訓,但它們似乎從來無法牢記。你記得做過類似的事情,但確切細節總是逃逸不見。
問題#
不從歷史中學習的人注定要重蹈覆轍。
解法#
用日誌、個人 Wiki 或部落格來記錄你的旅程。這份按時間排列的學習記錄不僅能為你指導的人提供靈感,也是你自己可以倚賴的重要資源。
使用這個模式的人遲早會經歷一個時刻:搜尋某個棘手問題的答案時,搜尋引擎返回的連結指向自己的 Wiki 或部落格。
記錄的實踐要點:
- 你的筆記本、部落格或 Wiki 應該是苗圃而非墓地——教訓應從中萌生,而非在此消亡
- 定期回顧你寫過的東西,每次嘗試建立新的連結
- 這種創造性回顧可以讓你基於新資料重新評估舊決定,或強化正在動搖的信念
- 同時維護公開記錄(分享教訓、獲得更廣泛社群回饋)和私人記錄(對自己的進展保持痛苦的誠實)
- 記錄工具的選擇本身也可以是一個重要的 Breakable Toy
行動#
拿一本紙質筆記本,開始記下你對這本書的想法和它激發的想法。確保筆記有日期。讀完這本書後,繼續用同樣的方式記錄你學到的其他東西。隨著時間推移,這些條目可能成為部落格文章、雜誌專欄甚至書籍的基礎。
Share What You Learn#
分享所學
我無法過度強調慷慨的精神對好運的貢獻。 —— Twyla Tharp
情境#
你當了一段時間的學徒,已經知道一些東西,人們開始視你為知識來源。
問題#
到目前為止,你只專注於自身作為工匠的提升。要成為熟練工,你需要具備有效溝通並快速帶領他人上手的能力。
解法#
在學徒期早期,養成定期分享所學的習慣。形式可以是維護部落格、舉辦午餐學習會(brown bag session)、在研討會上做簡報,或為你正在學的技術撰寫教學文章。
你可能覺得自己還不夠格分享——畢竟你既非大師也非熟練工。但你的同儕學徒會感激有人嘗試將複雜主題去神秘化。正因為你只知道一點點,你的解釋會是簡單、直接、不假設先備知識的——這反而讓它們成為更好的解釋。
分享時需注意的事項:
- 教學是強大的學習工具——教者可能比學者學得更多,所謂「一人教,兩人學」
- 分享前考慮該知識是否屬於你可以分享的——它可能是商業機密或可能傷害他人
- 透過 Sweep the Floor 獲得的成果,可能因分享時不夠謙遜而被毀掉
- 不要只顧著吸收知識(Be the Worst 模式),也要考慮那些能從你的分享中受益的人
Dave Smith 的模式 Prepare the Way 與此有很強的連結:作為開拓者,我們有額外的責任,在我們闖入荒野時留下一條標記清楚的安全路徑。
行動#
回想你最近學到的重要教訓,寫一篇部落格文章。提供你希望當初學習時就有的資訊。寫完後,想像你被要求準備一個會議工作坊來教別人同樣的課程。草擬工作坊的內容,看看思考如何教學是否讓你重新審視了那個教訓和那篇部落格文章。
Create Feedback Loops#
建立回饋迴圈
我們在軟體業處理的是一個幾乎看不見的產品,而這種隱形性只會加劇我們對回饋的需求。 —— Jerry Weinberg
情境#
你無法判斷自己是否處於無意識的無能(unconscious incompetence)狀態——正如 Kruger 和 Dunning 在研究中指出的,能力不足的人往往不自覺。而且,你的能力越低,你越難以評估自己和他人的能力。成功或失敗往往以突如其來的驚訝形式出現。
問題#
你的自我評估只是相對於你過去的能力,缺乏客觀性。團隊的水準很容易扭曲你對自身能力的認知——在高於平均的團隊中,你可能覺得自己是超級巨星(實際上只是伴唱),或者自信心被摧毀;在低於平均的團隊中,你可能變得自滿。
解法#
建立機制,定期收集關於你表現的外部客觀資料。透過及早、經常且有效地尋求回饋,你至少能意識到自己的不足。
獲取回饋的多種機制:
- 技術層面:測試驅動開發、互動式直譯器——讓程式快速失敗(fail fast)
- 同儕層面:Code Review、Pair Programming
- 評估層面:考試和認證(但這些通常只測試應試技巧)
- 直接詢問:聯繫面試過你的人,詢問他們對你的看法——即使沒有得到工作,也能從被拒的確切原因中獲益
收集到回饋後,你需要能夠處理原始資料。有用的回饋是可以採取行動的資料,讓你有選擇去做更多或更少某種行為。注意區分強化回饋(鼓勵你做更多)和平衡回饋(鼓勵你做更少)——兩者結合能讓系統保持穩定。
無用回饋的例子:
- 純粹的批評(沒有告訴你期望是什麼)
- 關於對方而非關於你的回饋
- 偽裝成建議的投射
- Stop energy——好意勸你放棄目標而非冒失敗風險的建議
Patrick Kua 的經驗教訓:「最困難的事情是,很少有人願意告訴你你正在犯錯。所以一半的戰鬥在於找到一個會盡快告訴你的人。」學徒不應該把重點放在避免犯錯,而應放在學會辨識自己的錯誤——一旦能辨識,從中學習就容易多了。
行動#
在你的工作環境中找到一個你可以測量且能夠影響的指標。追蹤它一段時間,當它變化時,問自己它告訴了你什麼。看看你是否能利用它(和其他指標)來理解你對工作環境所做改變的效果。
Learn How You Fail#
了解自己如何失敗
巧思常被誤解。它不是超群智力的問題,而是性格的問題。它首先要求的是願意承認失敗、不掩飾裂縫、並做出改變。 —— Atul Gawande
情境#
失敗不可避免。它遲早會發生在每個人身上。事實上,一個從未失敗過的人,不是迴避了挑戰自身能力的邊界,就是學會了忽視自己的錯誤。
問題#
你的學習技能增強了你的成功,但你的失敗和弱點依然存在。
解法#
辨識你傾向於失敗的方式,並嘗試解決那些值得修正的問題。這不是沉溺於過去錯誤的自憐,也不是追求完美的練習。目標是獲得關於導致你失敗的模式、條件、習慣和行為的自我認知。
有了這種自我認知,你可以:
- 做出有意識的選擇
- 在應用 Draw Your Own Map 時,用對自身邊界和限制的認知來調節理想主義傾向
- 選擇修正問題或認賠停損——接受有些事情你就是不擅長,或需要不成比例的投入才能獲得微小進步
- 設定現實的目標限制——你無法在每件事上都出類拔萃
Ade 在私人 Wiki 上維護一組頁面,列出他當前的技能和限制。這讓他能夠選擇哪些邊界要向外推(例如嘗試用動態型別語言維護大型程式碼庫),以及哪些地方該停止浪費精力(例如接受 Commodore 64 的 6502 組合語言不太可能再次復興)。
行動#
用你選擇的程式語言,使用簡單的文字編輯器(不要用 IDE)寫一個二分搜尋(binary search)的實作。先不要編譯或執行。接著寫你認為需要的所有測試,記下在這個階段發現的 bug 和問題。然後修正所有問題,反覆迭代直到你確信程式碼和測試都完美。最後才編譯和執行測試。大多數人會發現之前沒想到的邊界案例和瑣碎錯誤。在你修正這些錯誤之前,試著理解它們怎麼會出現在你確信完美的東西裡。記下你從中學到的東西。
Wrapping Up#
本章總結
終身學習可以被視為祝福或詛咒。學習新事物可能是痛苦的,尤其是在壓力下且缺乏指導時。然而,就像運動員必須忍受劇烈訓練後的肌肉酸痛,軟體開發者也要忍受學習新事物帶來的認知失調(mental dissonance)。這種失調可以成為進步的 welcome sign。
自我反思、透過回饋迴圈識別失敗、了解自己的弱點——這些表面上看起來都是負面的,但它們正在幫助你減少無知。另一條路是只專注於你已經知道的東西,但這不是通往精通軟體工藝的道路,而是通往單一專業的窄路。
Phillip Armour 在〈The Five Orders of Ignorance〉中指出:「軟體不是產品,它是儲存知識的媒介。因此,軟體開發不是生產產品的活動,而是獲取知識的活動。知識只是無知的另一面,因此軟體開發是一種減少無知的活動。」
