เพราะการ Rollback สำหรับ Mobile App นั้นจะต่างจาก Web หรือ Server ที่สามารถ Deploy โค้ดชุดใหม่ขึ้นไปได้อย่างรวดเร็ว และนั่นจึงเป็นที่มาของบทความนี้ที่จะช่วยแนะนำสิ่งที่นักพัฒนาควรรู้เกี่ยวกับการทำ Rollback บน Android ครับ

Rollback คือ?

Rollback คือการย้อนเวอร์ชันของโค้ดกลับไปยังชุดก่อนหน้าเพื่อรับมือกับปัญหาบางอย่างที่สร้างความเสียหายกับ Product ในระดับที่ร้ายแรงจนไม่สามารถรอทีมพัฒนาหาสาเหตุและแก้ไข ณ ตอนนั้นได้

Android ไม่มีระบบ Rollback สำหรับแอปภายในเครื่อง

อย่างที่เรารู้กันดีว่าผู้ใช้ไม่สามารถย้อนเวอร์ชัน (Downgrade) กลับไปเป็นเวอร์ชันก่อนหน้าได้ และการอัปเดตแอปก็จะต้องเป็นเวอร์ชันที่มี "Version Number" ที่สูงกว่าเวอร์ชันที่ติดตั้งอยู่เท่านั้น

Version Number จะต่างกับ Version Name ที่แสดงให้ผู้ใช้เห็น เพราะ Android OS และ Google Play จะสนใจแค่ Version Number เท่านั้น ส่วน Version Name จะกำหนดเป็นเลขอะไรก็ได้ตามใจชอบ

จึงทำให้เวลาที่ผู้ใช้อัปเดตแอปเป็นเวอร์ชันล่าสุดแล้วปัญหาจนใช้งานไม่ได้ ก็จะย้อนกลับไปใช้เวอร์ชันก่อนหน้าไม่ได้

เว้นแต่จะเป็น Pre-installed App ที่ผู้ใช้สามารถย้อนกลับไปเป็นเวอร์ชันที่ติดมากับเครื่องในตอนแรกได้ จะบอกให้ผู้ใช้โหลดไฟล์จากนอก Google Play มาติดตั้งแทน

และการดาวน์โหลดแอปผ่าน Google Play ก็จะได้เป็นเวอร์ชันล่าสุดเสมอ ซึ่งจะขึ้นอยู่กับการทำ Staged Rollout และ Form Factor จากนักพัฒนาด้วยด้วย

การ Rollback สำหรับ Android App ไม่มีอยู่จริง

เพราะการปล่อยอัปเดตแอปเวอร์ชันใหม่ในแต่ละครั้งจะไม่การันตีว่าผู้ใช้ทุกคนจะอัปเดตเป็นเวอร์ชันล่าสุดในทันที บ้างก็เปิด Auto-updates ที่จะทำการอัปเดตแอปในตอนกลางคืน บ้างก็อาจจะใช้เวอร์ชันก่อนหน้านั้นไปจนกว่าจะโดนบังคับให้อัปเดตก็มี ซึ่งรวมไปถึงการทำ Rollback ในบทความนี้ด้วยเช่นกัน

นั่นเป็นเหตุผลที่แอปส่วนใหญ่ทำ Force Update ไว้ด้วย

แล้วถ้าจำเป็นต้อง Rollback ขึ้นมาจริง ๆ ล่ะ?

ถึงแม้ว่า​ Android App จะไม่สามารถ Rollback ได้แบบ Web หรือ Server ก็ตาม แต่นักพัฒนาสามารถใช้วิธีที่ใกล้เคียงกับการทำ Rollback ได้เช่นกัน โดย "ใช้โค้ดชุดเก่า แต่เปลี่ยน Version Number ให้สูงกว่าเวอร์ชันล่าสุด"

สมมติว่านักพัฒนามีแอปเวอร์ชันล่าสุดและเวอร์ชันที่ต้องการทำ Rollback แบบนี้

Version Name Version Code
โค้ดที่มีปัญหาร้ายแรง 1.4.1 817
โค้ดที่ต้องการ Rollback 1.3.8 665

ให้เอาชุดโค้ดของเวอร์ชันที่ต้องการมาเปลี่ยน Version Code ให้สูงกว่าแบบนี้

Version Name Version Code
โค้ดเดียวกับ 1.3.8 1.4.2 / 1.3.9 818
โค้ดที่มีปัญหาร้ายแรง 1.4.1 817
โค้ดที่ต้องการ Rollback 1.3.8 665
เลขเวอร์ชันที่เจ้าของบล็อกยกตัวอย่างจะอ้างอิงจากมาตรฐานของ Semantic Versioning 2.0
Semantic Versioning 2.0.0
Semantic Versioning spec and website

จากตัวอย่างดังกล่าวจะกำหนดเป็น 1.4.2 หรือ 1.3.9 ก็ได้ แต่แนะนำให้ใช้เป็น 1.3.9 มากกว่าด้วยเหตุผลในหัวข้อถัดไป

แอปที่มีการใช้ Feature Flag เพื่อเปิด/ปิดบางฟีเจอร์

เป็นปกติที่ในหลายแอปจะมี Feature Flag ที่สามารถกำหนดค่าได้จาก Server หรือบริการ Remote Configuration อย่างของ Firebase เพื่อเปิดหรือปิดการใช้งานบางฟีเจอร์ได้อย่างอิสระที่อาจจะมีการกำหนดเงื่อนไขตามเวอร์ชันของแอปด้วย

