2016 年初,FTGO 的 CTO Mary 參加完一場軟體架構研討會,得出兩個結論:
- FTGO 是典型的「monolithic hell」案例——應用與工程組織已超過巨石架構的承載力。
- 微服務架構是解方,FTGO 必須把應用從巨石遷移到微服務。
Mary 與工程團隊收集了大量資料、看了無數演講與部落格、買了好幾本書,自認對微服務有了「足夠的理解」,便滿懷熱情地推動轉型。但結果並不如預期。
FTGO 忽略了第 1 章中許多關鍵概念——尤其是流暢交付成功三角(DevOps、Team Topologies、合適架構),也忽略了微服務最核心的兩個特徵:設計期鬆耦合與獨立可部署。
V2 架構長什麼樣#
V2 由眾多服務組成,但最值得注意的是:不同團隊對「服務應該是什麼」有截然不同的見解:
- 有些服務以業務領域為中心。
- 有些以技術能力為中心。
- 大小參差不齊,有大如怪物,也有小如螺絲釘。
幾個關鍵服務的設計:
- Order Service:由 50 人的 Order Management 團隊擁有,實作大部分訂單流程(下單、廚房準備、配送管理),也維護餐廳、營業時間與菜單資料。它沒有自己的資料庫,而是另外設一個 Order Data Service。
- Courier Service:同樣由 Order Management 團隊擁有,儲存外送員的基本資料、可用性、目前位置與指派路線。資料同樣放在獨立的 Courier Data Service。
- Consumer Management 團隊則用完全不同的策略:雖然領域相對單純,卻被切成一堆極小服務,每一種使用者資料都用一個服務存:
- Consumer Profile Service(個人檔案)
- Consumer Payment Methods Service(付款方式)
- Consumer Notification Service(通知偏好)
- 與前者不同,這些小服務各有自己的資料庫。
- 此外還有 Accounting Service(整合 Stripe 與 SaaS 會計系統)、Notification Service(向外送員與消費者推送通知)等等。

