「我們要換 microservices。」
你的 tech lead 說了這句話。你在想:這個決定是對的嗎?什麼時候換才值得?還是說我們根本還沒到需要換的規模?
這個問題有一個比「microservices 好不好」更根本的問法:microservices 在解什麼問題,而那個問題我們現在有嗎?
CGI → PHP → Rails → Node.js → Go,這條路不是隨機的,是一個可以解釋的鏈——每一代都在解前一代具體撞牆的問題。讀完前六篇,你可能已經注意到這個模式。這篇的目標是把這個模式說清楚,讓它在你遇到任何技術選型時都可以直接用。
驅動公式
技術演進的驅動力,幾乎都可以歸結成四個來源:
1. 規模壓力(Scale Pressure)
舊方案在小規模下完全夠用,但當請求量、資料量、開發者人數增加,舊方案某個假設不成立了。
CGI 假設「每個請求的行程啟動成本可以接受」——在 100 個請求/天成立,在 100 萬個請求/天不成立。動態型別假設「你記得每個函式的輸入型別」——在 5 個檔案成立,在 500 個檔案不成立。
2. 硬體能力的變化(Hardware Capability Shift)
新的硬體能力讓舊方案的某個取捨不再成立,或讓新方案的某個成本下降。
多核 CPU 的普及讓「並行」從稀缺資源變成標準配備——Go 的 goroutine 能真正利用多核,node.js 的 event loop 卻只能在單核上跑。SSD 的普及讓 I/O latency 下降兩個數量級——某些原本要用 event loop 避免 I/O 阻塞的場景,現在 I/O 快到阻塞幾十 μs 根本不是問題。
3. 安全與可靠性事故(Security/Reliability Incident)
一個夠大的事故,會直接推動語言或工具設計改變。
PHP 的 register_globals(使得任何 GET/POST 參數都自動變成全局變數)在 2003 年被禁用,因為它製造了大量的安全漏洞。C/C++ 的緩衝區溢位問題推動了 Rust 的 borrow checker。Windows 的 XP 時代 DLL hell 推動了 .NET 的 assembly isolation。
4. 開發者體驗的臨界點(DX Breaking Point)
有時候推動演進的不是效能問題,而是「寫起來太痛了,必須有更好的方式」。
Callback hell 不是效能問題——你的 Node.js 服務在 callback 裡跑得很好,但沒有人看得懂這段 code。async/await 解決的是開發者體驗,不是系統效能。Java 的 boilerplate 不是效能問題——但沒有人喜歡花三小時配置 XML 才能跑一個 hello world。
為什麼解方總是製造新極限
每一個解方,都帶著自己的假設。當使用場景超出這些假設,新的極限就出現了。
Event loop 的假設:「工作是 I/O 密集的,CPU 密集的工作是少數。」在 2009 年的 web API 場景,這個假設成立。在 2020 年的 AI 推論場景,這個假設完全不成立——你的 API handler 呼叫一個需要 2 秒的模型推論,event loop 被卡死。
Go goroutine 的假設:「共享記憶體的並行,可以靠 channel 和 sync primitive 管理。」對大多數後端服務,這個假設夠用。對分散式系統,共享記憶體根本不存在——你需要 Actor 或 CRDT。
Microservices 的假設:「獨立部署、獨立擴展的代價,比一個 monolith 的耦合代價低。」在大型 team、高流量場景成立。在小 team、早期產品,microservices 的 overhead(服務間通訊、分散式 tracing、各服務獨立 schema 管理)直接拖垮開發速度。
這不是說這些技術是錯的。是說每個技術都有它的有效範圍,超出那個範圍,你就在用一個為不同假設設計的工具。
用這個框架看當前的技術
現在有幾個技術正在「被解方」的狀態,可以用這個框架思考它們會在哪裡撞牆:
LLM as code generator:「AI 生成的 code 可以被人類快速 review 和驗證。」當 AI 能生成整個模組,不只是函式,這個假設開始不成立——你要 review 的 code 比你自己寫的還多。
Container + Kubernetes:「workload 可以被抽象成無狀態的、可替換的 pod。」有狀態的服務(資料庫、需要本地快取的 ML 模型)在 K8s 上永遠是二等公民。
GraphQL:「讓前端自由組合它需要的資料,比後端預先定義 endpoint 更靈活。」假設成立——但「自由組合」讓後端的 N+1 query 問題更難管理,query 的效能保證幾乎不可能提供。
實際的用法:在選型時問三個問題
這個框架在你遇到「要不要採用 X 技術」的時候,可以幫你問更好的問題:
問題一:它在解什麼具體的極限?
不是「它比較新」「它比較流行」,而是「我現在的方案撞到了什麼牆?」如果你找不到一個具體的答案,這個技術可能不是你現在需要的。
問題二:它的假設和我的場景符合嗎?
Event loop 的假設是 I/O 密集,Actor 的假設是無共享狀態。你的場景是哪一種?如果你的假設和技術的假設不同,你在引入一個為別人的問題設計的解法。
問題三:它的新極限在哪裡,我會撞嗎?
這個技術在解舊問題的時候,帶來了什麼新的取捨?你未來的規模或場景,會不會遇到那個新極限?
B01 的第一、第二組(01–08)是背景和框架。這八篇建立的問題意識,在第三組開始(09 起)會有更具體的落地:變數、記憶體、並行 primitive、IO 模型、錯誤處理、型別系統——每一個模型的細節,在這個演進驅動力的框架下,都有了更清楚的「為什麼它長這樣」的答案。