避開迷你框架 (Mini-frameworks) | laike9m 的部落格
查看 Hacker News 上的討論
過去幾年我在 Google Ads 工作。隨著時間推移,我看到一種模式反覆出現,給開發者帶來無盡的痛苦,那就是:建立「迷你框架」(mini-frameworks)。
什麼是迷你框架?
首先,我想讓讀者了解我所謂的「迷你框架」是指什麼。想像一家擁有 1000 名工程師的公司,他們共享同一套技術棧。久而久之,某個團隊發現這套共享技術棧不盡如人意,可能是因為他們必須反覆編寫樣板代碼,或是性能不夠好,或其他原因。於是,他們決定在現有的技術棧之上建立自己的框架。當你聽到工程師在展示成果時,使用他們自創的概念和術語來實現他們的思維模型時,你就知道這是一個框架,而不僅僅是一個函式庫(library)。這類框架就是我所說的「迷你框架」,它們通常具有一些共同特徵:
如果你在大公司工作,很可能見過甚至親手做過這種事。但為什麼這很糟糕呢?作為受害者,我可以分享我的故事。
我的故事
多年來,我的團隊一直使用 Google 內部的框架來編寫服務端代碼,並逐漸將我們部門的代碼庫遷移到該框架上。這個框架設計精良,並由專門的團隊維護。我們總體上對它很滿意,雖然它有一些讓人心癢的小毛病(不是痛點)。作為一名強大且有野心的工程師,我的主管提議在該框架之上增加一個抽象層,目的是降低部門採用的門檻,並順便解決那些小毛病。我當時持懷疑態度,並試圖說服主管不要這樣做,但失敗了。最終,幾名工程師花了數個季度的時間增加了這個抽象層,接著好戲開場了。
首先,其中一位作者嘗試在我們現有的代碼庫中採用它。大家原以為這會很容易,結果卻花了大約一年的時間。為什麼?因為在遷移過程中,他們發現這個抽象層無法處理某些使用場景,所以不得不花時間打補丁使其運作。與此同時,像我這樣的其他人仍在用原始框架編寫新代碼,這又增加了遷移的新需求和工作量。最終,在花費了遠超預期的時間後,遷移完成了,我的主管決定新編寫的代碼必須使用我們自己的框架。
你以為這就結束了嗎?當然不是!
自從我開始用新框架寫代碼以來,沒有一次是不想咒罵並辭職的。這個框架非常難用,它為了隱藏複雜性而引入了過多概念,結果反而帶來了更多複雜性。因為我不是編寫它的人,我不熟悉所有的技巧和偏門方法。結果,以前我一天就能完成的工作,現在可能要花兩週,而且我還得不斷詢問作者某些事情該怎麼做,甚至預約了結對編程(pair-programming)。據我所知,其他團隊成員也有同樣的抱怨。至於最初推動採用的目標,當然沒有起到幫助(如果沒有讓進度變慢的話)。諷刺的是,這個抽象層本應讓團隊以外的人更容易寫代碼,但現在連我們自己的團隊都深受其害。
回過頭來看,部分問題是由於糟糕的設計和實現造成的,但根本問題在於最初建立迷你框架的這個決定。我觀察過其他的迷你框架,它們或多或少都有同樣的問題,只是嚴重程度不同。因此,我總結了幾點想法來解釋為什麼迷你框架很糟糕。
為什麼迷你框架很糟糕?
第一,迷你框架缺乏功能完整性和兼容性。人們常以為自己可以神奇地「隱藏不必要的複雜性」,但現實中往往做不到。即使迷你框架能處理 80% 的使用場景,它們通常也缺乏原始框架的靈活性和功能來滿足剩下的 20%。DSL(領域特定語言)也有同樣的問題,這也是為什麼人們如此討厭它。
第二,迷你框架違反了 ETC(易於修改,Easier To Change)原則。我最早是從傳奇著作《程序員修煉之道》(The Pragmatic Programmer)中學到這個概念的,令我驚訝的是,許多程序員(甚至是資深程序員)竟然沒有意識到這一點。簡單來說,它建議編寫代碼時應考慮到未來的修改能輕鬆進行。建立迷你框架在兩方面違反了這一原則:
第三,迷你框架是創作者思維模型的體現,但並非每個人的思維模型。傾向於建立迷你框架的人通常更有主見,這本身是件好事。但當你為他人創造工具時,過於主觀會產生問題。我甚至會說,有時(並非總是)建立迷你框架直接反映了作者的虛榮心,他們選擇框架是因為一個小小的函式庫無法體現「這項工作的重要性」。
第四,迷你框架往往會導致技術棧碎片化。我生成了一張圖來說明我的意思:系統的一部分已遷移,另一部分則沒有。隨著新層不斷增加,情況會隨時間惡化。不確定其他公司是否如此,但在 Google,我從未真正見過一個代碼遷移能完全完成,這有點滑稽。
最後也是最令人擔憂的一點:缺乏維護。與由專門團隊擁有的共享基礎設施不同,迷你框架通常由創建它的那一兩個人擁有。一旦他們離開團隊或公司,就很難找到繼任者——當然其他團隊成員可能大致了解其運作方式,但肯定不如作者深入。此外,人們缺乏維護現有東西的動力,因為做這件事不會讓你獲得加薪或晉升。因此,迷你框架往往隨著原作者的離職而消亡,除非在那之前它已經獲得了大規模採用,而這種情況發生的機率微乎其微。
那麼,你應該怎麼做?
在這一點上,有必要澄清我反對什麼,不反對什麼。我當然不反對增加抽象——因為抽象本質上就是程序本身,我們離不開它。我反對的是以錯誤的方式增加抽象,以及增加不必要的抽象。
讓我再次強調這一點,因為它非常重要:
函式庫(library)和框架(framework)之間真正且唯一的區別,在於它是否引入了新概念。界限有時可能很模糊,但通常很容易辨別。例如,一個函式庫可以包含圍繞原始框架的一組子類或工具函數,因為它們沒有引入新概念。但如果你看到一個 README 以「術語表」(Glossary)章節開頭,那麼 99.99% 的機率它是一個框架(人們可能仍稱之為「函式庫」,但你懂我的意思)。
我的觀點是,我們在引入新概念時應該非常非常小心。如果可以,請避免這樣做。一堆流行詞彙帶來的認知負荷比人們意識到的要重,尤其是框架作者所能意識到的。文字是思想的鏡子,代碼亦然。作者創造概念是因為這是他們在大腦中對問題建模的方式,是他們的思考方式。這就是為什麼他們聲稱這些概念「自然且直觀」,而其他人卻在苦苦理解。
所以第一條規則是:避免建立迷你框架,改為建立函式庫。但在你確實發現有必要建立框架的情況下,我的建議是:
將概念與具體的業務需求掛鉤,而不是你腦袋裡的某種想法。
從零開始。不要圍繞現有框架構建包裝層(wrapper),而是從頭開始構建你自己的框架。是的,這將使它成為一個需要更多討論和資源的重大決策,但它能避免上述許多問題。畢竟,你有充分的業務理由這樣做,對吧……對吧?