ในการทำ Feature Flag ที่ดีควรอิงจากเลข Version Name แทนที่จะเป็น Version Number เพราะนอกจากจะเป็นเลขเวอร์ชันที่แสดงให้ผู้ใช้เห็น ก็ยังเป็นเลขเวอร์ชันที่ทีมพัฒนาใช้สื่อสารกันภายในด้วย

นั่นจึงเป็นที่มาในตัวอย่างก่อนหน้าว่าทำไมเจ้าของบล็อกแนะนำให้กำหนด Version Name เป็น 1.3.9 แทน เพราะใน 1.3.8 อาจจะมีโค้ดบางส่วนที่เขียนไว้ล่วงหน้าและรอเปิดใช้งานใน 1.4.0 ด้วย Feature Flag ดังนั้นถ้ากำหนดเป็น 1.4.2 ก็อาจจะทำให้โค้ดดังกล่าวที่ยังไม่พร้อมในเวอร์ชันนั้นทำงานขึ้นมาและสร้างปัญหาตามมาได้เช่นกัน

ระวังผลข้างเคียงจากข้อมูลที่อยู่ใน Persistence Storage ด้วย

ในกรณีที่โค้ดของฟีเจอร์ดังกล่าวเกี่ยวข้องกับการเก็บข้อมูลภายในเครื่องด้วย ก็อาจจะต้องทำ Impact Analysis หรือวิเคราะห์ปัญหาที่อาจจะเกิดขึ้นจากการย้อนเวอร์ชันของโค้ดด้วย

ยกตัวอย่างเช่น ในเวอร์ชัน 1.4.0 มีฟีเจอร์ที่มีผลต่อข้อมูลในบาง Field ของ Database ทำให้ต้องทำ Database Migration ก็อาจจะเกิดปัญหาในตอนที่ Rollback กลับไปเป็น 1.3.8 ได้ เพราะนักพัฒนาไม่เคยต้องเขียนโค้ดสำหรับ Database Migration ให้รองรับการ Downgrade มาก่อน

ลองนึกถึง Database Schema ภายในแอปที่มีการปรับค่าในบาง Field จาก Non-null ให้เป็น Nullable แล้วมีการเก็บค่าเป็น Null ในระหว่างนั้น แต่มีเหตุที่ทำให้ต้องย้อนโค้ดกลับไปเป็นตอนที่กำหนดค่าเป็น Non-null แล้วพยายามเรียกข้อมูลจาก Field ที่กลายเป็น Null ไปแล้ว ❌☠️❌

จะรีบแค่ไหนก็ต้องรอ Google Play อยู่ดี

ไม่ว่าปัญหาจะร้ายแรงแค่ไหน เมื่อต้องส่งแอปเวอร์ชันใหม่ขึ้น Google Play ก็ต้องรอจนกว่าจะผ่านการรีวิว ซึ่งอาจจะใช้ระยะเวลาตั้งแต่ 30 นาทีไปจนถึง 2 วันขึ้นไปโดยขึ้นอยู่กับสถานการณ์ของแอปและ Google Play ในตอนนั้น (เช่น ถ้าโดน Policy Issue จาก Google Play ก็อาจจะทำให้ใช้ระยะเวลาในการรีวิวนานขึ้นกว่าปกติ)

จึงทำให้ในบางแอปเลือกใช้ WebView หรือ Dynamic Code Loading (DCL) ในบางฟีเจอร์เพื่อให้ Rollback ได้รวดเร็วเมื่อเกิดปัญหา แต่ก็ต้องแลกมาด้วยการทำงานที่ซับซ้อนมากขึ้นในเวลาที่โค้ดดังกล่าวทำงานแบบเจาะจงเวอร์ชัน

ควรมี Force Update เพื่อช่วยให้เวอร์ชันใหม่ส่งถึงมือผู้ใช้ได้รวดเร็วขึ้น

การเพิ่มฟีเจอร์อย่าง Force Update จะช่วยให้นักพัฒนาบังคับให้ผู้ใช้ต้องอัปเดตแอปเป็นเวอร์ชันใหม่ล่าสุดได้ง่ายขึ้น แต่ถ้า Force Update บ่อยจนเกินไปก็อาจจะทำให้เกิด User Drop-off ได้เช่นกัน

ดังนั้นถ้าอยากให้ Force Update ทำงานอย่างมีประสิทธิภาพ ควรกำหนดการทำงานแบบมีเงื่อนไขได้ เช่น กำหนดให้ Force Update ทำงานเฉพาะเวอร์ชัน 1.4.1 ที่มีปัญหาร้ายแรงเท่านั้น และแอปเวอร์ชันที่ต่ำกว่านั้นก็ยังคงสามารถใช้งานได้ตามปกติ

สรุป

ถึงแม้ว่าแอปบน Android จะไม่สามารถ Rollback ได้ รวมไปถึง Google Play ที่บังคับให้นักพัฒนาต้องส่งแอปที่กำหนด Version Number สูงกว่า แต่นักพัฒนาก็สามารถใช้วิธีย้อนโค้ดกลับไปเป็นเวอร์ชันที่ต้องการแล้วกำหนด Version Number ให้สูงกว่าเวอร์ชันล่าสุดก็ได้เช่นกัน

แต่การใช้วิธีดังกล่าวเพื่อทำ Rollback ก็ต้องทำ Impact Analysis เพื่อป้องกันผลกระทบที่อาจจะเกิดจากการ Rollback ด้วย ไม่เช่นนั้นแทนที่ต้องแก้ปัญหาให้ทันเวลา จะกลายเป็นการสร้างปัญหาที่ซับซ้อนมากกว่าเดิมและเสียเวลาโดยใช่เหตุแทน