สรุป best practices สำหรับ modularized app ที่มี dynamic features จากทาง LINE กันเถอะ
สรุป session ที่ชื่อว่า Best practices for a modularized app with dynamic features จากงาน LINE Developer Day 2020 ในวันที่ 26 พฤศจิกายน 2020
เท่ากับเราดองการสรุป session นี้เป็นเวลาเกือบ 3 เดือนด้วยกัน
ลิ้งของ session นี้สามารถตามไปส่องได้ที่นี่
อันนี้ส่วนเนื้อหาย้อนหลังทั้งหลาย
ในแต่ละทีมที่ออฟฟิศนอกจากจะเปลี่ยนเป็น MVVM กันแล้ว ยังเริ่มทำเป็น modularized app เพื่อให้สามารถนำไปใช้ในแอพหรือโปรเจกอื่นๆได้ง่ายขึ้นอีกด้วย รวมถึงการเอาไปทำ dynamic features ก็ได้อีกเช่นกัน เลยทำให้เราสนใจ session นี้เป็นพิเศษเลยหล่ะ
จริงๆในวันงานเราก็ทำงานไป ฟังไป สรุปแบบสั้นสุดๆไปว่าแต่ละ session พูดเรื่องอะไรกันนะ แล้วมา list ว่าจะทำสรุป session ไหนเพิ่มเติมนะ
ว่าแต่ sesssion นี้จะมีอะไรบ้าง มาอ่านกันเลยดีกว่าจ้า
ปล. เนื่องจากเจ้าของบล็อกไม่ค่อยคล่องภาษาอังกฤษเท่าไหร่ เลยจะอธิบายเท่าที่เข้าใจเนอะ
Introduction
LINE Chat ออกมาในปี 2011 สามารถส่งข้อความและสติ๊กเกอร์ได้ และเพิ่ม feature ต่างๆ เช่น home และ timeline ในปี 2012, video calls ในปี 2013, LINE Premium Call ในปี 2014 และอื่นๆ แน่นอนว่าเวลาผ่านไปโปรเจกก็บวมตามการเติบโตของ product
ในปี 2019 project android ของตัวแอพนี้มีโค้ดทั้งสิ้น 1.4 ล้านบรรทัด มี 33 module และมี developer ที่ทำโปรเจกนี้ถึง 100 คนด้วยกันใน main repository
เกิด issue เกี่ยวกับ structure มีโค้ด 66% ที่อยู่ใน module app ซึ่งเป็น module ที่มีขนาดใหญ่มากถึง 248 MB ส่งผลให้โปรเจกบิ้วช้ามากๆ โดยการบิ้วครั้งแรกใช้เวลาถึง 17 นาที (ทำไมคุ้นๆเหมือนแอพๆนึงเลย) และ structure ก็อ่านยากเสียเหลือเกิน
ดังนั้นทางทีมจึงตัดสินใจ ใช้ modularize app module เพื่อแก้ปัญหานี้ (และตอนนี้ก็ยังคงทำอยู่) ในระยะ 1 ปีที่ผ่านมา ได้ผลลัพธ์ดังนี้
- มี 20 modules ที่ถูกสร้างขึ้น
- มีโค้ดใหม่ 270,000 บรรทัด ที่มาจาก module ต่างๆ
- โค้ด 10% ถูกย้ายไปยัง module ต่างๆ (อันนี้เขาพูดว่าโค้ดที่ยังไม่ได้ย้าย จาก 66 เหลืออีก 56 อ่ะ)
Android Modularization
วิธีการและแนวทางการนำไปใช้ในโปรเจก ตามทฤษฎีจะแบ่งออกเป็น 4 ประเภท คือ
- Android Library Module : มี android framework dependency
- Dynamic Feature Module : ใน session นี้จะพูดถึงตัวนี้เป็นหลักเนอะ
- Application Module : อันนี้มีได้แค่ 1 module ต่อ 1 application เท่านั้น
- Pure Java/Kotlin Library Module : อันนี้เป็น library module ที่ไม่มี android framework dependency เนอะ
1 application มีได้แค่ 1 app module เท่านั้น และสามารถมีได้หลายๆ library และ features module และสามารถแบ่งย่อย module ออกเป็นอีก 2 แบบ คือ Android Library Module และ Pure Java/Kotlin Library Module
ความแตกต่างระหว่าง library module และ feature module
- Library module เป็น module ที่ standalone, สามารถนำไป reues ไปใช้ได้ในหลายๆแอพ, เน้นการพัฒนาเป็น library, และ สามารถ improve เวลาบิ้วลงได้ ถ้ามีหลายๆ module โดยใช้ประโยชน์จาก parallel gradle build feature
- Dynamic feature นั้นมีความแตกต่างจาก library module เพียงเล็กน้อย และมันไม่ได้ถูก design มาเพื่อ reuse ไปใช้ที่อื่นได้ และถูกขึ้นอยู่ใน module app ช่วย improve เวลาให้บิ้วเร็วขึ้นได้เช่นกัน และมันช่วย delay ในการ delivery ได้ เป็นส่วนสำคัญในการลดขนาดของ apk เพื่อส่งมอบแอพให้กับ user
How to approach the modularization
มี 2 แนวทางคือ Top-down และ Bottom-up
- Top-down จาก class diagram ข้างในมี module app ที่มี class ที่ depend กับอันอื่นๆ และสำหรับแนวทางนี้ เราจะสร้าง library module เป็นอันดับแรก และเราจะ focus สิ่งที่เป็น higher hierarchy independence utility และ library class และสามารถย้าย class ไปยัง library module เพื่อนำไปใช้ในหลายๆ class และหลายๆ module แต่ต้อง refactor จึงเป็น impact ที่ใหญ่มาก
- Bottom-up เป็นแนวทางที่ใช้ library module โดยสร้าง dynamic feature แทน library module ในการ split feature ที่ผูกอยู่ใน module app ออกเป็นส่วนย่อยๆ และสามารถย้าย Class ไปใน dynamic feature module เพราะเราจะ focus ที่ feature นั้น feature เดียว และมี impact น้อยกว่า
ตารางความแตกต่างและสรุปจ้า
ข้อเสียของการทำ dynamic feature module
- Performance : มีการ install feature ลงเครื่องๆนึงมากกว่า 50 ครั้ง ทำให้เกิด performance issue และ มากกว่า 10 ครั้งในการถอดออกในการส่งมอบ module เพิ่มจำวนการ download และติดตั้ง
- Testability : update app bundle ลง Play store เพื่อทำการ test ทำให้ใช้เวลาในการเทสนาน
- Known Issues : มี issue ต่างๆ เช่น data binding และ unit test ซึ่งจะพูดต่อไปในส่วน best practice
- Dependency Structure : ทำให้มี dependency ที่ไม่จำเป็นทำให้ developer เกิดความผิดพลาดได้จากการสร้าง some high coupling และไม่มี cohesion code ทำให้ maintain ได้ยากขึ้น
ดังนั้น
ใช้ dynamic feature module เมื่อจำเป็นเท่านั้น
และมี decision making เป็น flowchart ให้เราดู
Best Practice
มี 5 ตัวอย่างด้วยกัน มีประโยชน์ในการนำไปปรับใช้ใน Android Project ของเราได้
1. Use Service Locator : เพื่อ resolve dependency challenge ระหว่าง module app และ dynamic feature เพราะ module app ของเรานั้น ไม่สามารถเข้าถึง dynamic feature module ได้โดยตรง ดังนั้นเราจะใช้ reflection api ในการแก้ปัญหา
เช่น สร้าง surface provider interface และ implement class ใน dynamic module โดยการนำมา inject เพื่อใช้ในแอพ
เขาได้กล่าวถึง session เมื่อปี 2019 ที่นำมาอ้างอิงถึงในส่วนนี้ด้วย
2. Set ProGuard rule for data binding : data binding มี issue ใน dynamic feature module เช่น ปัญหาในการ auto generate class ดังนั้นใช้ app manager function เข้าช่วย
3. Decide conventions of resources : ปัญหาไฟล์ resource ที่ชื่อเดียวกัน แต่อยู่คนละ module แล้วดัน conflict กัน? แก้โดยใส่ชื่อ module name เป็น prefix หน้าชื่อ resource ใน module นั้นๆ กับสร้าง common resource library ออกมา (ในทีมทำประมาณนี้อยู่ในการทำ modular app นะ)
4. Manage Library version at root directory : ใช้ library ในหลายๆ module แต่เจอปัญหาเรื่อง version ของ library นั้น conflict กันและทำให้บิ้วไม่ได้ แก้โดยการจัดการ library version ไว้ใน root directory (อันนี้ทีมก็ทำอยู่ อัพเดตที่เดียว ได้ทั้งโปรเจก)
5. Separate interface and implementation at different modules : อันนี้กล่าวถึง benefit ต่างๆ ที่ได้จากการทำ dynamic feature module เนอะ
- ลดการใช้ dependency ที่ไม่จำเป็น
- ง่ายต่อการ convert dynamic feature module ไปยัง Android library module
- สามารถนำ feature ที่ทำไว้ไป reuse กับแอพอื่นๆได้
Implementation Example
โปรเจกมี 1 app module และ 2 feature modules ก็จะเป็นประมาณนี้เนอะ
สรุปรวม
- ให้ทำ modular app โดยเร็วที่สุด
- ใช้ dynamic feature module เมื่อจำเป็นเท่านั้น
- หลีกเลี่ยงมาใช้ class หรือ resource จาก module app ใน feature module
More Information
เขาอ้างอิงถึง 2 sessions ในงานเดียวกัน ที่มีความเกี่ยวโยงกัน
- Sharing experience of adapting AppBundle on LINE Android CI.
- Enforcing code conventions with Lint
ซึ่ง session ด้านบนน้านน สั้นมากๆ ประมาณ 10 นาทีเอง
สรุปสั้นสุดโดยเราเอง
อ่ะยังไม่ต้องไปถึง dynamic feature module นะ เพราะตามหลักการคือถ้า user จะเล่น feature นั้นๆจึงจะโหลดเข้าแอพมาเพิ่ม แล้วส่วนใหญ่ก็ไม่ค่อยมีเหตุแบบนี้เท่าไหร่นัก
ดังนั้นจึงอยากชิญชวนให้ developer ไม่เฉพาะ Android Developer นะ อาจจะเป็น iOS Developer, Web Developer ทำ modular app ในโปรเจกของคุณ เพื่อการนำไปใช้ต่อในโปรเจกอื่นๆ และ maintain ได้ง่าย เพราะปกติชอบกองที่ module เดียว แล้วก็ผูกกับยังกะเส้นสปาเกตตี ถ้ามีเหตุที่ต้องปรับปรุงของในโปรเจก เช่น เปลี่ยน structure ของโปรเจกก็ต้องนั่งเปลี่ยนกันทั้งแอพอีก ทำให้เราใช้เวลาเยอะมากๆ พอไม่ work ก็อาจจะเสียเวลาเราอีก
แต่ถ้าทำ modular app โอเคช่วงแรกมันเสียเวลาแหละในการเอาความยุ่งเหยิงออกไป จัดเรียงอะไรให้ดีๆ เพื่อนำไปใช้ต่อได้ง่ายขึ้น ศึกษา best practices เอาไว้เยอะๆ เวลาเปลี่ยนจะได้ลื่นๆ เช่นของทาง LINE ใน session นี้ก็มีบางอย่างที่ทีมทำอยู่แล้ว และก็อยากแบ่งปันให้ทุกคนได้เอาไปทำตามกันได้บ้าง แล้วจริงๆบางงาน เช่น Droidcon APEC มีหลายๆ session ที่พูดถึง modular เหมือนกัน หรือในบ้านเราเองใน session ของฝั่ง Lineman Wongnai ก็มีทำเป็น modular เหมือนกันนะ จากที่ฟังในงาน Android Bangkok Conference 2020 คิดว่าเรื่องนี้จะเป็นเรื่องที่หลายๆทีม หลายๆที่เริ่มมาทางนี้กันแล้วเนอะ 🙂
download แอพอ่านบล็อกใหม่ของเราได้ที่นี่
ติดตามข่าวสารและบทความใหม่ๆได้ที่
และช่องทางใหม่ใน Twiter จ้า