我花了十多年的時間建構資料基礎設施:在 Twitter 建構可觀察性系統、在 Google 建構串流資料處理系統、在 Snowflake 建構宣告式資料處理系統。從一開始,我就注意到程式語言和資料庫的理論優雅與實際使用它們來開發和營運真實系統的現實之間,存在著一個奇怪的鴻溝。這種現實充滿了繁瑣和壓力。我曾經參與過的所有系統,或多或少都顯得脆弱:難以修改,且容易出錯。

基礎設施工程師對變更產生了偏執。我們投入更多精力來測試和部署變更,而不是進行變更。我們稱之為成熟,但我從未停止質疑。一定有辦法將繁瑣的工作委託給我們的工具,讓我們專注於吸引我們進入這個領域的初衷:腦力激盪想法、嘗試它們,並觀察它們的效果。

但究竟缺少了什麼?數十年來,成千上萬聰明才智投入了計算領域,其中許多都致力於縮小偶然複雜性與固有複雜性之間的差距。肯定不會有什麼重大的基礎創新只是在等待被發現——難道不會有人已經找到了嗎?

也許吧。但也許沒有。現代抽象的結構指向了一個特定的機會:現狀迫使我們在強大工具和通用工具之間做出選擇。這感覺像是一種假性二分法。如果我們能找到合適的模型,沒有理由我們不能兩者兼得。

經過多年的尋找,我認為我找到了一個可以打破這種權衡的模型。單憑我一人之力無法實現它,這就是為什麼我和我的共同創辦人 Daniel Mills 和 Skylar Cook 一起創立了 Cambra。我們正在開發一種新型的程式設計系統,它基於一個新模型重新思考傳統的網際網路軟體堆疊。我們的目標是:讓開發網際網路軟體感覺像是在處理一個單一、連貫的系統,而不是將一堆零散的組件串接起來。接下來,我將解釋模型為何重要、碎片化如何破壞它們,以及為什麼建構多領域連貫系統既可能又必要。

電腦是魔法。它們讓抽象概念在現實世界中顯現並影響現實世界。試算表的公式會更新預算,而你決定是否能負擔得起那件閃亮的新東西。路由演算法會計算最短路徑,而你就能抵達目的地。資料庫會記錄一筆交易,然後金錢就會在銀行帳戶之間移動。

每個電腦程式都基於一個模型運作:一種簡化地表示世界的方式。模型讓程式能夠忽略現實中壓倒性的複雜性,而是專注於對程式設計師目標至關重要的世界部分。最簡化的說,一個程式就是一個接收輸入、更新內部狀態、計算後果並發送輸出的迴圈。然而,這種過度簡化掩蓋了一個深刻的真相:模型的選擇對開發和維護哪些程式是可行的有巨大影響。換句話說,有更好的模型和更糟的模型。更好的模型依賴於直觀、行為良好的概念,並提供關於如何創建程式和推理其行為的有用規則。偉大的模型會賦予你超能力。它們不僅讓程式更容易閱讀和編寫。它們讓程式更容易推理。它們使得創建能夠自動驗證、優化和重構程式的工具成為可能。

那麼,為什麼我們不一直使用偉大的模型呢?要回答這個問題,我們需要從底部開始。所有現代電腦程式最終都基於相同的基礎模型運作:儲存在記憶體中的位元以及操作它們的指令。但這個模型非常低階,以至於很難將其概念對應到我們通常關心的熟悉概念。換句話說,給定一個以位元和指令為基礎編寫的程式,很難推斷其目的。反之,給定一個關於程式對現實世界影響的直觀規格,也很難將這個規格對應到一個「位元和指令」的程式。

為了簡化這種對應關係,我們在這個基礎之上建構了更高階的模型:程式語言、作業系統、資料庫。使用更高階模型進行程式設計會有所犧牲:你放棄了對程式如何被「降低」到更低階術語的控制。但隨著控制的喪失,複雜性也隨之降低,這通常是有利的。例如,垃圾回收讓程式設計師不必擔心記憶體釋放,但代價是放棄了對記憶體管理的控制。

模型形成一個部分排序的層級結構,一個模型在其之上構建的模型之上,並在其之下構建的模型之下。但更高階的模型並不一定更適合實現特定的程式。更好的選擇是其概念與問題領域的概念清晰對應的模型。在一個與領域對齊的模型中工作,可以更容易地在需求和實施之間來回轉換。

