最好的 Go 框架:沒有框架?

張哥說技術發表於2022-12-14

作為Go 語言的團隊領導者這幾年時間,我從初學者那裡聽到的最常見問題是“我應該使用什麼框架?”。我的想法是使用過去的語言經驗去編寫 Go 程式往往會讓結果變得非常糟糕。

其他的程式語言已經建立了 “預設” 的框架。Java有Spring,Python有Django和Flask,Ruby有Rails,C#有ASP.NET,Node有Express,PHP有Symfony和Laravel。但 Go 沒有預設框架,這一點跟其他程式語言完全不同。

並且,很多過來人建議你根本不應該使用框架。到底該怎麼辦?

最好的 Go 框架:沒有框架?

從庫構建 Go 服務可能會給人一種構建弗蘭肯斯坦的怪物的感覺。


Go 語言哲學

Go 語言有很多框架,但沒有一個提供像其他語言框架那樣的功能集。

這種情況短時間內還會持續

你可能認為這是因為 Go 語言的生態更年輕。但更重要的是,Go 是圍繞 Unix 哲學構建的,Unix 的哲學要求編寫的程式需要具備以下三要素:

  • 只做一件事,並做好。

  • 程式間協同工作。

  • 處理文字流,因為這是一個通用介面。

這種哲學起源於 B 程式語言(C 語言的前身)的設計者 Ken Thompson, 並以同樣的哲學思想在 2006 年建立了 Go 語言。

將 Unix 哲學運用實踐中,你會編寫出多個獨立的軟體來做好每一件事,而不是寫出一個巨大的程式去做所有事情。這些例子在 Linux 終端中就能經常看到。比如:cat example.txt | sort | uniq。cat從檔案中讀取文字,sort對行進行排序,並uniq刪除重複項。所有命令都是獨立的,只做一件事。它源自 Unix 哲學。由於這樣的設計,你可以獨立開發更小的、自主的命令。

在 Go 中,Unix 哲學在標準庫中隨處可見。最常見的就是最廣泛使用的介面:io.Reader和io.Writer。最好的庫也遵循這一理念。

框架的設計往往是 “反 Unix 哲學而行”。語言框架試圖在一個框架內涵蓋所有可能的用例。它們不是為與其他工具一起使用而設計的,而且通常不能重複使用。這意味著不可能將開發工作轉移到其他不相容的框架。如果一個框架沒有人用,那麼針對該框架所有的所有努力都會白費。


對你來說,什麼才是真正重要的

每個技術決策都有代價。你需要選擇專案可以承受的代價, 並從決策中獲得儘量大的收益。

當你在做一個專案的簡單技術驗證 (Proof of Concept)時,最重要的是你可以多快的完成。但如果這是個持續時間長,涉及多人協作的大型專案時,這個技術驗證帶來的代價將會是巨大的。

對於大多數專案,最需要關注的是以下幾點:

  • 啟動專案的速度

  • 完成專案的速度

  • 專案適應外來需求變化的能力, 這和上一點息息相關

讓我們來逐條分析下影響我們決策的因素。


節省時間

語言框架給出的最大承諾之一是節省時間。只需一個命令,就能啟動一個可用的專案。語言框架通常提供專案的預設架構,這會對入門有點幫助。但與大多數其他技術決策一樣,它也有一定的代價。

隨著時間的推移,當專案增長時,你會很快碰到該語言框架的約定和限制。語言框架的需求大機率會跟你的專案有區別。語言框架提供的教學文件可能適用於簡單的 CRUD 應用程式,但一般無法處理更復雜的場景。 為了和語言框架的限制作鬥爭,很容易就會將啟動專案所節省的時間全部浪費。長期以往,這會給團隊帶來很多挫敗感。

幾年前,我在一家使用某個 Go 語言框架起家的公司工作。公司正在發展並且不斷的創造新的服務。隨著時間的推移,當我們想要支援更復雜的用例時,我們開始越發痛苦。不僅如此,長此以往,使用語言框架逐漸成為了產生嚴重 Bug 的主要原因。但擺脫語言框架遠不如想象中容易。

有一次,一些框架元件變得無人維護並且與生態的其餘部分不相容。我們將不得不去掉它。但是, 該框架中的所有元件之間耦合非常高。從幾十個服務中刪除一個元件非常困難。這需要多團隊協作,並大機率會帶來一些事故。即使最後我們成功刪除了這個元件,我也不會覺得這是件值得驕傲的事。如果有人早點做出不同的決定,我們就可以更好的利用為了刪除這個元件所花掉的時間。這也是為什麼很多公司對開發團隊不太信任的主要原因。

為幾年前的小決定付出了高昂的代價, 這個故事是個很好的例子。

