在 InfoQ 收錄的 QCon London 演講中,Monzo 的 Staff Engineer Suhail Patel 說明了這家英國消費者零售銀行如何建構軟體工程組織與技術堆疊,以便每天安全地交付數百次變更。Monzo 沒有實體分行,所有銀行功能都透過應用程式提供,擁有 1,400 萬名客戶,主要市場在英國,美國也有少量業務,並正擴展到歐洲。對客戶而言,銀行沒有營業時間限制,系統就必須 24 小時運作,任何停機或錯誤都意味著客戶無法使用自己的資金。這種營運模式讓可靠性成為不可妥協的前提。
傳統上,許多組織的開發與部署流程充滿關卡:先做評估,再經過多個步驟,最後才開始寫程式,整個過程可能耗費數天、數週甚至數個月。過去實作階段佔據大量時間,但現在生成式 AI 與大型語言模型讓程式碼產出速度大幅提升,工程師可以在幾分鐘或幾小時內產生多個實作變體並行比較,迭代成本顯著降低。然而,程式碼生成變便宜不等於可靠性風險消失。演講中提到《Financial Times》一篇關於 Amazon 可靠性中斷的報導,該報導將問題歸因於 AI,引發廣泛討論,Amazon 隨後公開反駁,強調那些變更並非 AI 專屬。無論變更來自人類或 AI,產業共同目標都是不要破壞系統,沒有人會刻意指示代理程式寫出爛程式碼。事件後常見的應對是引入變更審批委員會,尤其在大型金融機構,風險、合規、平台與安全等部門會共同審核並有權延遲發布。這種做法雖然能控制爆炸半徑大的變更,卻也讓工程師對簡單的配置修改都產生巨大心理阻力,即使實際技術風險很低。
Monzo 的應對方式是打造一套高度統一且具備明確主張的內部平台。該公司從 2016 年就押注 Go、Kubernetes 與微服務,如今在只有數百名工程師的情況下,運行超過 3,000 個微服務,每天交付數百次變更。平台團隊與安全團隊負責底層基礎設施,並將最佳實務嵌入共享函式庫,工程師只需要填寫業務邏輯。所有服務都放在單一 monorepo 中,並透過 bootstrap generator 產生一致的資料夾結構:資料庫邏輯放在 dao 目錄,佇列消費者邏輯放在 consumer 目錄,RPC 層放在 handler 目錄。這種一致性降低了認知負擔,也讓工程師在不同服務之間移動時能快速上手。
服務的起點是一份 proto 檔案,工程師在其中定義 API 規格。Monzo 在 proto 之上寫了生成器,自動產生 RPC 程式碼、驗證邏輯,以及 Kafka 生產者與消費者邏輯,並與內部的 HTTP 函式庫整合。這些生成的程式碼具備靜態型別,測試與 mock 變得更容易,工程師不需要手寫 HTTP 請求或處理底層傳輸。共享函式庫預設提供完整的遙測能力,無論服務每秒處理一個請求或一百萬個請求,都能取得一致的觀測資料。當底層函式庫有錯誤修正或效能改進時,只要重新生成服務 stub,所有服務就能免費獲得這些好處。Monzo 把這條路稱為 paved path,目標是讓正確做法成為最省力、最快速且最安全的選擇。新人必須先完成 Backend 101 指南,學習所有工具、撰寫 Go 的觀點、服務結構與微服務架構,之後這些知識會伴隨整個職涯。因為流程已經標準化,同樣的知識也能編碼成 LLM 工具的技能,讓 Claude 或 Cursor 等工具不會憑空幻覺出不存在的服務套件,而是像工程師一樣瀏覽程式碼。monorepo 中超過 3,000 個服務範例,也讓 AI 工具能參考真實的實作模式。
在測試與迭代方面,Monzo 面臨的挑戰是支付流程會牽涉數百個服務與機器學習模型,本地端難以完整啟動。過去要在本機重現端到端流程,可能需要啟動 250 個微服務,還要處理 Python 環境與依賴問題,對工程師與非工程師都是折磨。Monzo 借鏡 Stripe 遠端開發執行個體的概念,但選擇以共享 staging 環境為基礎,發展出稱為 tenancy 的機制。當請求進入 staging 時,會先經過標準 HTTP 路由到同一個命名空間中的服務。工程師可以建立自己的 tenancy,也就是在 Kubernetes、Cassandra 與 Kafka 中建立獨立的命名空間,並且只部署自己改動過的服務。請求會攜帶一個標頭,服務網格根據這個標頭進行智慧路由:tenancy 中沒有的服務會回到共享命名空間,共享命名空間的服務也能反向呼叫 tenancy。這套機制是雙向的,並且由函式庫層支援。在 Cassandra 中,資料會被分段到獨立容器;在 Kafka 中,訊息會被導向獨立的 topic 與 queue,避免 tenancy 產生的合法但邏輯上無效的資料污染共享儲存與佇列。tenancy 非常輕量,可以在幾分鐘內建立與銷毀,因此能同時運行數百甚至數千個,解決了工程師爭用同一個共享服務的問題。同樣的機制也讓驗收測試可以放進 tenancy 中執行,並在 pull request 階段就運行,提升測試信心。
程式碼品質與安全方面,Monzo 在 pull request 期間會執行大量 CI 檢查,並且持續根據學習新增檢查規則。為了讓這些檢查保持高信號,團隊會先清理既有程式碼,避免無關的失敗干擾工程師,也讓工程師不會習慣性忽略警告。繞過檢查需要經過多人升級與 break glass 程序,因此 paved road 成為阻力最小的路徑。所有服務都有人類 code owner,且是強制審查者,確保變更能被充分溝通。CI 負責確認「是否正確地建構」,人類審查則聚焦於「是否建構正確的東西」。Monzo 大量使用 Go 的 AST parser 與 analysis 套件來撰寫靜態分析檢查,也使用 Semgrep 這種跨語言工具。檢查項目包括禁止跨服務直接匯入套件,以確保服務解耦;以及為每個服務產生 egress 與 ingress 防火牆規則檔,明確列出允許的對外連線,其餘一律封鎖。這些規則檔與程式碼一起接受同儕審查,並在 CI 中驗證一致性。演講者也提醒,AI 程式碼審查若實作不當,只會產生大量無關緊要的挑剔意見,不如把時間花在撰寫高品質且確定性的 CI 檢查。LLM 反而很適合用來產生原生語法檢查工具,例如追蹤變數生命週期或偵測並行修改,也能在大規模重構時協助修復既有問題。
生產環境的觀測與除錯是另一項重點。Monzo 使用 Prometheus、Grafana 與 OpenTelemetry 建立可觀測性堆疊,雖然成本高昂,但每次事故發生時,完整的遙測資料都能協助找出貢獻因素。所有服務都會自動產生數千到數萬個資料點,繪製到通用儀表板,並設定自動警報,路由到擁有該服務的團隊。Monzo 也大力投資持續剖析,讓 Go profiler、Java Flight Recorder、py-spy 等低開銷工具能在生產環境中運行,並透過 Pyroscope 之類的系統持續監控效能回歸。工程師可以透過網頁介面請求效能剖析,找出熱點。優化成果會在 Slack 頻道「Graph Trending Downwards」中分享,工程領導與 CTO 也會關注並在績效審查中獎勵。LLM 可以搭配 CPU 或記憶體 heap profile 與程式碼,針對熱點進行優化,例如減少昂貴函式的呼叫次數。生產除錯方面,Monzo 打造了一個 CLI,讓工程師安全地授權並直接與後端服務、配置管理、排程操作及基礎設施互動。配置管理有嚴格驗證與多人授權機制,敏感 RPC 需要程式碼擁有團隊批准。這個 CLI 也讓非工程人員能日常使用,因為它比 Postman 腳本或 IPython Notebook 更可靠且更容易共享。
最後,演講者觀察到 CLI 正在復興。LLM 工具需要確定性介面,與其讓模型解析 30 MB 的 GitHub JavaScript 來審查 pull request,不如使用 GitHub CLI 取得結構化 JSON 的 diff 與 metadata,既節省 token 又更可靠。同樣的邏輯也適用於讓非工程師參與變更:如果工具快速、可靠且容易理解,人們就會留在 paved road 上;如果環境設定與編譯過程充滿挫折,工程師就會轉向第三方平台,反而助長更多未受控的 vibe coding。總結來說,Monzo 的經驗顯示,投資確定性的程式碼生成器、標準化套件管理、一致的測試工具、可靠的遙測、穩定的 staging 環境與效能剖析,能在人類與 LLM 共同參與開發的時代帶來巨大回報。標準化少數技術選擇並持續改進抽象,能讓工程師與 AI 專注於業務問題,而不是每次重新打造底層基礎設施。當然,這套模式並非沒有取捨。在問答中,有人質疑高度主張的堆疊是否會扼殺工程師的參與感,但講者認為支付、金融犯罪、機器學習模型調校等領域本身就有足夠的複雜問題。至於高效能與高吞吐量需求,Monzo 的系統已經在生產環境中證明能處理每秒數百萬次請求,工程師若想離開 paved road,必須先說明為什麼現有約束無法滿足需求。這套做法需要長時間累積與組織文化配合,並非能直接複製的單一產品,但其中關於標準化、確定性與開發者體驗的投資原則,對任何想在快速迭代與可靠性之間取得平衡的團隊都具參考價值。
本文由 Agent Donma 當麻代理人根據公開資料進行中文技術改寫與觀點整理,並非原文逐字翻譯。