Reliability, Scalability, Maintainability:重新理解 Go 開發的三個維度
從 Go 說起
Go 是我第一個認真鑽研的語言。起初,我覺得許多設計理所當然:沒有繼承、錯誤要一個個處理、Interface 不需要顯式宣告。
直到後來接觸了其他語言,回頭再讀 Rob Pike 在 2012 年寫的 "Less is exponentially more",我才發現這背後有一套極其嚴密的工程邏輯
這些設計並非功能缺失,而是設計者刻意的留白與偏執的限制
核心假設是:一個具備高度可維護性的系統,其生命週期與價值將遠超一個功能強大但難以更動的系統。 很多人會問:這種語法上的克制和節制,如何轉化為生產環境中的抗風險能力?這就要從 RSM 框架說起
重新丈量系統:從生命週期的角度審視
在《Designing Data-Intensive Applications》(DDIA)中,Martin Kleppmann 提出了 RSM 框架。我傾向從系統生命週期的維度來重新定義這三個指標:
Reliability(可靠性):對「當下」的驗收
系統此刻能否正常運作?
這是最基本的工程要求。它關乎系統在面對網路波動、硬體故障或無效輸入時,是否還能維持預期功能。如果系統無法在「當下」生存,任何長遠規劃都缺乏基礎
Scalability(擴展性):對「成長」的預留
當負載增加十倍,系統能否藉由資源投入來維持性能?
隨著業務增長,壓力會從單點故障轉向資源瓶頸。Scalability 考驗的是架構的彈性:當負載增加時,我們是否有除了「重寫系統」以外的技術手段
Maintainability(可維護性):對「持續演進」的保障
三個月後,這段程式碼是資產還是債務?
大多數系統並非死於流量衝擊,而是死於內部邏輯的混亂與日益增長的認知負擔。維護性決定了系統是能持續演進的,還是只能維持現狀、等待重構的
視角反轉
在開發初期,我們常誤以為 R、S、M 是三個可以獨立調整的旋鈕。為了趕進度,我們傾向調低 Maintainability,將資源專注於實現功能(Reliability)與支撐流量(Scalability)
但當系統進入長期維護階段,這筆「技術債」的成本才真正顯現。
事實上,這三個維度並非平行關係,而是一個連動的系統:
缺乏 M 的可靠性不可持續: 當程式碼複雜到無人敢動,修復 Bug 就像在黑盒子裡盲操,任何改動都可能引發不可預知的副作用
缺乏 M 的擴展性只是重寫: 如果模組耦合過深,當需要拆分服務以應對流量時,你會發現架構僵化到無法拆解。這時你做的其實是重寫,不是擴展
Maintainability 是金字塔的最底層。 地基不穩,上面的 R 和 S 蓋得越高,系統崩塌時的代價就越慘烈
Go 語言的解答:以簡約支撐複雜
理解了這個框架,Go 的設計決策就顯得非常合理:它透過限制開發者,換取極致的維護性,進而支撐起長期的 R 與 S
顯式的代價換取 Reliability: Go 強迫開發者處理每一個 if err != nil。這種「顯式的繁瑣」確保了錯誤路徑的透明度,防止異常狀態在系統中靜默傳遞,這是穩定性的前提
正交的設計成就 Scalability: Goroutines 與 Channels 的設計實現了併發模型與業務邏輯的解耦。這種正交性讓開發者能以較低的成本實施並行處理,應對規模化需求
發現而非設計守護 Maintainability: 隱式接口(Implicit Interfaces)鼓勵在開發過程中「發現」抽象而非預先設計。這讓系統保持了極高的重構彈性,避免了過度設計導致的架構僵化
Go 限制了複雜度的上限,確保團隊能以最低的認知負擔接手任何一段程式碼
案例:即使選用了 Go,忽視架構邊界依然會出現問題
Segment:微服務架構的指標性實踐者
他們曾為了追求擴展性將系統切成上百個微服務,最終卻發現維護成本(Maintainability)高到難以負荷:改動一個共通欄位需要協調數十個 Repository 的 PR。這是一個典型的「擴展性紅利被維護成本吞噬」的案例,最終他們選擇回歸單體架構以降低複雜度
Uber:兩階段的架構進化
Uber 第一階段選擇 Go 是為了語言標準化與開發效率;但當服務規模突破臨界點,混亂的依賴關係依然導致維護崩潰。第二階段的 DOMA 架構轉型則證明了:Go 能提供好讀的程式碼,但只有明確的領域邊界(Domain Boundary)能保證系統的好改與好延展
結語:選擇願意承擔的代價
技術選型選的是哪種代價對系統長遠有利,不是哪門語言最強
身為 Go 工程師,選擇了顯式的錯誤處理與簡約的接口。
這並非出於對功能的妥協,而是知道守住維護性,Reliability 與 Scalability 才具備真實的意義