最好的 Go 框架:沒有框架?


專案的可維護性

量化專案的可維護性一直以來都是個有爭議的話題——很難比較兩個專案哪個可維護性更高。一些人覺得使用語言框架很好, 使用很方便。但是對於一些複雜的專案,長期使用語言框架會使開發工作變得越來越困難。並且,還有很多人覺得跟語言框架作鬥爭是他們的正常工作。因此很難客觀地衡量框架對專案可維護性的影響。

但是,我們可以透過一些手段真正理解它。基於《加速:精益軟體科學和 DevOps》一書。這本書重點描述瞭如何找出表現最好和最差的團隊的特點。書中提到,想要讓一個團隊表現更好,最重要因素之一是讓專案架構的耦合更加鬆散。

我領導的團隊成員經常問我如何知道我們的架構是否鬆散耦合。這裡有一個好方法,如果你應用裡的元件可以被輕鬆替換或刪除, 說明這個應用是鬆散耦合的。但是, 如果大多陣列件都很難被替換,改動一個元件會導致其他地方發生問題的情況,則說明你的應用耦合很高。

為什麼松耦合架構如此重要?首先我們需要承認,我們是人, 即使做了最周密的準備,我們仍然可能犯錯誤。當你選擇了錯誤的框架或庫時,應該很容易替換它而不用重寫整個專案。如果我們想節省時間,我們應該從長遠來看某些決策是否有收益,而不僅僅是在專案開始時。

考慮一個場景,當你想要完全刪除框架時。它需要重寫大量程式碼麼?它可以獨立地在多個服務上完成嗎?如果沒有,你可以努力將框架與核心邏輯分開。但當你這樣做時,使用框架被節省的時間就已經開始被浪費了。


替代方案?在沒有框架的情況下構建服務

你可能覺得如果不用語言框架的話,構建服務會花費很長時間。特別是當你從其他語言轉型到 Go 語言時。我懂你的痛。幾年前,當我開始用 Go 語言程式設計時,我也有過同樣的感覺。這是一種毫無理由的的恐懼。不使用語言框架不代表你需要自己編寫所有程式碼。有許多經過驗證的庫可以提供你需要的功能。

你需要花更多時間做調研,尋找更好的答案。比如找到並閱讀本文。在整個專案的生命週期中,幾個小時的調研不算什麼。足夠的正確資訊帶來的靈活性將讓你很快讓專案回到正軌。

如果你決定不使用框架,你應該怎麼做?開頭最大的障礙可能是如何構建服務。最簡單的方法是開始時只用一個檔案編寫所有邏輯。一切從簡,推遲一些決策,隨著時間的推移改進你的專案。

擁有可用作參考的示例專案會很有幫助。你可以檢視我在 GoRemoteFest 上使用 Watermill 演示在 15 分鐘內構建事件驅動應用程式時使用的專案 – github.com/roblaszczak/goremotefest-livecoding。這個例子只需要兩個外部庫就可以正常執行。

隨意克隆這個 Git 並測試。為了幫助你處理更具體的用例,下週我們將釋出一篇文章,其中包含可用於構建你的 Go 服務的 Go 庫列表。我們已經使用它們幾年了。我們還將解釋為什麼我們使用這些庫,以及如何知道一個外部庫是否值得使用。

最好的 Go 框架:沒有框架? 

在不終止專案的前提下修改專案的某些部分是很實用的。

當你的專案變得更加複雜,並且你已經知道你的庫如何協同工作時,你可以開始重構它。你可能不需要很多看起來很重要的框架功能。長此以往,你得到一個更簡單的專案,並最終花了更少的時間去調研。

如果你正在尋找一些複雜專案的最佳實踐,你應該檢視 Wild Workouts - 我們們功能齊全的示例 Go 專案。我們釋出了一本描述我們如何構建應用程式的書。


總結

當一個專案開始時,決定如何構建服務是最不應該走捷徑的地方。從長遠來看,錯誤的決定會對團隊的效率產生負面影響,還會影響士氣。

在做出錯誤的決策後,你很快就會陷入沉沒成本謬誤陷阱。我們不應該成為解決他們製造的問題的英雄,而應避免製造他們。

讀完此文,你應該瞭解了使用語言框架的代價並瞭解如何做出權衡。你現在可以做出負責任的決定。我希望這篇文章至少能幫助一家公司避免需要經歷跨團隊經歷幾個月才能完成的的重構專案的痛苦。

你是否有任何關於使用語言框架的恐怖故事?評論裡見!



原文地址:
原文作者:
Robert Laszczak
本文永久連結:
譯者:魚雷

校對iddunk

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024923/viewspace-2928142/,如需轉載,請註明出處,否則將追究法律責任。

相關文章