模型的大部分價值來自工具。工具可以幫助我們確保正確性、提高效能,並隨著時間推移演進我們的系統。但工具是基於特定模型運作的,並且只能在該模型中的概念上發揮作用。例如,考慮像 top 這樣的作業系統層級工具可以告訴你關於你的程式的哪些資訊:資源消耗、運行時間、網路吞吐量等。它無法做到像 gdb 這樣的語言層級工具可以做到的事情,後者是基於 C 的程式設計模型運作的。

但由於工具只在其模型內提供幫助,如果你經常需要「下降」到更低的層級,你就會失去這些好處。最好的更高階模型是那些你很少需要下降的模型。我們稱之為「密封模型」:它們提供了一個不會經常洩漏內部細節的抽象。現代世界有許多無處不在的密封模型的例子:很少有程式是直接用組合語言編寫的,自己實現作業系統,或者在沒有資料庫的情況下管理狀態。一旦一個模型被密封,努力就會分叉:有些人基於該模型開發程式,有些人開發實現該模型的程式。

這是理想情況:在一個密封的、與領域對齊的模型中工作,讓工具處理無聊的事情。但當你正在建構的系統不適合單一模型時會發生什麼?

現代軟體系統由組件組成:資料庫、快取、佇列、服務、前端。原則上,這很有力量——你從貨架上取下組件,將它們串接起來,就能得到一個複雜的系統。

實際上,這個過程常常令人沮喪:

因此,我們建構的系統往往很脆弱。但為什麼呢?這是建構複雜系統的必然結果嗎?我們不這麼認為。我們認為這是出於一個特定原因發生的。

每個組件都有一個內部模型——它在內部使用的概念。但組件也需要相互互動,並且經常使用一個不同、更低階的模型來進行這些互動。函式庫以與你的程式碼相同的模型進行互動。而公開 API 的微服務則不然。

當我們用組件建構系統時,我們用來推理系統的模型是由這些互動模型決定的,而不是內部模型。當組件使用更低階的模型進行互動時,整個系統就被迫下降到該層級。在網際網路軟體中,系統絕大多數被強制進入我們所謂的「網路和作業系統」模型:電腦、處理器、記憶體、網路位址、封包。這些是強大的抽象,但它們離我們實際關心的東西很遠。它們基於位元組和位址運作,而不是物件、人、地點和動作。

例如,假設我們編寫了一個程式並將其連接到關聯式資料庫。程式和資料庫的內部模型具有清晰、定義良好的語義,並且允許我們合理地模擬我們的領域。但是系統的行為不容易受到任一個模型語義的約束。相反,我們必須考慮網路和作業系統來理解任何不完全包含在單一組件內的任何問題(例如,「伺服器進程崩潰了」、「資料編碼損壞了」、「連線中斷了」)。

許多組件使用與其內部模型不同的互動模型是有充分理由的:互通性。有很多模型存在,其中建構了有價值的組件。但大多數這些模型彼此不相容——因為它們有不相容的概念(例如程式語言和資料庫),或者因為它們根本沒有我們需要的概念(例如,大多數程式語言產生的程式運行在單一作業系統進程中,而不是跨多台機器)。具有不相容內部模型的組件無法直接互動——它們必須下降到一個更低階、通用的模型。這就是為什麼「網路和作業系統」模型如此普遍的原因:它強大、經過實戰考驗,並且足夠低階,大多數組件都可以建立在其之上。但以這種方式實現互通性,會犧牲在與領域對齊的模型中工作的系統級別的好處。

我們將這種系統稱為「碎片化系統」。碎片化系統的顯著特徵是它由許多具有不相容內部模型的組件組成。碎片化系統很脆弱:它們難以修改且容易出錯。實際上,這種脆弱性以多種方式體現。

這些都是症狀。根本原因是什麼?在碎片化系統中,開發人員必須根據低階互動模型來推理行為。組件並非可以簡單地組合——每次添加或修改一個組件時,該變更對系統其他部分造成的影響不受該組件內部模型的約束。它們僅受互動模型的約束,而互動模型與領域不對齊,因此難以匹配系統的需求。開發人員必須仔細地從這些角度思考每一次變更。對系統滿足其需求的信心通常需要廣泛的驗證。

