[譯] 重建桌面端的 Slack 而不是重寫

重建桌面端的 Slack 而不是重寫

新版本的 Slack 將面向桌面用戶推出,從頭開始構建,更快,更高效,易用性更強。前端

Photo by [Caleb George](https://unsplash.com/@seemoris?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)

廣泛的觀點認爲你不該該從頭重寫代碼,這是個好建議。花時間重寫已經被使用的東西,並不能使咱們的客戶的工做生活更簡單、更愉快、更富有成效。運行代碼也知道一些事情:經過數十億小時的累積使用和數萬個 bug 修復得到的知識是來之不易的。react

不過,軟件代碼具備生命週期。Slack 的桌面版是咱們最老的客戶端,成長於咱們公司早期的快速發展和試驗階段。在那期間,隨着客戶對產品的使用和指望的增加,咱們一直在優化產品的市場適應性,並在不斷的衝刺中跟上客戶的步伐。android

現在,通過五年多的快速發展,Slack 被數以百萬計的人使用,他們所在的公司規模比五年前更大,處理的數據比咱們剛開始時想象的還要多。多少能夠預見,桌面客戶端的基礎開始出現一些內部問題。此外,技術格局已經偏離了咱們在 2012 年末選擇的工具(jQuery,Signals,以及直接操做 DOM),目前的程序趨向於採用可組合的接口和更乾淨的程序抽象。即便咱們盡最大努力保證敏捷,但很明顯,須要進行一些根本性的改變,才能發展桌面應用程序,併爲下一波產品開發作好準備。ios

現有的桌面應用程序的體系結構有許多缺點:git

  1. 手動更新 DOM。 Slack 最初的 UI 是使用 HTML 模板構建的,每當底層數據發生變化時,都須要手動重建這些模板,這使得數據模型和用戶界面很難保持同步。咱們想採用 React,一個流行的 JavaScript 框架,它使這些事情更自動化,更不容易發生潛在錯誤。
  2. 過多的數據加載。 數據模型是「完整的」,這意味着每一個用戶會話都是經過下載與用戶相關的全部內容開始的。雖然理論上很好,但實際上這對於大型工做空間來講過於昂貴,這意味着咱們必須作不少工做才能使數據模型在用戶會話過程當中保持最新狀態。
  3. 多個工做空間有着多個進程。 事實上,當登陸到多個工做區時,每一個工做區都在一個單獨的 Electron 進程中運行 Web 客戶端的獨立副本,這意味着 Slack 使用的內存將比用戶預期的多。

個問題是隨着時間的推移咱們能夠逐步改進的,可是,在一個 Electron 進程中運行多個工做區意味着要改變原始設計的一個基本假設,即一次只運行一個工做區。儘管咱們爲擁有大量空閒工做空間的人們作了一些漸進式的改進 額外的改進,可是真正解決多進程問題意味着從零開始重寫 Slack 的桌面客戶端。github

一個接一個

忒修斯之船是一個思想實驗,這個實驗考慮的是,當一個物體每一部分隨着時間的流逝被逐個替換時,這個物體還會不會是原來的物體。若是一艘船上的每一塊木頭都被替換了,那麼這是同一艘船嗎? 若是一個應用程序中的每個 JavaScript 片斷都被替換了,它仍是同一個應用程序嗎?咱們固然但願如此,由於這彷佛是最好的作法。redux

咱們的計劃是:後端

  • 保持現有的基本代碼;
  • 建立代碼庫的「新」部分,它是不會過期的,並將按照咱們但願的方式工做;
  • 逐步實現Slack的更新,逐步用新代碼替代舊代碼;
  • 定義將在舊代碼和新代碼之間強制執行嚴格接口的規則,以便容易理解它們之間的關係;
  • 並不斷地在現有的應用程序中發佈上述全部內容,用適合咱們新體系結構的更新實現替換舊模塊。

最後一步,對咱們來講也是最重要的一步 —— 是建立一個新版的 Slack,它開始時不完整,但隨着模塊和接口的更新,逐漸向功能完整性發展。架構

去年的大部分時間裏,咱們一直在內部使用這個只有新版本的應用程序,如今它正在推廣給客戶。app

最近

首先要作的是建立新的代碼庫。雖然這只是咱們代碼庫中的一個新子目錄,但它有三個由約定和工具強制執行的重要規則,每一個規則都旨在解決咱們現有應用程序的一個缺點:

  1. 全部 UI 組件都必須使用 React 構建
  2. 全部數據訪問都必須假設成一個懶加載且不完善的數據模型
  3. 全部代碼都必須支持多工做區

前兩條規則,雖然耗費時間,但實現相對簡單。然而,轉向多工做空間架構是一項艱鉅的任務。咱們不能指望每一個函數調用都傳遞一個工做區 ID ,咱們也不能只設置一個全局變量來講明當前哪一個工做區是可見的,由於不管用戶當前正在查看哪一個工做區,程序內部仍然發生着許多事情。

咱們方法的關鍵在於 Redux,咱們已經在用來管理咱們的數據模型。在 redux-thunk 庫的幫助下咱們進行了一些斟酌,咱們可以在 Redux 存儲上模擬幾乎全部的動做或查詢,容許 Redux 圍繞單個工做空間的概念提供一個方便的抽象層。每一個工做空間都有本身的 Redux 存儲區,其中包含全部內容 —— 工做空間的數據、關於客戶機鏈接狀態的信息、用於實時更新的 WebSocket —— 應有盡有。這個抽象圍繞每一個工做區建立了一個概念容器,而沒必要將該容器保存在它本身的 Electron 進程中,這正是過去客戶端所作的。

有了這些認識後,咱們就有了新的架構:

**架構比較。** 右側的新版本。

一個互操做性的遺留問題

關於這一點,咱們有一個咱們認爲可行的計劃和架構,咱們已經準備好經過現有的代碼庫,對全部內容進行更新改造,直到咱們留下一個全新的 Slack。因此只有最後一個問題要解決。

咱們不能隨便用新代碼替換舊代碼;若是沒有某種類型的結構將新舊代碼分開,它們將會無可救藥地糾纏在一塊兒,咱們就永遠不會有新代碼。爲了解決這個問題,咱們在一個名爲 legacy-interop 的概念中引入了一些規則和函數:

  • 舊代碼中不能直接引入新代碼: 只有已經「導出」以供舊代碼使用的新代碼纔可用。
  • 新代碼中不能直接引入舊代碼: 只有已經「調整」以供新代碼使用的舊代碼纔可用。

將新代碼導出到舊代碼很簡單。咱們最初的代碼沒有使用 JavaScript 模塊化或導入導出。相反,咱們將全部內容保存在名爲 TS 的頂級全局變量上。導出新代碼的過程僅僅意味着調用一個輔助函數,該函數使新代碼能夠在全局空間的一個特殊的 TS.interop 部分中使用。例如,TS.interop.i18n.t() 將調用咱們新的、多工做區感知的字符串本地化函數。因爲 TS.interop 命名空間只在咱們的遺留代碼庫中使用,它一次只加載一個工做區,因此咱們能夠在後臺對工做區 ID 做簡單的查詢,而不須要遺留代碼擔憂它。

爲新代碼調整舊代碼就沒有那麼簡單了。當咱們運行舊版本的 Slack 時,新代碼和舊代碼都會被加載,但新版本只會包含新代碼。咱們須要找到一種方法,在適當地利用舊代碼同時不會在新代碼中形成錯誤,而且咱們但願這個過程對開發人員是儘量透明的。

咱們的解決方案稱爲 adaptFunctionWithFallback,它在咱們的舊 TS 對象上運行一個函數路徑,並且若是咱們在僅使用新的代碼的代碼庫中運行,可使用這個函數。這個函數默認爲空操做,這意味着若是底層舊代碼不存在,那麼試圖調用它的新代碼將沒有任何效果 —— 而且不會產生任何錯誤。

有了這兩種機制,咱們可以認真地開始咱們的更新新舊代碼工做。舊代碼能夠在更新時訪問新代碼,新代碼能夠訪問舊代碼直到更新時。正如您所想的那樣,隨着時間的推移,新代碼庫中舊代碼的使用愈來愈少,而且在咱們準備發佈時趨向於零。

將全部組合起來

這個新版本的 Slack 已經推出很長時間了,它包含了過去兩年來一直致力於向客戶不斷推廣的數十人的貢獻。它成功的關鍵是咱們在項目早期採用的增量發佈策略:隨着代碼的更新和功能的重建,咱們將它們發佈給咱們的客戶。Slack 應用程序的第一個「新」部分是咱們的表情符號選擇器,咱們在兩年多前發佈了它 —— 以後是頻道側邊欄,消息窗格和許多其餘功能。

若是咱們等到 Slack 所有被重寫後再發布它,咱們的用戶在發佈一個「爆炸的」替代品以前,就會對錶情符號、消息、頻道列表、搜索和無數其餘功能有着更糟糕的平常體驗。增量發佈容許咱們儘快向客戶交付真正的價值,幫助咱們專一於持續改進,並經過最大限度地減小客戶首次使用的全新代碼量,下降了新客戶的發佈風險。

廣泛的觀點認爲,最好避免重寫,但有時好處也很大,不容忽視。咱們的主要指標之一是內存使用狀況,新版 Slack 提供:

**內存使用率比較。** 右側的新版本。

這些結果驗證了咱們在新版 Slack 中所作的全部工做,咱們期待着繼續迭代,並隨着時間的推移使它變得更好。

在策略規劃的指導下,以明智的發佈爲調和,以及有才華的貢獻者的鼓舞,逐漸的重寫是糾正過去錯誤、爲本身打造一艘嶄新的船的絕佳方式,並使您的用戶的工做生活更簡單,更愉快,更有成效。

敬請期待

咱們熱心於分享更多關於咱們在此過程當中學到的知識。在接下來的幾周,咱們將會在 slack.engineering 上撰寫更多文章:

  • Slack Kit,咱們的UI組件庫
  • 「Speedy Boots」,推出這項工做的原型
  • 易得性,自營銷而不是捆綁營銷
  • Gantry,咱們的應用啓動框架
  • 以及大規模推出的新的客戶端架構

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄。 譯

相關文章
相關標籤/搜索