陋見:從商業到開源的一些思考
本文討論了 商業項目 vs 開源項目 在多個方面的差異,關鍵要點包括:
- 交付品:開源項目交付品更復雜,源碼、開發過程等都需透明,對各方面要求更高。
- 工程化:開源項目人力緊缺,對工程化、自動化需求更高,需預先設計搭建適用環境。
- 自動化測試:開源項目對自動化測試要求高,需體系化設計測試系統。
- 依賴管理:開源項目依賴管理需更嚴格,要合理設計依賴結構,降低復雜性。
- 溝通:開源項目多使用異步溝通工具,如 Github Issue 等,可配合工程化手段提升效率。
前言
在正式開始討論之前,需要各位讀者先思考一個問題:開源的收益是什么?具體答案在不同上下文中可能略有偏差,但大致上至少有這兩方面的收益:
- 擴大個人或團隊影響力:讓社區更多人了解到有這么個擅長解決某一問題的個人或團隊,甚至成為這個方向的權威人物,擁有更大話語權,參考 @evan、@zack、@Langchain 等;
- 生態共建:理想情況下,開源方式更容易引入更多優秀工程師參與到產品開發中,群策群力,對需求與問題更敏感,因而迭代速度可能更快,相比人數有限的商業團隊更有可能開發出滿足諸多長尾需求的技術產品;
這些收益能切實解決許多現實問題,因而對許多從業者而言“開源”似乎已經某種程度上成為“政治正確”的默認選項,于是經常出現一些團隊或個人,在沒有做好充分調研的情況下,匆忙進入開源領域,“天真”(這個詞確實不太好聽)地認為只要將代碼掛載在 Github 上“開放”給社區就算是達到開源狀態了,但通常 后繼乏力,即使堅持投入時間精力許多時候也很難達到預期目標,究其根本,我認為主因在于:許多人并沒有意識,商業開發與開源開發是兩件差異極大的事情!
說來慚愧,雖然我已經從事前端超過 10 年,但從未正式參與過稍具影響力的開源項目,因此我個人更熟悉商業項目的運作方式 —— 相信這也是大多數讀者的真實狀態,好在工作關系日常需要深入理解各類開源產品的底層實現,多多少少也摸索出了一些門道,對商業項目與開源項目的區同點有了一些自己的看法,拋磚引玉吧。
商業項目 vs 開源項目
首先,商業項目通常只需要交付應用的最終執行界面即可,因此相對更著重于滿足功能、穩定性、性能等方面的需求,具體實現細節從外部視角看完全是個黑盒。但開源項目的交付品要復雜的多,在功能基礎上,所有源碼、開發過程、工程設施甚至溝通討論過程都是對外透明的,因此開源產品不僅僅要對結果負責,還需要對過程負責,也因此對于優秀的開源項目而言,代碼質量、穩定性、接口易用性、可擴展性、分支模型、開發規范、版本管理、工程化設施等等維度,都是產品的一部分,都需要仔細斟酌維護的。
舉一個非常細節的例子:分支模型,分支應該如何命名?那些分支可以往那些分支合并?特性分支何時合入主干分支?那些分支必須確保穩定,又如何確保穩定?進一步的,那些分支可以發布正式版本?什么時候應該打什么 Tag?是否需要保持 Linear-history ?等等。
并且分支模型規范還必須足夠簡單,讓各類背景的開發者能迅速參與到項目;最好還可以補充一些自動化工具,確保參與者不犯低級錯誤。國內許多商業團隊可能已經習慣于火車模型 —— 本質上是 FBD 的變種,但這種方式太過復雜,理解、操作成本過高,多數情況下并不適配開源環境,因此多數時候會轉而采用 TBD,但 TBD 模型對穩定性要求極高,進而又催生非常復雜而重要的自動化測試需求。
圖片
再比如說,版本管理,我們都知道 semver 模型(參考:NPM 依賴管理的復雜性),但什么時候應該發 patch,什么時候應該發 minor 呢?判斷標準是什么?誰來做這個判斷?什么時候能從 0.x 切換到 1.0 呢?是否需要保持向后兼容?又有哪些特性、接口需要保持向后兼容?總之,當你預期開發一個優秀的開源項目時,你必須仔細思考這些平時并不需要關注的問題,否則在未來總會引發一些技術、PR 風險。
當然,這并不是說開源就必然比商業項目更難更復雜,相反,許多優秀開源項目通常只聚焦于解決某個具體問題,并在架構上留出足夠靈活的邏輯插槽,交由社區按需擴展實現各類長尾需求,例如 Webpack、ESLint、 RSPack、Vite 等等,因此開源項目通常有比較高的技術復雜度,但功能通常是非常收斂的。反觀商業產品的功能復雜度幾乎沒有上限,例如淘寶、抖音、火山引擎等,當功能足夠復雜時,也必然會反推整體架構、技術復雜度的非線性增長,雖然可能內在的許多技術細節并不是最優,也不具備可遷移復用性,但也不能否認這里面存在深層次的技術難度。因此,我認為兩者并沒有絕對優劣之分,歸根結底只是在解決不同場景下的不同問題罷了,并沒有明顯的優劣之分。
我認為更重要的,在進入開源之前一定要理解這件事情的成本與收益,理解各類工程處理細節的差異,評估 ROI 是否能打正,團隊是否有足夠能力與技術品味等等,切忌為了開源而開源。
工程化
開源項目通常有一個非常突出的特點:人力緊缺,畢竟能全心全意為愛發電的人并不多,多數時候,參與者是在本職工作(解決生存問題)之外,花費個人寶貴的休息時間參與開源(解決情懷問題),因而能投入的有效時間非常有限。但同時,優秀的開源項目通常有比較高的準入門檻,且不說深入理解項目的實現原理、架構設計,之后提交符合整體設計、代碼風格的 PR,光是理解如何初始化環境并運行項目、如何提交有意義的 PR、如何按照提交高質量 ISSUE,可能已經需要耗費比較多的學習時間。
而站在項目管理者視角,當參與開發的人數到達一定數量時,成員良莠不齊必然會衍生一系列過程質量問題,例如提交一堆連單測都跑不通的 PR,又如未按各類規范準則編寫代碼,再如提交的代碼存在明顯性能問題等等。原則上,問題越早發現修復成本越低,因此要求倉庫管理者們投入比較多的時間精力前置做好質量把控,攔住這部分低質量開發行為。
這兩類案例,究其根本都是時間、空間復雜度問題。在商業項目中,可以通過配置分工合理的團隊結構,完善開發流程及規范,在有限空間復雜度內通過增加人力與行為約束的方式緩解團隊協作引發的熵增。
但開源項目不可能采用這種方案,因為參與項目的群體可能比較龐大且地理位置分布廣泛,技術水平參差不齊,文化背景多種多樣,很難照搬商業公司的管理模式,將每一位成員按在特定的職責范圍上 —— 多數情況,反而是由個體按照其擅長的領域自發地解決某些特定問題(實際上這也正是開源的魅力所在),但這種個體視角的解決方案在倉庫上下文環境中不一定是最優的;其次,在開源環境中通常也很難通過規范文檔方式約束個體的開發行為,即使編撰了一堆完美的開發說明書,一是很少有人有耐心完整看完,并認可;二是文檔約束力非常薄弱,需要配置相應監督者持續關注研發細節,而這很不敏捷。
這些問題最終會導向一個相對可行的解決方案:工程化。注意,工程化并不僅僅是一堆工具的簡單堆疊,而是一個復雜、綜合的工程學問題,通常,開源環境對工程化、自動化的需求要遠高于商業項目,不僅需要配置好常見的 Bundle、Lint、UT、E2E 等基礎設施,還需要根據具體場景進一步搭建各類自動化工作流。
在發起開源項目時,非常值得投入一部分精力預先設計、搭建好適用的工程化環境,因為這些自動化流程能夠長期以極低的成本防止項目質量劣化 —— 至少能規避許多來自四海八方的低級問題,減少管理者審核負擔;能在出錯時及時給出適當反饋,降低項目的準入門檻;也能規范化各類關鍵流程,避免人的隨機性帶來的隨機謬誤。
當然,工程化也并不是銀彈,有許多邊界問題無法或很難被自動化解決,例如項目架構設計是否足夠優秀,或者用戶文檔是否足夠完備清晰,又或者整體項目規劃等,這類問題依然強依賴于人力介入。
自動化測試
這是需要著重強調的點:開源項目對自動化測試的要求比常規商業項目高出許多!商業團隊通常會設置專職測試者定期檢查產品的質量情況,對最終質量負責 —— 或者至少起兜底作用吧。但如上所述,開源項目很難出現這類專職角色,因而開發者自身直接對產品質量負責,需要親自完成各類測試動作,但為愛發電的開發者們很難投入時間反復做各類測試,也很難做的很細致。
因此,在開源項目中很自然地采用了另一種更敏捷,對人力需求更低的方案 —— 自動化測試,由代碼負責測試代碼的穩定性。具體的測試技術有很多類型,單元測試、E2E 測試、性能測試、接口測試等等,視乎具體情況,許多優質開源項目會采用其中一種或多種自動測試方案,為功能代碼編寫若干測試用例,之后在合碼前、發布前等關鍵節點設置卡口,執行測試代碼,當所有用例都能運行通過,且測試覆蓋率達標后才能繼續推進流程。
某種程度上,測試用例就是項目成員之間的一種非文檔性質的強約束契約,任何人嘗試修改代碼時都必須遵循這份契約,必須保證存量用例都能運行通過 —— 或者,必要時更新這些契約以適應功能代碼的迭代。雖然開發和維護用例代碼本身也是一件比較消耗時間的工作,但這份契約定義的越是詳細,覆蓋面越廣,越是不容易犯錯,即使是完全不了解項目上下文的新人,也能夠在缺乏第三者協助的情況下,單純依靠測試框架及其它質檢工具,就可以寫出符合要求的代碼,而這很契合開源項目的人力分布特點。
理論上測試用例越是完整,項目整體質量越是穩定,但自動化測試也是有技術門檻與人力成本的,要達到上述的理想狀態并不是容易,需要體系化設計測試系統,常見的手段包括:
- 借助單元測試(UT)技術實現白盒測試,覆蓋代碼模塊內部的各邏輯分支。需要注意的是,單純追求覆蓋率其實意義不大,而應該進一步思考并推導出各類邏輯上的邊界場景(雖然這很難) —— 特別是異步、并發等復雜時序場景。舉個例子,在測試一個按鈕組件時,不僅要驗證它的基本功能,還要設計用例來測試連續點擊是否會導致事件的連續觸發;
- 借助 E2E 技術,對產品界面做黑盒測試,從最終用戶視角與產品交互,驗證過程與結果的正確性。相比于單測,這種測試方案更關注代碼模塊集成后的運行效果,更接近用戶體驗,適合作為 UT 的一種補充;
- 其次,必要時還可以使用 Benchmark 等工具對產品的核心算法,或執行頻率較高的代碼片段補充性能測試,保證性能下限。
等等。
依賴管理
依賴管理是一個較為復雜的工程問題(詳見:《NPM 依賴管理的復雜性》),若處理不當,容易引發性能、穩定性等質量問題,因此理論上,無論是商業項目還是開源項目,通常都需要設計一些精細的管理方法,控制三方依賴的使用情況,避免濫用。而對于 NPM Package 形態的產品而言,這類管控措施需要更加嚴格一些 —— 許多開源項目最終提供的使用方式也正恰恰是 NPM Package 形態。
在使用 npm/pnpm/yarn 等包管理器安裝 Package 時,工具會向下遞歸分析并安裝依賴下游的所有子孫依賴,因此對用戶而言,每增加一個 Package 就需要導入該依賴對應的依賴關系圖,最終依賴結構越復雜越是容易出問題,包括:
- 容易出現依賴安裝的性能問題;
- 底層依賴的問題會向上擴散,影響上層應用穩定性;
- 依賴關系圖不穩定,實際安裝版本容易出現大范圍變動,最終影響項目穩定性;
- 容易出現重復依賴,例如 NPM Package 聲明了 lodash@1.2.0 依賴,而用戶的 package.json 中也聲明了 lodash@1.3.0 依賴,那么最終會在用戶項目就需要安裝兩個 lodash 版本;
圖片
毫不夸張的說,NPM Package 的子依賴數量越多,性能與穩定性越差,用戶的使用成本就越高,進而會給人一種強烈的“難用”的感覺。因此在這類場景中務必保持一定的克制,合理設計依賴結構,盡可能降低依賴圖的復雜性,為此可以視情況有意識地采用一些緩解手段,例如:
- 可以將一些相對簡單的代碼片段(例如:escape-string-regexp)直接復制進倉庫中,不必為此額外增加子依賴項。雖然在軟件工程中,“復制”通常為人所不屑,但適當的冗余確實能非常有效降低方案復雜性;
- 也可以將一些簡單依賴與項目代碼整體打包成一個 Bundle 文件,同時將子依賴聲明為 devDependencies 類型,避免在用戶側重復安裝。這種方式本質上就是子依賴以快照方式與項目代碼捆綁發布,雖然也存在一定冗余,但不會受到子依賴版本變化的影響,穩定性與性能相對更好一些;
- 對于復雜依賴,也可以考慮將其設置為 peerDependencies,由用戶自行管理三方依賴版本,雖然這可能會引發其它復雜問題,但能有效避免沖突。
歸根結底,依賴管理容易被忽視但又比較復雜,處理不當會直接影響用戶口碑,推薦讀者擴展閱讀《NPM 依賴管理的復雜性》一文,更深入了解前因后果,以及關于依賴管理的各類最佳實踐。
溝通
商業開發團隊通常會采用一些 IM 軟件(飛書、企業微信、Bear Chat 等)作為主要溝通手段。但在開源項目中很少見到使用 IM 的情況,多數時候更偏向于使用 Github Issue、Disco、Reditt 等工具溝通各類項目細節,如 Bug 反饋、RFC、用法咨詢等。
雖然這類論壇形態的工具遠不如 IM 即時溝通帶來的高效率與便利性,但確實存在許多特質使之成為開源項目的首選,包括:
- 這類工具以 Timeline 形態組織信息流,圍繞特定話題展開討論,使得溝通主題非常聚焦,不容易發散走偏,信噪比會高出許多;
- 足夠開放,甚至幾近透明,任何人都可以極低的門檻進入這類信息環境;同時,非常有利于搜索引擎檢索;
- 歷史記錄更容易追溯,方便新人了解歷史上下文,使得這類 Issue 本身自然形成項目文檔的一部分;
- 開源項目成員來自世界各地,時區對不上,實時溝通意義不大,天然更適合使用異步溝通工具。
因此,推薦在開源環境中優先使用 Github Issue、Disco 等工具作為主流溝通手段,雖然這會部分喪失 IM 實時性帶來的溝通效率。
不過,可能很多同學沒有定期看郵箱或 Github Notice 的習慣,接觸開源項目的前期可能比較難適應這一點,所幸這類工具都提供了非常便利的開放接口體系,可借此設計實現一些自動化工具提升信息流轉效率,例如在 Github Issue 中可以使用 Github Actions 實現:
- 監聽 Issue 變化,回調 IM 接口(例如飛書:feishu-bot-webhook-action)將動態轉發到對應群組,提升實時性;
- 定期匯總活躍 Issue、PR 等,整理成報表發送到 IM 軟件,避免信息阻塞;
- 定期關閉不活躍 Issue,避免信息泛濫;
- 配合 LLM,在創建 Issue 后由 AI 分析內容,自動給出初步反饋;
- 等等,不一而足。
總之,開源環境不推薦使用 IM 作為主要溝通手段,建議切換為 Github Issue 等異步溝通工具,之后配合各類工程化手段提升信息流轉效率即可。
最后
文章內容比較散,雖然聊了很多,但實際上開源與商業的差異遠不止如此,這里只是蜻蜓點水,求個拋磚引玉吧。但最核心的,我認為商業團隊在進軍開源領域之前,務必先停下來,想清楚預期與成本,以及兩者之間文化差異所帶來的解決問題的方式方法上的變化。