碎片化系統本質上是脆弱的。良好的架構和謹慎的工程可以一定程度上緩解這種情況,但如果無法在單一與領域對齊的模型中組合組件,系統可用的工具將受到根本限制。建構和維護碎片化系統所需的努力,隨著其複雜性的增加而呈現不利的擴展。

所以碎片化系統很糟糕。替代方案是什麼?我們稱之為「連貫系統」。連貫系統完全在單一、與領域對齊的模型中運作。這種限制使得工具能夠在整個系統內基於該模型運作,為驗證、優化和自動化創造了巨大的機會。

有許多模型能夠在特定領域內實現連貫系統的例子:

當完全在這些模型中工作時,工具的影響力要大得多。程式設計師的生產力通常會大幅提升,他們建構的連貫系統通常比同等的碎片化系統具有更好的正確性和效能。

所以連貫系統很棒:每個人都應該採用最能有效完成工作的模型,對吧?不幸的是,上面列出的模型都是領域特定的——它們不能推廣到其他情境。而大多數現代網際網路軟體並非領域特定的。現代應用程式通常跨越廣泛的領域,包括 Web 和 API 服務、交易處理、背景處理、分析處理和遙測。這意味著試圖保持系統的連貫性會限制系統最終能做什麼。隨著實現更多功能,應用程式的需求將我們推向單一領域之外,迫使我們尋找具有不同內部模型的組件。因此,一點一點地,我們的系統就變得碎片化。

業界對這種情況的回應是接受碎片化是不可避免的。「使用適合工作的工具」,他們說。每個領域都有自己的專用組件,程式設計師應該將它們串接起來。這是務實的建議——它反映了現實。但它也包含了一個隱藏的假設:碎片化是可以接受的成本,我們無法做得更好。我們拒絕這個假設。

但拒絕一個假設並不等於證明它是錯的。一個通用的、與網際網路軟體領域對齊的模型,是否真的可能?如果可能,難道不會已經存在一個嗎?你可能會猜測,通用性和模型的領域對齊程度之間存在固有的權衡。「與領域對齊」和「領域特定」聽起來很相似,不是嗎?從經驗上看,我們確實觀察到這種趨勢。但這似乎並非絕對必要。我們討論過的許多模型既是通用的又是密封的。C 語言存在於幾乎所有現代軟體的堆疊中。Linux 無處不在,僅在硬實時和安全關鍵系統等罕見情況下不適用。關聯式資料庫幾乎無處不在,NoSQL 資料庫僅佔一小部分使用量,應用程式很少需要尋求低於該層級的模型。有時,權衡會被完全推開。Rust 比 C 更通用,與更多領域對齊,並且擁有更好的工具——而沒有犧牲效能。這些創新很少見,但它們是可能的。

所以,讓我們再想像一次這樣的創新。如果我們能夠建構一個通用的、與建構網際網路軟體通常所需的領域對齊的密封模型,那麼就可以在其之上建構連貫系統,而不會被限制在單一狹窄的領域。這將為工具創造巨大的機會:

如果實現了這些機會,它們就有可能徹底改變網際網路軟體的開發。

這就是我們正在嘗試實現的:一個通用的、與領域對齊的、用於網際網路軟體的密封模型。這是一種對傳統觀念的挑戰,傳統觀念認為「使用適合工作的工具」並簡單地接受由此產生的碎片化。我們相信系統不必在連貫性和建構在通用模型之上之間進行權衡——並且同時擁有兩者的回報是巨大的。

當然,我們不是第一個嘗試這樣做的人。許多人試圖建構通用的、與領域對齊的模型來進行軟體開發。大多數為了實現通用性而犧牲了領域對齊,這是我們關注的情況。有些人為了實現領域對齊而犧牲了通用性,例如 Erlang、Smalltalk 和 Prolog。那些同時實現兩者的人未能實現密封——它們洩漏得太頻繁,無法取代更低階的替代方案。令人難忘的嘗試包括物件導向資料庫和 J2EE。那麼,為什麼我們認為我們可以成功呢?我們將在未來分享更多關於我們方法的資訊。目前,我們只想說:我們相信程式語言理論和資料庫系統的進步開闢了一條以前不存在的道路。

這就是核心論點。但有一個問題,我們預計許多讀者已經在問:人工智慧是否改變了一切?當代理程式可以為我們處理複雜性時,為什麼還要擔心模型和連貫性?

