我打造大型技術專案的方法
原文由 Mitchell Hashimoto 于 發布,訂閱此部落格
無論是從零開始打造一個新專案、實作一個大型功能,還是著手進行大規模重構,要保持動力並完成大型技術專案都不是件容易的事。對我來說非常有效的一個方法,就是持續看到實質的成果,並以此為依據來安排工作的順序。
我們都體驗過剛開始一個新專案時那種興奮感。前幾週總是迫不及待想打開電腦工作。然後隨著時間慢慢過去,你開始分心、找藉口,投入的時間越來越少。如果這是正職工作,你只能硬著頭皮痛苦地把它拖到終點,每天都很煎熬。如果只是為了興趣,幾年後回頭看,只會想起那個本來可能實現的成果。
我學到的是,當我把大型任務拆成一塊塊、每一塊都能讓我看到具體進展時,我往往更能完成工作,也能在整個專案過程中保持熱情。每個人被激勵、被驅動的方式都不同,所以這套方法不一定適合你,但就我大致的觀察,我還沒遇過不會被一場精彩 demo 打動的工程師。而目標就是不斷給自己一場精彩的 demo。
我並不是要說這篇文章裡的任何東西是原創的。它確實與許多知名的軟體工程或管理實踐有重疊之處。我只是想分享我處理較大型技術工作的方式,以及我為什麼會這樣做。
在這篇文章中,我會以我的終端機模擬器專案作為例子,這樣才能分享真實、具體的經驗。其實還有很多其他專案可以用,但我選這個,因為它跟我的正職工作無關,而且夠新,記憶猶新。
我想非常明確地說,我並不是要指責任何沒有完成專案的人。只要你樂在其中、覺得有成就感(或是根本不在乎有沒有完成),那就很好,為你高興、也為你加油。這篇文章是寫給那些更想把專案做完的人,或是單純想了解我是如何努力把專案做完的人。
起點
一開始,你有一個大型專案,必須想清楚該從哪裡開始。對我來說,這是最困難的部分,我可能會花上好幾個小時——有時甚至好幾天——猶豫不決,糾結於正確的起點。
以我的終端機模擬器為例,有許多大型元件是我知道若想完成這個專案就必須存在的:終端機解析、執行與管理 shell 行程、字型渲染、網格渲染、輸入處理(鍵盤/滑鼠)等等。通往「完成」的路上,有數百個相對龐大的子專案。
如果我一開始的目標就是做出一個能跑 Neovim、可啟動的終端機,那我可就麻煩大了。就算撇開未知的未知不談,這個目標光聽起來就太大了。我可以直覺地意識到,這條路上需要很多元件:渲染 GUI、啟動行程、終端機解析與狀態管理。這是個糟糕的目標,太大,我很可能做個一兩個月就失去興趣。
相反地,我會去想,什麼樣的專案是務實的、而且能讓我盡快看到成果。一旦套用這個篩選條件,可行的子專案數量就會大幅減少。舉幾個例子:
- VT 解析 - 解析終端機的逸出序列
- 空白視窗渲染 - 開啟一個視窗並繪製一塊空白畫布
- 子行程啟動 - 啟動一個子 shell,例如 bash、zsh、fish,設定好 TTY 並能讀取它的輸出(也就是最初的 shell 提示字元)
在這個階段,我不會試圖列出所有大型子專案。我只是大致掌握專案未來的粗略輪廓,然後找一個可以獨立實作、同時又能實際看到某種具體成果的項目來做。
這是經驗最能派上用場的階段。經驗較豐富的工程師通常更能有效地描繪出專案的粗略輪廓。他們能更準確地辨識出各種子元件,並看出這些部分如何組合在一起。經驗較少,或是在我不熟悉的領域,我就只能盡力猜測,並預期之後把做好的東西丟掉重來的可能性會更高。
早期的成果
早期的工作往往不太可見,這讓人覺得很難看到具體的成果。舉例來說,如果我選擇先做終端機的 VT 解析,沒有接上某種 UI,我就無法看到它運作。或者換成別的專案,如果我選擇先做資料庫結構與最小可行 API,同樣得寫一個客戶端加上 CLI 或 GUI,才能看到成果。
如果你一開始選擇的子專案是 UI,當然就能很快看到成果!基於各種原因,我很少先從前端開始,通常都是先做後端。而且無論如何,你終究會做到後端,並遇到類似的挑戰。
要度過這個階段,最好的工具就是自動化測試(在這個階段通常是單元測試)。自動化測試讓你實際執行一些程式碼、看到它是可運作的,同時也有助於維持良好的開發習慣。
這也為你挑選最初幾個任務提供了另一個指引:如果不是圖形相關的工作,就挑一個不需要太多麻煩就能測試的項目,這樣你才能看到成果。
以我的終端機來說,我決定先從 VT 解析開始,因為在當時那是我對終端機比較不了解的部分,而且感覺非常容易測試:給它一些字串作為範例輸入,預期它會輸出某個解析後的動作或事件。
看到「1 項測試通過」、「4 項測試通過」、「13 項測試通過」這樣逐步進展,對我來說非常令人興奮。我正在執行自己寫的程式碼,而且它是可運作的。同時我也知道,自己正在一個大型專案的關鍵子元件上取得進展。
衝刺到 Demo
我在早期子專案的目標,不是打造一個完成的子元件,而是打造一個夠好的子元件,讓我能繼續往demo 路上的下一個任務前進。✨
這種取捨不僅體現在功能上,也可能體現在演算法或設計考量上。舉例來說,你可能知道未來需要用到真正的資料庫、精巧的資料結構,或是支援串流資料。但在最初的工作階段,你大可先用記憶體內的資料、字典這類內建資料結構,並要求所有輸入/輸出一次到位就好。
我認為這是個重要的取捨,所以我要再說一次:別讓完美成為進步的敵人。更進一步說,別讓那些你知道未來勢必得做的改進,阻礙你前進到下一個任務。目標是做出 demo。
無論我在做什麼,我都會試著每週做出一到兩個 demo,並穿插前一節提到的自動化測試回饋。
打造 demo 也能為你提供無比珍貴的產品回饋。即使還沒完全可用,你也能很快直覺地感受到某個東西用起來感覺好不好。這些還稱不上「最小可行產品」,因為它們其實還無法真正使用,但已經足以讓工程師進行有價值的自我檢視。
這是一個我認為經驗反而會造成阻礙的領域。我看過資深工程師執著於打造完美的東西,等到終於做出 demo 時,才發現這東西很爛。爛的不是實作,而是產品或功能本身就很爛。
回想一下,我為終端機挑的第一個任務是 VT 解析。在早期階段,我只看到自動化測試在運作。為了做出第一個 demo,我寫了一個 shell 腳本,它會執行某個指令、擷取其輸出、餵給我的 VT 解析器,然後輸出所有解析出來(或無法解析)的內容。隨著時間,我不斷迭代這個 CLI,把它當作我的第一個「UI」——我會用 ASCII 來渲染終端機網格。
這帶給我巨大的滿足感,因為我可以執行像 man 或 ls 這樣簡單的程式,或是像 vim 這樣更複雜的程式,然後看到我的解析器運作(或是壞掉,這本身也同樣令人興奮)。
在這個情境中,我寫的那個 CLI 長期來看幾乎沒什麼用(我很快就把它丟掉了)。但花上一兩天把它當作 demo 做出來,卻帶給我一種重要的進展感,而親眼看到某個東西運作,也幫助我保持動力。
為自己而做
這一節更適用於個人專案,而非公司指派的工作專案。即使你期望最終要把軟體發布給別人使用,也要只做你當下需要的東西、需要時才做,並且盡快開始使用自己的軟體。
當我在處理自己親身遇到的問題時,總是更有動力1。而且,如果一個為你設計的產品連你自己都覺得不好用,那它很可能對別人來說也不好用。因此,我從 demo 邁向真正可在實際環境中使用的產品的路徑,就是找出只打造我認為自己需要的功能的最短路徑。
以我的終端機來說,那意味著首先要能載入我的 shell 設定(fish),然後能夠啟動並使用 Neovim。所以我讓所有工作都直奔只為達成這件事所需的功能:只處理那些程式會用到的逸出序列、只渲染我每天使用的字型,等等。一開始被我省略的功能範例包括:捲動、滑鼠選取、搜尋、分頁/分割視窗等。
接著我開始把自己的終端機當作日常主力工具來用。這個步驟通常會經歷幾次失敗的嘗試;你會發現自己其實需要某個被省略或遺忘的功能。在我最初幾次使用自己的終端機時,我發現方向鍵完全沒反應,還有一些細微(但會中斷工作流程)的渲染錯誤等等。於是我又暫時放棄使用它,但這也為我提供了下一個具體要做的任務。
此外,使用自己寫的程式碼做出來的軟體,總是讓我感到非常自豪,這通常也能幫助我保持繼續做下去的動力。
總結
將大問題拆解成小問題。重要的是,每個小問題都必須有某種能讓你清楚看到工作成果的方式。
每個小問題只需解決到足以推進大問題中面向 demo 的部分,然後就前進到下一個小問題。
只需解決足夠多的小問題,讓你能開始打造可執行的軟體 demo,然後再持續迭代更多功能。盡可能頻繁地做出 demo。
優先實作能讓你自己開始使用這套軟體的功能,如果適用的話(例如個人專案,或是解決你實際遇到的問題的工作專案等)。然後持續優先解決你自己的問題。
視未來改進的需要,回頭迭代各個元件,並依需要重複這個過程。
結語
大致上就是這樣了。我在個人專案、團隊專案、工作專案、學校專案等各種情境下都遵循這個大致的模式,這就是我保持動力的方式2。
請注意,我有很多東西都沒提到!我沒有談發布。我知道很多人覺得發布很有激勵作用。我認為一個專案不一定需要發布才算成功。而且對我來說,發布是個太大的事件,無法長期激勵我。我也沒有談工具(Git 工作流程、CI 等)。我在多份工作中都使用這套流程,並且讓它去配合既有的各種流程。諸如此類。
我認為這有助於說明這在多大程度上是一個個人化的流程。我想每個人都需要找到某種能以健康方式鞏固自身動力的方法。我發現看到成果能非常強烈地激勵我,我圍繞著這點建立了我的工作風格,到目前為止效果都很好。
註解
隨機一篇部落格
留言
登入後參與討論