押錯寶!一次性將百萬行代碼從 Flow 遷移至 TypeScript

作者 | Caleb Meredith,Andrew Wang       

譯者 | 彎月      責編 | 歐陽姝黎

出品 | CSDN(ID:CSDNnews)

以下爲譯文:

最近,爲了緊跟時代發展的腳步,我們決定引入新技術和模式,並將我們的代碼庫遷移到 TypeScript。

上帝說:要有光

這個故事要從最初我們創建代碼庫說起:

commit 681429d64232f44c6af1a1d838b91fe39d52edb0
Author: Howie <howie@Howie-Lius-MacBook-Air.local>
Date:   Sun Apr 22 15:55:06 2012 -0700
beginning state

2012 年,在服務器端使用 JavaScript 的想法仍然相對較新。記得那一年蘋果發佈了 iPhone 5,鳥叔的一首《江南 Style》紅遍全世界,成爲互聯網上首個播放量超過 10 億的視頻。而我們的創始人則利用首代技術棧開始了 JavaScript 以及 JavaScript 生態系統的冒險之旅。

在當初選擇的技術中,有些已經相當成熟(比如 Node.js、Express 和 JavaScript 本身),而有些則仍然很年輕(比如 Backbone、Underscore.js、EJS 和 jQuery)。在過去的九年中,通過不斷的重構工作,即使公司已經發展到一百多名工程師,代碼量也超過了一百萬行,但我們的代碼庫仍然是統一用相對現代的 JavaScript 語言編寫而成的。

核心代碼庫的展示:擴展記錄

下面,我來舉個例子。我們的代碼庫有一個擴展記錄的功能,是於 2012 年引入的。此後,核心功能幾乎保持不變,但代碼的結構已發生了很大變化。如下是過去 9 年中擴展記錄功能的前 50 行代碼:

我們可以通過這段代碼大致瞭解一下我們的代碼庫:

通過上述擴展記錄的代碼發展歷史,相信你可以看出,我們願意大規模地重構我們的代碼庫。我們認爲無法維護的代碼是不可避免的。我們認爲保證代碼質量是全體工程師的責任。比如,上述擴展記錄的代碼變化就是由不同團隊的不同工程師在過去的幾年中付出的心血。

接下來,我詳細介紹一下,我們將代碼庫遷移至 TypeScript 的整個過程。

押錯了寶

最初我們的代碼庫採用了原始的 JavaScript。靜態類型的好處就不用我再重複了。2016 年,在我們研究 JavaScript 的增量類型時,Flow 和 TypeScript 這兩匹黑馬脫穎而出,一時之間難分上下。我們之所以選擇 Flow,是因爲在當時它對 React 的支持更好。

時至 2019 年,我們發現自己押錯了寶。TypeScript 的發展速度遠遠超過了 Flow,而且 TypeScript 在功能、IDE 支持和社區資源等方面也展現出了明顯的優勢。於是,我們決定將代碼庫遷移至 TypeScript。

指導原則

如今,我們的代碼庫包含一百多萬行 JavaScript 代碼。考慮到規模以及複雜性,我們決定主要圍繞以下三個原則開展遷移工作:

大規模的遷移

一般情況下,TypeScript 的遷移都是增量逐步完成的,即逐個類型、逐個文件進行。由於我們的大部分代碼庫已經轉換成了 Flow 類型,因此我們採用了另一種方法:一次性將整個代碼庫遷移至 TypeScript,這是一次大規模的遷移。

第一步,我們編寫了一個小工具,完成了代碼的純機械轉換。雖然有一些 Flow 轉換爲 TypeScript 的工具,但我們還是編寫了自己的工具,爲的是滿足我們的一些特定需求:

我們的工具最有技術含量的一個功能是,它處理未聲明類型的函數參數的方式。例如,請看以下示例,參數 x 沒有指定類型:

function f(x) {
return x * 2;
}

在這種情況下,Flow 可以根據上下文推斷 x 是一個數字。但是,TypeScript 不會,而且在嚴格模式下還會報錯。

我們的某些代碼利用了 Flow 的這一功能。由於我們的指導原則之一是 “不能降低類型安全”,因此所有帶有 any 參數的函數都需要改。我們的工具通過執行 flow type-at-pos,使用 Flow 推斷的類型來標註每個未聲明類型的函數參數。事實證明,大多數情況下,Flow 可以推斷出 any 的類型。

擼起袖子開幹

我們的工具完成了大部分必要的改動,總共修改了 3300 個文件。但是,仍然還有很多無法自動處理的地方。tsc 運行的結果顯示,1600 多個文件引發了 15000 多個 TypeScript 錯誤,我們必須手動修改了。

幸運的是,我們找到了一位類型系統專家,他花了大約一個星期的時間,專門坐下來修復 TypeScript 的錯誤。整個過程枯燥又乏味,但是總好過使用 // @ts-ignore 搞亂代碼。看來,我們樹立指導原則還是很有必要的,我們不能讓現有 Flow 代碼的類型安全性倒退(原則 2:不能降低類型安全),但是爲了提高類型安全性而積極地重構實際上也很危險(原則 1:不能破壞產品)。由於首要原則是不能破壞產品,因此有時添加 // @ts-ignore 是最好的解決方案。

所有這些工作都是在單獨的分支上完成的。在分支通過類型檢查和自動化測試之後,修改就可以反映到主分支上了。

由於手動檢查 1600 多個文件(以及 4.8 萬行代碼)的改動不太現實,因此我們結合使用了很多工具:

2019 年 10 月底,我們凍結了主分支,重新運行了代碼自動轉換,併合並了 TypeScript 分支。從那以後,我們就正式邁入了 TypeScript。

塵埃落定

由於我們的第三項原則是:儘量保持簡單,因此其他的改進想法都被推遲了。我們的一位工程師撰寫了一份大約包含 20 種修改思路的文檔。令我們十分自豪的是,在過去的兩年中,我們的工程師們靠自己的努力實現了一半以上的修改方案。下面,僅舉幾個例子:

總結

從長遠來看,低代碼 / 無代碼應用程序開發平臺是一種趨勢,我們相信在未來幾十年中我們還將在該領域繼續創新。我們非常重視代碼的質量,並希望不斷髮展我們的代碼庫。

TypeScript 遷移是我們的代碼庫所經歷的一次最大的重構,但肯定不是最後一次。

鏈接:https://medium.com/airtable-eng/the-continual-evolution-of-airtables-codebase-migrating-a-million-lines-of-code-to-typescript-612c008baf5c

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/MzejJ_m-SGLxw_3H_X92mQ