我們一直在抽象地談論工具,並使用傳統工具的例子。但最強大的工具形式最近才以代理式 AI 的形式出現。代理程式比以前的工具更靈活,並且在提高開發人員生產力方面具有巨大潛力。它們已經代表了軟體開發的一場革命。

考慮到這一點,有一種流行的說法認為,AI 本身足以實現上述機會。在這個說法中,程式碼僅僅是一個副產品,是程式設計師意圖的中間表示,其真實性由提供給代理程式的文字提示和文件捕捉。代理程式將處理所有程式碼的複雜性。我們不需要擔心抽象或可維護性。這些是即將過去時代的殘餘顧慮。AI 是未來,傳統軟體工程將很快過時。

我們認為這種說法建立在幾個對軟體的根本性誤解之上。

第一個誤解是模糊性和抽象性的混淆。人們經常認為程式碼是「低階的」,而他們真正想說的是它與領域不對齊。但這並非所有程式碼的固有屬性。它是所用模型相對於要解決的問題的屬性。有些模型較低階,有些較高階。程式碼可以是兩者之一。程式碼的真正意義在於精確性:程式碼是無歧義的,即使它是抽象的。很容易混淆歧義和抽象——兩者都指「一個可能有多種含義的單一陳述」。但歧義陳述的含義完全不受限制。相反,抽象陳述的含義受到其模型語義的嚴格約束。文字總是模糊的,而且將來也總是如此。意義的流動性是其效用的核心。程式碼是精確的。精確性是其效用的核心。作者是人類還是 AI,可能會隨著時間而改變,但程式碼永遠不會被文字取代,因為在設計複雜系統時,精確性將始終很重要。

第二個誤解與模型和領域之間的對齊有關。即使在用文字交流時,程式設計師和 AI 仍然需要共享的概念來進行推理。模型對此始終很重要——它們賦予概念意義。這些模型必須與我們試圖解決的問題領域對齊。如果它們不對齊,問題很快就會變得難以處理。這就是碎片化系統和連貫系統之間的區別所在:使用單一、與領域對齊的模型來使問題變得易於處理。無論是誰建構系統,這種區別都將保持相關。

第三個誤解與良好模型的價值和稀有性有關。有些人會認為,AI 將變得足夠好,可以讓程式設計師免受領域不對齊問題的困擾,從而否定了與領域對齊模型的價值。這種觀點的問題在於它沒有考慮到 AI 內部必須發生什麼才能實現這一點。讓程式設計師免受領域不對齊的困擾是一個非常困難的問題,而最好的方法是發明一個密封的、與領域對齊的模型,並將這個新模型呈現給程式設計師。擁有這樣一個模型的 AI 將優於沒有它的 AI。發明這樣的模型很困難,而且一旦找到,這些模型就很有價值。無論是人類還是 AI 在追求,密封的、與領域對齊模型的追求和使用都將繼續下去。

通過對流行說法的更細緻的理解,我們得出了截然不同的結論。AI 很強大,是的,而且它可能會繼續變得更強大。但是,雖然 AI 可能能夠比人類更快地推理或記住更多,但有些約束普遍適用:宇宙的空間範圍太大,無法通過蠻力來探索。智慧在於找到好的模型來理解和操縱宇宙。在軟體方面,我們可以使用許多強大的模型,但顯然還有改進的空間。發現和實現有用的新模型將賦予 AI 超能力,就像它賦予我們一樣。

已經有大量證據支持這一觀點。看看代理式編碼就知道了。如果你即使將我們最強大的 AI 代理程式釋放到一個複雜、結構不良的程式碼庫中,它往往也會搞得一團糟。代理程式在狹窄的環境中,具有清晰規則的環境中最強大,例如:

這些只是代理程式在與領域對齊的模型中建構連貫系統的例子。

當然,AI 系統的能力將繼續增長。因此,它們能夠表現出色的領域範圍也將隨之擴大。但是,總會有創新的空間,讓 AI 更具生產力。AI 讓在高層級工作變得更容易,但它不是停止在低層級創新的藉口。現在,我們迫切需要一個能夠實現連貫網際網路應用程式開發的程式設計模型。目前,AI 可以提供幫助,但它不會為我們完成這項工作。我們必須自己建構它。