เหตุผลและวิธีที่ Meta เปลี่ยนการพัฒนา Android จาก Java มาเป็น Kotlin
(engineering.fb.com)- Repo ฝั่ง Android ของ Meta เป็นรีโปขนาดมหึมาที่รวม Facebook, Instagram, Messenger, Quest ฯลฯ เอาไว้
- ปัจจุบันมีโค้ด Kotlin มากกว่า 10 ล้านบรรทัดแล้ว
เหตุผลที่เปลี่ยนมาใช้ Kotlin
- นอกเหนือจากความนิยมแล้ว ยังมีข้อดีสำคัญดังนี้
- Nullability : NPE เป็นปัญหาร่วมกันภายใน Meta มานาน จึงมีการรับมือหลายรูปแบบ แต่การจัดการ Nullability ที่มีมาในตัวของ Kotlin นั้นทรงพลังและทำงานได้ง่ายกว่ามาก
- การเขียนโปรแกรมเชิงฟังก์ชัน : inline function และ lambda expression ของ Kotlin ช่วยให้ใช้สไตล์ FP ได้โดยไม่ทำให้ความเร็วในการรันลดลง
- โค้ดสั้นกว่า
- DSL / Type-safe Builder
- แน่นอนว่าก็มีข้อเสียเล็กน้อยที่มองข้ามไม่ได้
- การนำภาษาใหม่เข้ามาหมายความว่าจะต้องคงโค้ดเบสแบบผสมไว้ค่อนข้างนาน
- แม้ Kotlin จะได้รับความนิยม แต่ก็ยังมีช่องว่างเมื่อเทียบกับ Java ในแง่ความแพร่หลาย ทำให้มีเครื่องมือน้อยกว่า และเครื่องมือ Kotlin จำนวนมากต้องคำนึงถึงการทำงานร่วมกันระหว่าง Kotlin และ Java จึงทำให้การพัฒนาซับซ้อนขึ้น
- ความกังวลที่ใหญ่ที่สุดคือเวลา build โดยรู้อยู่แล้วตั้งแต่แรกว่าเวลา build ของ Kotlin อาจช้ากว่า Java และเวลา build ที่ช้าก็ส่งผลไม่ดีต่อประสบการณ์ของนักพัฒนา
แนวทางที่ใช้ในการย้ายระบบ
- การย้ายไป Kotlin นั้นทั้งง่ายอย่างน่าประหลาดใจ และซับซ้อนมากในเวลาเดียวกัน
- มี J2K(Java To Kotlin Converter) จึงช่วยให้สะดวกขึ้น แต่ก็ยังซับซ้อนอยู่ดี
- J2K ไม่ได้แม่นยำเสมอไป และการทำงานร่วมกันระหว่าง Java กับ Kotlin ก็สร้างกรณีสุดขั้วบางอย่างขึ้นมา
- มีตัวเลือกในการย้ายอยู่ 2 แบบ
- เขียนเฉพาะโค้ดใหม่เป็น Kotlin และปล่อยให้โค้ดส่วนใหญ่ยังคงเป็น Java ต่อไป
- ข้อดีคืองานน้อยกว่า แต่การใช้สองภาษาปะปนกันทำให้ Kotlin ต้องใช้ platform type ซึ่งอาจนำไปสู่การ dereference null pointer และทำให้แอป crash ได้
อีกทั้ง Java ก็ไม่สามารถแท็ก type parameter ให้เป็น Nullable ได้ (จนกระทั่งเมื่อไม่นานมานี้) และกฎการ overload ของ Kotlin พิจารณาว่า null ได้หรือไม่ แต่กฎการ overload ของ Java ไม่ได้พิจารณาเรื่องนี้
- ข้อดีคืองานน้อยกว่า แต่การใช้สองภาษาปะปนกันทำให้ Kotlin ต้องใช้ platform type ซึ่งอาจนำไปสู่การ dereference null pointer และทำให้แอป crash ได้
- พยายามแปลงโค้ดภายในทั้งหมดให้เป็น Kotlin
- เขียนเฉพาะโค้ดใหม่เป็น Kotlin และปล่อยให้โค้ดส่วนใหญ่ยังคงเป็น Java ต่อไป
วิธีที่ใช้ในการย้ายจริง
- พิจารณาทั้งสองทางเลือกแล้ว และตัดสินใจตั้งเป้าแปลงโค้ดทั้งหมดเป็น Kotlin
- ตอนแรกค่อนข้างช้า แต่หลังจากแก้ blocker บางจุดได้แล้ว ก็สามารถแปลงได้ครั้งละมาก ๆ
- ปัจจุบันแอป Android ของ Facebook, Messenger และ Instagram ต่างก็มีโค้ด Kotlin อย่างละ 1 ล้านบรรทัด และอัตราการแปลงก็ยังเพิ่มขึ้นเรื่อย ๆ
- ตอนนี้โค้ดเบส Android ทั้งหมดมีโค้ด Kotlin อยู่ 10 ล้านบรรทัด
Unblocking
- หลังเริ่มแปลงในช่วงแรก ก็พบปัญหาบางอย่าง
- ต้องอัปเดต Redex เพราะรูปแบบ bytecode
- ไลบรารีภายในบางตัวมีการทำ bytecode transforming เพื่อประสิทธิภาพ ซึ่งใช้กับ Kotlin ไม่ได้
- หากภายในองค์กรมีการทำ optimization ไว้มาก ก็มีแนวโน้มจะเจอปัญหาคล้ายกัน
- เครื่องมือเดิมก็มีปัญหาเช่นกัน
- ใน code review / wiki ไม่มีการเน้นไวยากรณ์ของ Kotlin จึงต้องอัปเดต Pygments
- ได้พัฒนา Ktfmt ซึ่งเป็น formatter ของ Kotlin แยกขึ้นมาเอง
การเร่งความเร็วการย้ายระบบ
- เมื่อเครื่องมือพร้อมแล้ว ก็พร้อมที่จะแปลงโค้ดเป็น Kotlin
- แต่การย้ายแต่ละครั้งยังต้องทำ boilerplate ด้วยมือจำนวนมาก
- J2K เป็นเครื่องมือทั่วไป จึงไม่ได้เข้าใจตัวโค้ดเอง ทำให้ยังต้องทำงานด้วยมืออีกมาก
- ตัวอย่างเช่นกฎของการทดสอบ JUnit
- จึงนำ J2K ไปวางไว้ตรงกลางของ pipeline 3 ขั้นตอน
- ขั้นแรก นำ Java package หนึ่งชุดมาเตรียมให้พร้อมสำหรับการแปลงเป็น Kotlin ทั้งการแก้บั๊กและการแปลงที่จำเป็นต่อเครื่องมือภายใน
- ขั้นที่สอง รัน J2K แบบอัตโนมัติผ่านสคริปต์
- ขั้นที่สาม post-process ไฟล์ Kotlin ใหม่ ซึ่งเป็นส่วนที่สำคัญที่สุด โดยทำ auto-refactoring / linter ฯลฯ ในโหมด headless
- แม้ระบบอัตโนมัติจะไม่สามารถแก้ได้ทุกปัญหา แต่ก็ช่วยจัดลำดับความสำคัญของปัญหาที่พบบ่อยได้
สิ่งที่ได้เรียนรู้จากการย้ายไป Kotlin
- โค้ดสั้นลง
- ความเร็วในการรันยังคงเดิม
- ขนาด build ไม่ใช่ปัญหา
- แก้ปัญหาเวลา build ที่ยาวขึ้นได้ด้วย KSP(Kotlin Symbol Processing API)
4 ความคิดเห็น
ขอบคุณที่แชร์บทความดี ๆ ครับ ส่วนตัวตอนเริ่มใช้ Kotlin ใหม่ ๆ ก็ชอบมาก เพราะมีหลายจุดที่สะดวกกว่า Java และเคยคิดว่าในอนาคต Kotlin น่าจะกลายเป็นกระแสหลัก แต่พอใช้ไปสักพักก็พบว่ายังมีหลายด้านที่รู้สึกว่า Java ดีกว่าอยู่เหมือนกัน
ผมคิดว่า Android จะไปทาง Kotlin ก็ไม่เป็นไร แต่ในสภาพแวดล้อมอื่น ๆ (เช่น Spring เป็นต้น) ถ้าความเสถียรเป็นเรื่องสำคัญ ตอนนี้ Java ก็น่าจะยังเป็นตัวเลือกที่ดีกว่า
ช่วยยกตัวอย่างเรื่องความเสถียรให้สักกรณีหนึ่งได้ไหมครับ? ผมยังมีประสบการณ์ไม่มากเลยยังไม่เคยเจอกรณีแบบนั้น เลยสงสัยมากครับ
ไม่แน่ใจว่าเป็นเพราะ JetBrains ปล่อยรีลีสถี่มากหรือเปล่า แต่มีการแก้บั๊กของคอมไพเลอร์เยอะกว่าที่คิด
https://github.com/JetBrains/kotlin/releases/tag/v1.7.20
บางครั้งตัวคอมไพเลอร์เองก็ล่มได้เหมือนกัน (อันนี้ยังพอรับได้เพราะเกิดก่อนปล่อยใช้งาน) และในผลลัพธ์ที่คอมไพล์ออกมาก็มักมีบั๊กปะปนอยู่เป็นระยะ
ยิ่งไปกว่านั้น ในกรณีของ Android นั้น R8 bytecode optimiser ก็เลือกเวอร์ชัน Kotlin ด้วย
เพราะมีฟีเจอร์การเพิ่มประสิทธิภาพเฉพาะสำหรับโค้ด Kotlin แต่กลับไม่มีเอกสารตารางความเข้ากันได้อย่างเป็นทางการสำหรับเรื่องนี้ (Android Gradle Plugin : Kotlin Version)..
ดังนั้นตอนเปลี่ยนเวอร์ชัน Kotlin ในโปรเจกต์ Android จึงต้องระวังให้มาก;;;
รายงานบั๊กที่เกี่ยวข้อง: https://issuetracker.google.com/issues/207397158
ขอบคุณมากครับ/ค่ะ มีโลกที่ทั้งลึกและกว้างอยู่จริง ๆ..