許多開發者常陷入「完美開端」的陷阱,執著於前期設計,導致專案停滯不前。然而,技術日新月異,需求不斷變化,預先構建完美架構的效益有限。實際上,及早產出可運作的原型,透過持續迭代和重構,更能適應變化,創造商業價值。本文將探討如何擺脫完美主義的束縛,擁抱迭代開發的優勢。
打破完美主義:告別「完美開端」的開發迷思
軟體開發者,不論資歷深淺,都曾經歷過這種掙扎:滿懷憧憬地開啟新專案,腦中構思著完美的架構,但幾個小時過去,螢幕上卻只有零星幾個資料夾、幾個佔位符和一個 README.md
檔案。於是開始翻閱檔案尋找「正確」的專案結構,或參考其他人的作法。
團隊合作時更是如此:會議上無止盡地討論架構、抽象概念和框架,點子像流星般劃過,大家都同意「一切必須完美」,但實際工作卻停滯不前。因為你和團隊成員潛意識裡都在等待著:完美的開端。
「完美開端症候群」並非虛構,而是阻礙高效開發的絆腳石。本文將探討為何等待「完美」會拖慢進度,這種思維模式如何阻礙發展,以及如何克服它。
為什麼我們渴望完美的開端?
坦白說,工程思維讓大多數程式設計師追求「乾淨」和「永恆」。專案必須建立在穩固的基礎上,才能持續發展多年。但這種對「完美無瑕」的渴望,常常演變成一種有害的習慣,那就是在找到**「完美的解決方案」**之前,不斷拖延實際行動。
問題是,完美通常是無法達成的。原因如下:
- **世界變化速度快於編寫程式碼的速度。**當你還在分析新的框架或模式時,新版本可能已經釋出,環境也已改變,專案需求甚至可能從網頁應用程式轉變為行動應用程式。
- **我們無法預料一切。**專案初期,似乎需要立即做出數百個決策:是否採用微服務?是否使用 monorepo?抽象層應該如何設計?但大多數情況下,這只會導致癱瘓。當你猶豫不決時,實際工作就會減慢速度。
- **害怕「弄髒」程式碼函式庫。**對乾淨程式碼的堅持有時會造成阻礙。我們過於執著於程式碼從一開始就必須清晰、經過測試且風格一致,以至於害怕編寫看起來「不完美」的程式碼。
為什麼停止等待完美的開端至關重要?
在程式設計和生活中,最重要的就是行動。快速且逐步地進行微小的改進,遠比花費數天時間去構建理論上完美的架構或符合理念的架構更有效率。
以下是一些原因:
- **隨著專案成長,決策總會改變。**在規劃階段,你甚至無法想像日後會遇到哪些實際挑戰。專案決定架構,而非反過來。
- **可運作的原型比抽象概念更重要。**大多數情況下,做出一些可運作的東西,即使遠非完美,也是成功的一半。實際程式碼不僅能推動進展,還能提供回饋。
- **過程就是經驗,日後能改善系統。**即使最初的程式碼很「糟糕」,路徑已經鋪設完成。而重構(沒錯,開發者的最愛)是任何開發者的忠實夥伴。
- **商業價值。**要記住,「完美的程式碼」賺不了錢。客戶、產品或使用者重視的是其功能性。程式碼總是可以日後改進,但時間卻無法挽回。
一個追求「完美開端」的案例
幾年前,我親眼見證了這種對「從一開始就正確的架構」的渴望。當時我們開發一個內部使用的應用程式:一個大型資料分析儀錶板,包含互動式表格、圖表以及與多個 API 的整合。任務很具野心,但一開始進展很快:選擇了設計系統,討論了框架,甚至在幾天內就搭建了基本的結構。
接下來就一發不可收拾了…團隊厭倦了維護「舊」專案,深信舊專案之所以不好是因為:
- 它很舊,而且是遺留系統
- 商業方面不允許花時間重構,所以一切都很糟糕
於是他們開始追求「炫技」,力求現代化等等。
似乎任何問題的解決方案都必須一開始就達到最高水平:例如,每次與 API 的互動都必須嚴格型別化、通用化,並且根據最佳實務設定錯誤處理程式。他們開始構建一個自定義的機制來與所有服務互動,以防萬一他們的 API 發生變化;他們設計了令人髮指的角色模型和存取許可權邏輯……結果如何?
一個月後,除了大量的抽象概念、數十個介面和基本的日誌記錄外,我們什麼有用的東西也沒有。就像那個老掉牙的梗:
我們有 35 行程式碼、2 個迴圈、2 個 try/catch 結構用於安全、4 個 if/else 條件和大量縮排和權宜之計。這並不是解決這個任務所需的必要儲備。但如果你開始編寫垃圾程式碼,就很難停下來。唯一讓我擔憂的是遞迴。沒有什麼比遞迴更令人困惑的了。我知道遲早我們也會用到這種爛東西。
最後,我們不得不放棄一切,從頭開始,盡可能簡化方法。我們專注於基本情境:是否需要立即處理通用的 API 模組?不需要。為每個服務建立普通的函式。第一天是否需要通用的抽象機制來處理來自前端的資料?不需要。等等等等。per rectum ad astra,正如俄羅斯鄉村所言。
我們很快建立了一個可運作的原型,可以向業務展示。你們知道嗎?許多我們一開始很想實作的功能,根本不需要。如果我們一開始就做了,那將是浪費時間。
完美開端的悖論
追求完美開端的有趣之處在於,在現實生活中,它永遠不會到來。以下是幾個原因:
- **完美的架構只有從實踐中才能誕生。**當專案只存在於腦海中時,你沒有完整的畫面,只有尚未經過實踐驗證的假設。實際的商業或使用者需求會打破你的計劃。
- **框架和技術的更新速度快於你的開發速度。**不要指望去年選擇的技術在一年後還能幫上大忙。
- **經過第一個混亂階段後,一切都會變得更清晰。**當專案以最小化版本運作時,你可以清楚地看到真正需要改進的地方。
如何停止等待完美的開端?
1. 分解任務——盡可能細分
當你面對一個空專案和「創造偉大成果」的宏偉目標時,這個目標會讓你感到壓力。解決方案是將任務盡可能細分,直到它們變得非常明顯為止。
範例: 不要設定「設定 API 架構」這樣的大目標,試著從「建立一個輸出 Hello World 的基本 Express 伺服器」開始。是的,這聽起來太簡單了。但一個小時後,你將擁有一些可以繼續發展的東西。
2. 不要同時做太多決策
如果你正在開發一個新專案,不必一開始就決定它「今天」就必須具備:
- 清晰的目錄結構;
- 「完美」的 Redux/Signals/Vuex 儲存區;
- 自定義的 CI/CD 管線;
- 100% 的測試覆寫率。
將這些任務留待以後,專注於最小的可運作版本。
3. 允許初期階段存在混亂
將專案構建過程轉變為一系列迭代。事實上,一開始根本不存在完美的程式碼,因為專案本身正處於不確定性階段。
在工作中,不要將編寫「糟糕」的程式碼或使用臨時解決方案視為問題。重要的是始終牢記這條鏈:
- 編寫可運作的程式碼。
- 改進現有程式碼函式庫中的某些內容。
- 重複。
如果你定期進行重構,那麼重構並不可怕。
4. 設定依賴於行動而非結果的目標
不要告訴自己:「今天我將建立後端的模組化結構」,最好寫下:「今天我將建立具有最小耦合性的驗證功能」。這種方法有助於你專注於過程,並減少對結果尚未「宏偉」的感覺的困擾。
5. 學會放手
放棄第一個解決方案總是最終解決方案的幻想。事實並非如此。程式碼意味著它是在編寫,而不是刻在石頭上。如果你選擇了一種策略,但後來發現它效率低下,那麼沒有什麼可以阻止你改變它。不要害怕留下一些未完成的工作。重要的是你的前進能力。在這個行業中,生存下來的不是那些第一次就能創造完美的人,而是那些釋出可運作的專案並準備在每次更新時改進它們的人。
重構不需要等待特殊時機
這裡需要強調另一點——持續重構的重要性。我們經常在思想上將開發與重構區分開來。例如,存在一個「我們快速編寫 MVP 的階段」,然後是「我們回頭清理一切的階段」。然而,這種方法從根本上是錯誤的:除非專案已經像蠟燭一樣燃燒殆盡,否則業務不會為「重構階段」提供資金。
坦白說,我再也不會區分這些東西了。重構不是專案的單獨階段,而是開發過程的一部分。這是你每天,每次提交都做的事情,目的是讓程式碼比昨天好一點。
多年前,我參與了一個專案,它一開始是糟糕開發的典型例子。程式碼函式庫很混亂:大量的「熱修補程式」、「重複」、「缺乏測試」以及目錄結構的完全混亂。這裡根本沒有「架構」的概念,大量的邏輯分散在整個專案中。儘管產品完成了它的任務,但使用它非常困難,以至於即使對於經驗豐富的開發人員來說,每個新功能都變成了一場噩夢。
該專案已經產生了一些利潤,但任何新增功能或錯誤修復都需要比最初估計的時間多 2-3 倍。公司面臨著一個選擇:完全重寫專案(可能需要長達一年時間),或者在不停止業務的情況下逐步改進它。
首先,團隊達成了一項務實的方法:「每次提交都必須讓程式碼比之前更好」。這並不意味著我們會立即重寫所有內容。該計劃包括以下步驟:
- 在新增新功能時,重構周圍的程式碼。例如:如果在過時的類別中新增了一個方法,那麼這個類別將被調整到更整潔的狀態,而且我們會盡力根據最佳實務來實作該方法。
- 逐步引入測試。每個新增或修改的程式碼塊都將被測試覆寫,以最大限度地減少出現故障的風險。
- 在過程中寫下小的任務。例如,如果我們發現一個複雜且難以閱讀的方法,我們很少立即重新編寫它,但我們會建立一個「最佳化 X」的票證。
- 將程式碼樣式統一。甚至匯入順序和格式都變得很重要。
經過一年的逐步改進後,該專案透過了外部稽核,因為公司吸引了一家大型客戶。外部稽核員指出,儘管內部仍然存在舊的糟糕程式碼的遺留問題,但目前的結構和維護工作已經符合標準。最重要的是,業務看到了差異——新增功能需要更少的時間,而且成本也降低了很多。因此,該產品得以發展到產生顯著收入的水平。
這個案例向我和團隊證明,逐步改進是一種真正有效的策略。如果願意並有紀律地逐步改進,則無需從頭開始重寫專案。關鍵是系統性和改進的意願。
沒有人記得是怎麼開始的,但每個人都會看到結果
當專案完成(或至少可以運作)時,沒有人會在意一開始有多麼混亂。使用者將使用它,客戶將簽署合約,你的團隊主管將表揚你(或不表揚你——但專案已經運作)。
讓我們回想一下大多數成功的產品是如何創造出來的。Facebook 起源於大學的區域網路。Amazon 起源於一家書店。而根據 Python 建立者 Guido van Rossum 的回憶,這個流行語言的第一個程式碼遠不如你的學生專案。
如果世界上最著名的公司都是從「勉強」運作的功能開始的,那麼我們為什麼有信心認為我們的程式碼第一次就必須完美?