Categories
程式開發

代碼行數減少30-90%!多鄰國從Java遷移到Kotlin的奇妙體驗


英文學習 App Duolingo(多鄰國)的 Android 版最初是使用 Java 開發的,並一直沿用了 5 年。兩年後,它變成了 100% 的 Kotlin App!從代碼可維護性和開發者滿意度方面來看,這次遷移是一個巨大的成功。網上已經有很多 Kotlin 的學習資源,所以在這篇文章中,我們將重點介紹如何將一個有百萬用戶的 App 遷移到 Kotlin。

代碼行數減少30-90%!多鄰國從Java遷移到Kotlin的奇妙體驗 1

為什麼選擇 Kotlin

2018 年初,我們開始考慮使用 Kotlin,當時 Android 對 Kotlin 的支持還不到一年時間,而且不知道 Kotlin 會像現在這樣流行,也不知道它會取代 Java 成為 Android 的開發首選語言。

我們當時的主要預期:

  • 生產力。 Kotlin 比 Java 要簡潔得多,編寫 Kotlin 代碼速度更快,維護起來也更容易。它與 Java 的無縫互操作性以及添加語言新特性的方式讓 Android 開發者可以輕鬆上手。
  • 穩定性。我們的代碼庫歷史記錄中有 100 多次提交都與“修復 NullPointerException 問題”有關,這些問題都來自 Java 代碼。 Kotlin 的 Null 安全特性避免了大部分 NullPointerException,讓我們在代碼評審期間可以更多地關注其他問題。
  • 開發者滿意度。 Stack Overflow 發布的 2018 年度開發者編程語言報告表明,Kotlin 是開發者最喜愛的編程語言之一,僅次於 Rust。我們的開發人員已經對公司內部另外兩個主要平台的語言升級做出了積極的反應:在 iOS 應用程序中使用 Swift,以及使用 TypeScript 完全重寫了 duolingo.com。

當然,遷移也存在一些風險,主要是開發人員的時間成本問題。另一個擔憂是 Kotlin 是否會像 CoffeeScript 一樣,最後可能會被它的“影子”語言打敗。

最後,我們的 Android 開發人員一致認為,Kotlin 的好處很有價值,足以證明使用 Kotlin 開發新代碼是合理的,儘管我們還沒有準備好全面遷移所有的代碼。

讓開發人員跟上進度

Duolingo 的所有 Android 開發人員每兩週開一次例會,討論最近和即將做出的平台變更和非正式的事後分析,並進行問答。前期的會議專門介紹 Kotlin,內容主要基於 Kotlin 官方語言指南、Kotlin Koans、Android 官方文檔和 MindOrks 備忘單,等等。

然後,每個 Android 開發人員都分配到一些 Java 代碼,負責將它們遷移到 Kotlin。我們讓相對有 Kotlin 經驗的開發人員擔任“Kotlin checker”角色,讓他們在代碼評審期間分享最佳實踐。這個角色的人數逐步增加,直到所有的 Android 開發人員都包含在內。

Kotlin 相關的開發工具

從一開始,我們就對 Kotlin 工具進行容器化,並在代碼預提交和 GitHub 拉取請求狀態檢查中強制使用,以此來確保代碼的一致性。

我們使用 detekt、IntelliJ Inspection、Android lint 和我們自己開發的基於正則表達式的 Splinter 來檢查所有的 Kotlin 代碼。

在代碼自動格式化方面,我們在公司範圍內使用了 ktlint,將其作為代碼預提交 hook 的一部分。另一個工具是 IntelliJ Formatter,不過我們發現它在 Docker 中運行會慢一些。

在將 Java 代碼減少到只有 10% 的時候,我們從 CI 管道中移除了 PMD、SpotBugs 和大部分檢查工具。繼續使用這些 Java 工具將會降低我們的開發速度,而且不會為我們提供太多的價值。

轉換 Java 代碼

為了讓代碼轉換的評審工作盡可能輕鬆,我們建議每個源文件的拉取請求至少包含三個單獨的提交:

  • 運行 IDE 的自動轉換器。這個提交會造成代碼行錯亂,但不需要仔細檢查,因為對於運行時來說通常是安全的,儘管可能會引入編譯時錯誤。
  • 修復編譯錯誤。這些修復通常很容易進行,例如,在必要時添加 @JvmStatic 註解。
  • 重構。開發人員需要重構代碼,讓代碼更符合 Kotlin 的習慣,例如使用 sumBy 而不是 for 循環。

我們發現,將 Java 文件轉換成 Kotlin 後,行數平均減少了 30% 左右,在某些情況下甚至減少了 90%!

雖然移植代碼對於我們的 Android 平台工程師來說不是問題,但對於我們的產品團隊來說可能會相對困難。我們鼓勵產品團隊的開發人員在空閒時間遷移經常修改的代碼,還通過每天的排行榜比賽來游戲化這個過程。最終,產品團隊的開發人員擔起了一半的工作量。

絆腳石

Kotlin 的工俱生態系統比 Java 的要小得多。儘管如此,它已經足夠滿足我們的需求了。

我們偶爾還是會遇到NullPointerException 和IllegalArgumentException,這些異常來自第三方Java 依賴庫(比如Android 框架本身),它們沒有遵循最佳實踐,沒有使用可空註解,以至於Kotlin 編譯器無法知道某些方法的參數或返回值可以為空。隨著谷歌給它們的公共 API 加入註解,這種情況得到了改善。

不過,Kotlin 仍然缺乏對一些 Java 特性的原生支持,包括不太常見的超類靜態受保護方法調用和神秘的超類構造函數調用,但這類問題很容易解決。

結果

在 2018 年初引入 Kotlin 之前,我們的 Android 代碼庫的代碼行數每年增長 46%。兩年過去了,我們加入了很多新特性,活躍的貢獻者數量增加了一倍多,而我們的代碼庫現在幾乎只和以前一樣大!

根據 NPS 數據,這一次 Android 開發人員的滿意度增加了 129 個點,大多數開發人員認為是採用 Kotlin(以及我們的工具)起到了主要作用。 NPS 的數據具體為:

https://en.wikipedia.org/wiki/Net_Promoter

我們現在也同時使用 Python 和 Java 作為後端服務開發語言,這幾乎不需要額外的工作量,因為我們可以重用現有服務中的 Java 代碼和 Android 代碼庫中的 Kotlin 工具。

總的來說,在遷移到 Kotlin 之後,我們感到非常開心。我們也很高興能夠看到它在我們的公司內部和整個軟件行業中的採用率不斷增長!

英文原文

Migrating Duolingo’s Android App to 100 Kotlin