Vì sao và bằng cách nào Meta chuyển phát triển Android từ Java sang Kotlin
(engineering.fb.com)- Repo Android của Meta là một kho lưu trữ khổng lồ chứa Facebook, Instagram, Messenger, Quest, v.v.
- Hiện đã bao gồm hơn khoảng 10 triệu dòng mã Kotlin
Lý do chuyển sang Kotlin
- Ngoài độ phổ biến, còn có những ưu điểm chính sau
- Nullability: NPE là vấn đề phổ biến tại Meta nên đã có nhiều cách đối phó, nhưng cơ chế xử lý nullability tích hợp sẵn của Kotlin mạnh hơn nhiều và cũng dễ làm việc hơn
- Lập trình hàm: các hàm inline và biểu thức lambda của Kotlin cho phép áp dụng phong cách FP mà không làm giảm tốc độ thực thi
- Mã ngắn gọn hơn
- DSL / Type-safe Builder
- Tất nhiên cũng có một vài nhược điểm không thể bỏ qua
- Việc đưa vào thêm một ngôn ngữ mới đồng nghĩa phải duy trì codebase pha trộn trong thời gian khá dài
- Kotlin tuy phổ biến nhưng vẫn có khoảng cách với Java về mức độ phổ biến. Vì vậy công cụ ít hơn, và nhiều công cụ Kotlin phải tính đến khả năng tương tác Kotlin & Java nên việc triển khai trở nên phức tạp
- Mối lo lớn nhất là thời gian build. Ngay từ đầu họ đã biết thời gian build của Kotlin có thể chậm hơn Java. Build chậm gây ảnh hưởng xấu đến trải nghiệm của lập trình viên
Cách tiếp cận việc migration
- Việc chuyển sang Kotlin vừa đáng ngạc nhiên là khá dễ, vừa rất phức tạp
- Có J2K (Java To Kotlin Converter) nên khá tiện, nhưng vẫn phức tạp
- J2K không phải lúc nào cũng chính xác, và khả năng tương tác giữa Java với Kotlin tạo ra một số trường hợp biên cực đoan
- Có hai lựa chọn cho migration
- Chỉ viết mã mới bằng Kotlin và giữ nguyên phần lớn mã hiện có bằng Java
- Ưu điểm là ít việc hơn, nhưng do trộn hai ngôn ngữ nên trong Kotlin sẽ phải dùng platform type, từ đó có thể phát sinh dereference null pointer và gây crash. Ngoài ra còn có các vấn đề như Java không thể gắn tag nullable cho type parameter (cho đến gần đây), và quy tắc overload của Kotlin có xét việc cho phép null còn quy tắc overload của Java thì không
- Cố gắng chuyển toàn bộ mã in-house sang Kotlin
- Chỉ viết mã mới bằng Kotlin và giữ nguyên phần lớn mã hiện có bằng Java
Cách họ đã migration
- Sau khi cân nhắc cả hai phương án, họ quyết định đặt mục tiêu chuyển toàn bộ mã sang Kotlin
- Ban đầu hơi chậm, nhưng sau khi giải quyết được một số blocker thì đã có thể chuyển đổi trên quy mô lớn
- Hiện nay mỗi ứng dụng Android của Facebook, Messenger và Instagram đều đã có 1 triệu dòng mã Kotlin và tỷ lệ chuyển đổi vẫn đang tăng dần
- Hiện toàn bộ codebase Android có 10 triệu dòng mã Kotlin
Gỡ blocker
- Khi bắt đầu chuyển đổi đã phát sinh một số vấn đề
- Cần cập nhật Redex vì các pattern bytecode
- Một số thư viện nội bộ có thực hiện bytecode transforming vì lý do hiệu năng, nên điều này không hoạt động với Kotlin
- Nếu bên trong đã tối ưu hóa nhiều, có thể sẽ gặp các vấn đề tương tự
- Các công cụ hiện có cũng phát sinh vấn đề
- Do code review/wiki v.v. chưa hỗ trợ highlight cú pháp Kotlin nên họ đã cập nhật Pygments
- Họ cũng phát triển riêng Ktfmt, một formatter cho Kotlin
Tăng tốc migration
- Khi công cụ đã sẵn sàng thì cũng là lúc có thể bắt đầu chuyển mã sang Kotlin
- Nhưng mỗi đợt migration lại có nhiều mã boilerplate phải xử lý thủ công
- J2K là công cụ tổng quát nên không hiểu ngữ cảnh của mã. Vì vậy cần rất nhiều thao tác thủ công
- Ví dụ như các quy tắc test JUnit
- Vì thế họ đặt J2K ở giữa một pipeline 3 bước
- Bước đầu tiên là lấy một package Java và chuẩn bị cho việc chuyển sang Kotlin. Bao gồm sửa lỗi và các chuyển đổi cần thiết cho công cụ nội bộ
- Bước thứ hai là chạy J2K tự động bằng script
- Bước thứ ba là hậu xử lý các file Kotlin mới. Đây là bước quan trọng nhất. Thực hiện các tác vụ như refactor tự động/linter ở chế độ headless
- Tự động hóa không giải quyết được mọi vấn đề, nhưng họ đã ưu tiên xử lý trước các trường hợp phổ biến
Những điều rút ra từ migration Kotlin
- Độ dài mã đã giảm
- Tốc độ thực thi được giữ nguyên
- Kích thước bản build không phải vấn đề
- Đã giải quyết được vấn đề thời gian build tăng lên: sử dụng KSP (Kotlin Symbol Processing API)
4 bình luận
Cảm ơn bạn đã chia sẻ bài viết hay. Cá nhân tôi khi mới dùng Kotlin thì thấy có nhiều điểm tiện hơn Java nên cũng rất thích, và từng nghĩ rằng sau này Kotlin có thể sẽ trở thành xu hướng chủ đạo. Nhưng dùng một thời gian rồi thì tôi thấy vẫn có khá nhiều điểm mà Java tốt hơn.
Tôi nghĩ Android chuyển sang Kotlin thì không sao, nhưng ở các môi trường khác (như Spring chẳng hạn..) nếu tính ổn định là quan trọng thì hiện tại Java vẫn có vẻ là lựa chọn tốt hơn.
Bạn có thể cho biết chỉ một ví dụ về tính ổn định được không? Vì kinh nghiệm của tôi còn ít nên vẫn chưa gặp những trường hợp như vậy, nên tôi rất tò mò.
Có lẽ vì JetBrains phát hành bản mới dồn dập, nhưng số lượng bản vá lỗi compiler lại nhiều một cách đáng ngạc nhiên.
https://github.com/JetBrains/kotlin/releases/tag/v1.7.20
Cũng khá thường xuyên xảy ra trường hợp chính compiler bị crash (trường hợp này còn đỡ vì xảy ra trước khi phát hành), và cả trong sản phẩm đầu ra cũng thỉnh thoảng có lỗi.
Hơn nữa, với Android thì bộ tối ưu hóa bytecode R8 cũng còn phụ thuộc vào phiên bản Kotlin.
Bởi vì có tính năng tối ưu hóa dành riêng cho mã Kotlin, nhưng lại không có tài liệu bảng tương thích chính thức cho phần này (Android Gradle Plugin : Kotlin Version)..
Vì vậy, cần cẩn thận khi thay đổi phiên bản Kotlin trong dự án Android;;;
Báo cáo lỗi liên quan: https://issuetracker.google.com/issues/207397158
Cảm ơn bạn. Hóa ra có một thế giới vừa sâu vừa rộng.