Figure 2.3: Part of the V2 microservices-based FTGO architecture — many services of varying sizes and ownership
V2 起初看起來確有好處#
- 元件變簡單:單一服務遠比巨石小,容易理解、修改、排錯。
- 團隊自主性提升:每個團隊可獨立開發、部署自家服務。
- 多技術棧支援:不同服務可使用不同棧,升級可一次只動一個服務。
- 依特性分離模組:資源、可用性、安全性需求不同的模組可被打包成獨立服務。
但這些好處掩蓋不了下方的嚴重問題——FTGO 漸漸覺得自己只是換了一種地獄。
V2 出了什麼問題#
V2 架構主要的問題清單:
- 變更上線速度依然很慢
- 服務之間設計期緊耦合
- 因資料不一致而產生的 bug
- 效能不佳
- 服務之間執行期緊耦合
- 技術棧升級依然困難
此外還發現:把所有應用都遷到微服務並沒有帶來等比的價值,「microservices-first」幾乎是個失敗策略。
變更上線速度依然很慢#
FTGO 期待微服務能帶來連續、小批次的部署,但部署仍然不頻繁,而且每次部署的變更又大又複雜,出問題時診斷困難。三個障礙:
- 未落實 DevOps 與 Team Topologies:開發者仍然得把程式碼交給 QA team 測試,團隊間延遲與誤解不斷。
- 忽略獨立可部署:QA 堅持每次都要測整個應用,而非只測有變更的服務,跑慢且脆弱的端到端測試。
- 大量手動測試:耗時、易錯、延後一致性。
部署降到一到兩週一次,每次都堆積大量跨服務變更——當生產出事,診斷如同大海撈針:
- 多個團隊半夜開電話會議,試圖搞清楚服務間互動。
- 從變更發生到部署距離太久,當事人連細節都記不清。
服務之間設計期緊耦合#
微服務的核心特徵之一是設計期鬆耦合,然而 FTGO 卻故意設計出緊耦合的服務,有時則是無意間引入。
故意造成的耦合:資料服務拆分#
- Order Service 與 Order Data Service 拆開後,Order Service 想加新欄位,兩個服務都得改。
- 因為兩個服務由同團隊擁有,協調尚可控。
- 但若是破壞性 API 變更,團隊得做標準的「演化三步驟」:
- 新版 endpoint 與舊版 endpoint 並存。
- 客戶端切到新 endpoint。
- 移除舊 endpoint。
- 一個原本簡單的變更,被迫做成大工程。
無意造成的耦合:領域演化沒有預留彈性#
- 起初 Order Service 的
OrderLineItem由「數量 + Restaurant Service 的 MenuItem 參考」組成,模型簡單。 - 隨著餐廳越來越多,某些品項(例如 Burritos)需要客製化,簡單模型不再夠用。
- 每次擴充
MenuItem,就必須對應擴充OrderLineItem,兩個服務的團隊必須持續合作,並重複做 API 演化的三步驟。
設計期緊耦合讓「應該很簡單的變更」變得複雜,工時遠超預期,業務面極度不滿,FTGO 在市場上的競爭力因此被拖累。
因資料不一致而產生的 bug#
- 細粒度架構讓很多請求橫跨多個服務,流程相當複雜。
- 跨服務更新資料必須使用 Saga 模式(第 10 章),但 FTGO 的某些 saga 因為架構過細而異常複雜。
- 結果是時不時冒出資料不一致的 bug,例如:
- 偶爾一筆訂單沒有指派到外送員,於是無法配送。
- 顧客剛下單立刻取消,有時退款失敗。
- 這些 bug 因為跨服務交互過於複雜,極難診斷與修復。
效能不佳#
- 部分請求要在服務之間往返多次。
- 少數請求甚至要在服務間搬移大量資料。
- 結果是高延遲、糟糕體驗,顧客抱怨應用「有時很慢」。
服務之間執行期緊耦合#
- 執行期耦合:服務 A 必須等服務 B 可用才能回應請求(第 4 章詳述)。
- FTGO 某些請求扇出成長串的 REST 呼叫,行為像傳統「聖誕燈」——一處故障,整串失靈。
- 顧客頻繁看到下單失敗的錯誤頁。
技術棧升級依然困難#
- 各服務的「plumbing(基礎設施程式碼)」——logging、security、monitoring、distributed tracing 等——缺乏一致性。
- 團隊新建服務時要嘛從零寫,要嘛從另一個服務複製貼上,造成無謂的差異。
- 升級流程因此每個服務都不一樣,所需時間遠超預期。
- 嚴重時,團隊乾脆放棄全應用層級的基礎設施改進。
解法:Service Template 與 Microservice Chassis 兩種模式(第 17 章),統一服務 plumbing,讓升級可大規模一致地推動。
怎麼遷移才會出事:大爆炸重寫#
FTGO 採取了大爆炸重寫(big bang rewrite):從零打造全新的微服務應用——應該用 Strangler Fig 模式(第 21 章)漸進遷移才對。
這個選擇造成三大問題:
- 未完成前沒有任何價值交付。
- 追逐移動目標,實際耗時遠超計畫。
- 設計決策直到部署才被驗證,深層錯誤(例如過度細粒度架構)在程式碼基底中反覆出現,需要大量返工才能上線。
把所有應用都遷到微服務,大多沒有價值#
- FTGO 主應用的規模與組織確實值得遷移,但多數其他應用其實很小、由小團隊開發。
- 對這些小應用,微服務化幾乎沒有效益,有時還拖慢開發。
「microservices-first」也大多失敗#
- 多數新應用本來就小、團隊也小,微服務並未加速交付。
- 新應用的領域知識仍未穩定,API 不斷變動,團隊把大量時間花在改 API,反而讓開發停滯。
章節重點摘要#
- 沒有 DevOps 與 Team Topologies,微服務的好處不會自動實現。
- 把「MICROservice」字面化會做出細粒度架構,帶來設計期緊耦合、執行期緊耦合與效能不佳。
- 漸進式遷移優於大爆炸重寫:能持續取得設計回饋,降低做出錯誤架構的機率。
- 巨石只有在有具體效益時才該遷往微服務。
- microservices-first 鮮少是好主意:新應用通常還沒遇到微服務要解決的問題,且 API 難以穩定。