กรณีศึกษา

วิธีที่วิศวกรของ Instagram Direct สร้างสถาปัตยกรรม UI ที่เป็น AI โดยใช้ Jetpack Compose และลดต้นทุนโทเค็นต่อเซสชันของตัวแทนได้ 33%

อ่าน 11 นาที

บล็อกโพสต์นี้เขียนขึ้นโดยความร่วมมือกับทีม Meta

Instagram Direct เป็นหนึ่งในแพลตฟอร์มหลักบน Instagram ที่จัดการข้อความของผู้ใช้หลายพันล้านข้อความในแต่ละวัน ทีมได้ทำการปรับปรุงระบบ Android View แบบเดิมมาหลายปี และได้ทำการเพิ่มประสิทธิภาพเล็กๆ น้อยๆ ทุกอย่างที่ทำได้ อย่างไรก็ตาม การดูแลรักษาและขยายแพลตฟอร์มเดิมที่ได้รับการเพิ่มประสิทธิภาพอย่างมากจะทำให้เกิดหนี้ทางเทคนิคและค่าใช้จ่ายด้านวิศวกรรมจำนวนมาก โดยเฉพาะอย่างยิ่งเมื่อทีมต่างๆ นำ UI แบบประกาศและผู้ช่วยเขียนโค้ด AI มาใช้มากขึ้น 

การปรับใช้ Jetpack Compose สำหรับ Instagram Direct ไม่ได้เป็นเพียงการปรับปรุง UI ให้ทันสมัยตามปกติ ทีมได้สร้างฐานของโค้ด UI ที่เป็น AI โดยเฉพาะซึ่งมีขนาดเล็กลง 50% เมื่อเทียบกับการติดตั้งใช้งานเดิม ขณะเดียวกันก็ลดเวลาในการดำเนินการของ AI Agent ลง 35% ลดการแลกเปลี่ยนระหว่างวิศวกรกับเอเจนต์ลง 32% และลดต้นทุนโทเค็นลง 33% ทีมได้นำ Jetpack Compose มาใช้ในขณะที่ยังคงรักษามาตรฐานประสิทธิภาพสูงไว้ โดยทำงานร่วมกับ Google อย่างใกล้ชิด การเพิ่มประสิทธิภาพทำให้ Meta และ Google ปรับปรุง Compose ไม่เพียงแต่สำหรับ Instagram แต่ยังรวมถึงระบบนิเวศของนักพัฒนาแอป Android ที่กว้างขึ้นด้วย

การปรับฐานของโค้ดให้ทันสมัยในระดับขนาดใหญ่

AI ได้กลายเป็นเพื่อนร่วมงานประจำวันของวิศวกรในอุตสาหกรรมอย่างรวดเร็ว และการนำ AI ไปใช้กับโค้ดเบสขนาดใหญ่ เช่น Instagram ก็ช่วยเพิ่มประสิทธิภาพการทำงานได้อย่างแท้จริง ทีม Instagram Direct ตั้งเป้าหมายที่ท้าทายมากขึ้น ทีมงานได้ออกแบบฐานของโค้ดและสถาปัตยกรรมใหม่ให้เป็น AI โดยเฉพาะตั้งแต่ต้น แทนที่จะเพียงแค่ชี้เครื่องมือ AI ไปที่โค้ดที่มีอยู่ ซึ่งจะช่วยเพิ่มผลลัพธ์ของ AI ได้มากกว่าการปรับเปลี่ยนที่มีอยู่เพียงอย่างเดียว

ทีม Instagram Direct เลือก Jetpack Compose เป็นคอมโพเนนต์หลักในการสร้างสถาปัตยกรรม UI ที่เป็น AI โดยเฉพาะ ลักษณะการประกาศของ Jetpack Compose ช่วยให้มั่นใจได้ว่าโค้ดจะกระชับ คาดการณ์ได้ และมีโครงสร้างที่โมเดล AI สามารถให้เหตุผลได้ง่ายขึ้น โดยมีผลข้างเคียงน้อยลง สถานะโดยนัยน้อยลง และขอบเขตของคอมโพเนนต์ที่ชัดเจนยิ่งขึ้น

การย้ายข้อมูลไปยัง Jetpack Compose ต้องมีการวางแผนอย่างรอบคอบ ผู้คนหลายร้อยล้านคนส่งข้อความบน Instagram ทุกวัน ดังนั้นการย้ายข้อมูลจึงต้องค่อยๆ เป็นค่อยๆ ไปอย่างราบรื่นโดยไม่ขัดขวางประสบการณ์การใช้งานเลยในขณะที่ทีมปรับโครงสร้างพื้นฐานใหม่ เพื่อแสดงให้เห็นถึงความท้าทายในระดับต่างๆ องค์ประกอบ UI แต่ละรายการสามารถแสดงผลในการเรียงสับเปลี่ยนสถานะที่แตกต่างกันกว่า 160 รายการ และหน้าจอการสนทนาเพียงหน้าจอเดียวก็รองรับข้อความประเภทต่างๆ กว่า 200 ประเภท

Product Design 1.png

เมื่อย้ายข้อมูลฐานของโค้ดขนาดนี้ไปยัง Compose คุณอาจอยากใช้วิธีที่ง่ายที่สุดและฝังคอมโพเนนต์ UI ของ Compose ไว้ในลำดับชั้นการแสดงผล View ที่มีอยู่ ซึ่งเป็นขั้นตอนที่เพิ่มขึ้นระหว่างการย้ายข้อมูลแบบค่อยเป็นค่อยไป อย่างไรก็ตาม ในระยะยาว การผสานรวม Compose ภายในฐานของโค้ดที่ใช้ View เป็นหลักจะทำให้เกิดความท้าทาย เครื่องมือ AI มักจะเลือกเส้นทางที่ง่ายที่สุด หากคุณผสมโค้ด UI แบบประกาศและแบบคำสั่ง AI มีแนวโน้มที่จะผสมโค้ดเหล่านั้นอย่างไม่ถูกต้อง ซึ่งจะทำให้เกิดข้อบกพร่องเล็กๆ น้อยๆ หนี้ทางเทคนิค และการถดถอยของประสิทธิภาพ 

การสร้างสถาปัตยกรรม UI ที่สร้างขึ้นสำหรับ AI โดยเฉพาะ

เมื่อพิจารณาถึงขนาดของ Instagram การแยกส่วนสถาปัตยกรรมในระดับหนึ่งจึงเป็นสิ่งที่หลีกเลี่ยงไม่ได้ และเป็นสิ่งที่ช่วยให้แอปยังคงบำรุงรักษาได้เมื่อมีการเติบโต พิจารณารูปแบบทั่วไปที่สร้างโมเดลRecyclerViewประเภทรายการแต่ละรายการเป็นองค์ประกอบสืบทอดมาจากคลาสพื้นฐาน RecyclerViewItem ที่กำหนดเองซึ่งแสดงฮุกวงจรปกติ เช่น onBind

ตัวอย่างที่ 1

class ChatItem(
  val features: FeatureFlagProvider
) : RecyclerViewItem<ComposeViewHolder, ChatUiState> {

  // Imperative context:
  // AI could often take the path of least resistance and generate a mutable
  // state here, dispatched outside the ChatUiState. This class survives
  // re-bindings and is shared across multiple items, ultimately leading to
  // unexpected, hard-to-reproduce bugs.
  var isPinned: Boolean = false

  override fun onBind(holder: ComposeViewHolder, uiState: ChatUiState) {
      // Imperative context
      val isPinnedChatsEnabled = features.isEnabled("pinned_chats_feature")

      // Declarative context
      holder.composeView.setContent {

        // Blending imperative and declarative contexts
        if (isPinnedChatsEnabled) {
          Button(onClick = { isPinned = !isPinned }) {
            Text(if (isPinned) "Unpin" else "Pin")
          }
        }
        
        ...
      }
  }
}


ในข้อมูลโค้ดด้านบน มีปัญหา 2 อย่างที่แทรกเข้ามา ก่อนอื่น ระบบจะอ่านแฟล็ก isPinnedChatsEnabled ในโค้ดคำสั่ง จากนั้นจะจับภาพภายใน Compose Lambda ซึ่งเป็นการเชื่อมต่อที่ละเอียดอ่อนในหลายๆ รูปแบบ ประการที่ 2 isPinnedจะอยู่ในช่องที่เปลี่ยนแปลงได้ในตัวสินค้าเองแทนที่จะอยู่ใน ChatUiState ดังนั้นจึงยังคงอยู่เมื่อมีการผูก RecyclerView ใหม่และการรีไซเคิลในแถวต่างๆ ทำให้เกิดการรั่วไหลและข้อบกพร่องที่สร้างความเจ็บปวดในการทำซ้ำ

แม้ว่าโค้ดจะได้รับการล้างข้อมูลโดยการให้ฟังก์ชัน @Composable เฉพาะกับรายการ แต่ปัญหาก็ยังคงเหมือนเดิม

ตัวอย่างที่ 2

class ChatItem(
  val features: FeatureFlagProvider
) : ComposeRecyclerViewItem<ChatUiState> {

  // Imperative context
  val isPinnedChatsEnabled = features.isEnabled("pinned_chats_feature")
  var isPinned: Boolean = false

  // Declarative context
  @Composable
  override fun Content(uiState: ChatUiState) {

 
    // Blending imperative and declarative contexts
    if (isPinnedChatsEnabled) {
      Button(onClick = { isPinned = !isPinned }) {
        Text(if (isPinned) "Unpin" else "Pin")
      }
    }

    ...
  }
}

นี่เป็นตัวอย่างที่ตั้งใจทำให้เรียบง่าย แต่แสดงให้เห็นถึงปัญหาที่กว้างขึ้น นั่นคือยิ่ง AI มีขอบเขตน้อยลงเท่าใด คุณภาพของโค้ดที่สร้างขึ้นเมื่อเวลาผ่านไปก็จะยิ่งต่ำลงเท่านั้น แม้ว่าการป้องกันและทักษะจะช่วยได้ แต่ก็ไม่เพียงพอในตัวของมันเอง เนื่องจากเมื่อ AI พบอุปสรรค ก็มักจะหาทางหลีกเลี่ยงเพื่อปลดบล็อกตัวเอง

หากต้องการทำให้ฐานของโค้ดเป็นมิตรกับ AI คุณต้องปฏิบัติตามกฎ 2 ข้อต่อไปนี้

  • ลดการพึ่งพาบริบทที่กำหนดเอง ยิ่ง AI Agent ต้องมีความรู้ที่เฉพาะเจาะจงกับโค้ดเบสมากเท่าใดเพื่อให้ทำการเปลี่ยนแปลงได้อย่างถูกต้อง คุณภาพของเอาต์พุตก็จะยิ่งต่ำลง ยิ่งโค้ดเบสใกล้เคียงกับแนวทางปฏิบัติแนะนำที่ทราบมากเท่าใด ผลลัพธ์ของ AI ก็จะยิ่งดีขึ้นเท่านั้น
  • ฐานของโค้ดที่เน้น AI เป็นอันดับแรกต้องบังคับใช้ขอบเขตของตัวเอง การแก้ไขช่องโหว่ด้านการออกแบบด้วยทักษะ AI ไม่สามารถปรับขนาดได้ เนื่องจากทักษะทุกอย่างที่โหลดลงในบริบทต้องใช้โทเค็นและอาจทำให้ประสิทธิภาพของเอเจนต์ลดลง แต่สถาปัตยกรรมเองควรรับภาระนั้น AI Agent จะเลือกเส้นทางที่ง่ายที่สุดโดยธรรมชาติ ดังนั้นการออกแบบควรทำให้เส้นทางนั้นนำไปสู่โค้ดที่ถูกต้องและมีคุณภาพสูง ในขณะเดียวกันก็ทำให้การตัดสินใจออกแบบที่ไม่ดีเป็นเรื่องยากและมีค่าใช้จ่ายสูง

รายการในลิสต์ยังคงแสดงด้วยการแยกส่วนของตัวเองได้ แต่ในกรณีนี้โค้ด Compose ทั้งหมดจะอยู่ในตัวสร้าง ดังนั้นจึงไม่มีสิทธิ์เข้าถึงสมาชิกของคลาสหรือสถานะ และแหล่งที่มาของอาร์กิวเมนต์เพียงแหล่งเดียวคือตัวสร้าง ซึ่งทำให้เทียบเท่ากับฟังก์ชัน @Composable ธรรมดา ในขณะที่ยังคงเป็นไปตามสถาปัตยกรรมที่มีอยู่

ตัวอย่างที่ 3

class ChatItem(
  val features: FeatureFlagProvider,
  val onPin: (Boolean) -> Unit,
) : ComposeItem<ChatUiState>(

 
   // Compose UI
   content = { uiState: ChatUiState ->
    val isPinnedChatsEnabled = features.isEnabled("pinned_chats_feature")

    if (isPinnedChatsEnabled) {
      Button(onClick = { onPin(!uiState.isPinned) }) {
        Text(if (uiState.isPinned) "Unpin" else "Pin")
      }
    }
    
    ...
  },
)

การย้ายฐานของโค้ดขนาดนี้เป็นงานที่ต้องใช้ความพยายามอย่างมาก เป็นเวลานานแล้วที่คอมโพเนนต์ UI หลายร้อยรายการซึ่งประกอบกันเป็น Direct UI ส่วนใหญ่ต้องอยู่ร่วมกับคอมโพเนนต์เดิม โดยทั้ง 2 ส่วนได้รับการดูแลควบคู่กันไป เวิร์กโฟลว์ AI ช่วยให้การย้ายข้อมูลแบบคู่ขนานนี้เป็นไปได้ด้วยการเร่งกระบวนการเขียนโค้ดจำนวนมาก แนวทางดังกล่าวช่วยให้ทีม Direct ทำการย้ายข้อมูลได้ในเวลาที่รวดเร็วที่สุด โดยไม่กระทบต่อทีมอื่นๆ ที่ยังคงเปิดตัวฟีเจอร์ต่างๆ ที่ช่วยปรับปรุงประสบการณ์การใช้งานของผู้คนหลายล้านคนทุกวัน

วิศวกรหลายคนได้เรียกใช้ AI Agent ของตนเองกับฐานความรู้ที่ใช้ร่วมกันของทักษะและแบบแผนที่นำกลับมาใช้ใหม่ได้ ซึ่งสร้างขึ้นระหว่างการย้ายข้อมูล ซึ่งช่วยให้เวิร์กโฟลว์และแนวทางปฏิบัติแนะนำสอดคล้องกันในทีม แทนที่จะให้วิศวกรแต่ละคนค้นหาแนวทางปฏิบัติแนะนำด้วยตนเอง ในแต่ละแพลตฟอร์ม ทีมได้ทำการย้ายข้อมูลในระยะต่อไปนี้

  • เขียนโค้ด Compose ทั้งหมดด้วย AI
  • ปรับปรุง จัดการกรณีขอบ และปิดช่องว่างด้านประสิทธิภาพ จนกระทั่งเปิดตัว UI ให้ผู้ใช้จริงในการทดสอบแบบสาธารณะ

การแบ่งงานออกเป็น 2 ขั้นตอนต่อหน้าจอช่วยให้วิศวกรคนหนึ่งสามารถเลื่อนผ่านทั้งพื้นผิวได้อย่างรวดเร็ว โดยจะกำหนดสถาปัตยกรรมและกรณีขอบที่ซับซ้อนตั้งแต่เนิ่นๆ เมื่อวางรากฐานดังกล่าวแล้ว คนอื่นๆ ก็จะมุ่งเน้นที่การเตรียม UI ให้พร้อมใช้งานจริงได้โดยไม่ต้องหยุดเพื่อตัดสินใจด้านเทคนิคด้วยตนเอง ซึ่งจะช่วยให้การย้ายข้อมูลโดยรวมเป็นไปอย่างรวดเร็ว

ผลลัพธ์ของการย้ายข้อมูลได้ยืนยันแนวทางนี้ สำหรับแพลตฟอร์ม Instagram Direct ที่ย้ายข้อมูลแล้ว Jetpack Compose ช่วยให้ทีมลดจำนวนโค้ด UI ทั้งหมดลงได้ 50% โค้ดที่น้อยลงซึ่ง AI จะสร้างนั้นเชื่อมโยงกับเอาต์พุตที่มีคุณภาพสูงขึ้นและต้นทุนโทเค็นต่อภารกิจที่ต่ำลง 

Quote-Pavlo-New.jpg

การวิเคราะห์ข้อมูลภายในของโค้ดเบส Android สำหรับ Instagram Direct เปรียบเทียบเซสชันของ AI Agent ที่ทำงานใน Compose UI กับงานเดียวกันโดยใช้ Android Views ประสิทธิภาพที่เพิ่มขึ้นนั้นชัดเจนใน 2 มิติ ดังนี้

  • ต่ออักขระของโค้ดที่สร้างขึ้น: ลดการแลกเปลี่ยนข้อมูลระหว่างวิศวกรกับเอเจนต์ได้ 32% และลดเวลาการดำเนินการของเอเจนต์ได้ 35% (เวลาที่ผ่านไปตั้งแต่เอเจนต์เริ่มดำเนินการตามคำขอของวิศวกรจนกว่าจะส่งคืนการตอบกลับ)
  • ต่อเซสชันของตัวแทน: ต้นทุนโทเค็นโดยรวมลดลง 33%  เมื่อใช้ Compose เทียบกับ Views

เราจะรายงานทั้งประสิทธิภาพเอาต์พุตและจํานวนเซสชันทั่วไปเนื่องจากเป็นผลลัพธ์ที่มีประโยชน์ในตัวของมันเอง ตัวเลขเวลาในการแลกเปลี่ยนและดำเนินการของเอเจนต์วิศวกรจะเปรียบเทียบการใช้ทรัพยากรต่อหน่วยของเอาต์พุตที่ได้ ในขณะที่ตัวเลขโทเค็นจะเปรียบเทียบต้นทุนทั้งหมดสำหรับเซสชันเอเจนต์ทั่วไป

นอกจากนี้ ข้อมูลยังแสดงให้เห็นถึงความแตกต่างที่สอดคล้องกันในวิธีที่เฟรมเวิร์กทั้ง 2 จัดการกับโค้ดที่ซับซ้อนหรือเปราะบาง Meta ติดตามเรื่องนี้โดยใช้คะแนนความเสี่ยงของการเปลี่ยนแปลงโค้ด ซึ่งจะประเมินคุณภาพโค้ดโดยรวมและความน่าจะเป็นที่การเปลี่ยนแปลงจะทำให้เกิดเหตุการณ์ในเวอร์ชันที่ใช้งานจริง การวิเคราะห์นี้วัดประสิทธิภาพของทรัพยากรของ Agent โดยใช้การผสมผสานการใช้โทเค็น เวลาในการดำเนินการของ Agent และการโต้ตอบระหว่างวิศวกรกับ Agent เมื่อไฟล์มีคะแนนความเสี่ยงสูงขึ้น เซสชันของ AI Agent ก็จะมีประสิทธิภาพด้านทรัพยากรน้อยลงโดยอัตโนมัติ

เมื่อคะแนนความเสี่ยงสะสมของไฟล์เพิ่มขึ้นเป็น 2 เท่า UI ที่ใช้กับ Android Views จะลดประสิทธิภาพของทรัพยากรตัวแทนลง 30% (ต่ออักขระที่ส่ง) ในสถานการณ์เดียวกัน การลดขนาดโดย UI ของ Jetpack Compose จะอยู่ที่ 9%เท่านั้น

ทีม Instagram Direct ได้นำมุมมองใหม่ๆ มาใช้ในการนำ Compose ไปใช้ผ่านการเป็นพาร์ทเนอร์ระหว่าง Google กับ Meta โดยพิจารณาจากความพร้อมของ AI ในโค้ดเบส ไม่ใช่แค่การเขียน UI ใหม่ งานนี้แสดงให้เห็นถึงความแข็งแกร่งของ Compose ในการเป็นรากฐานสำหรับการสร้างฐานของโค้ดและสถาปัตยกรรมที่เน้น AI เป็นอันดับแรก โดยเฉพาะอย่างยิ่งเมื่อนำไปใช้กับแอปขนาดใหญ่อย่าง Instagram

การเพิ่มประสิทธิภาพ 

Instagram Direct เป็นหนึ่งในแพลตฟอร์มที่สำคัญที่สุดของแอป และผู้คนคาดหวังว่าแพลตฟอร์มนี้จะทำงานได้อย่างรวดเร็วและตอบสนองได้ดีตลอดเวลา การนำ Jetpack Compose มาใช้จริงหมายถึงการเขียน UI ใหม่ทั้งหมด และเป้าหมายอันดับหนึ่งคือการรักษาประสบการณ์การใช้งานคุณภาพสูงโดยไม่มีการถดถอย

การทำซ้ำมาหลายปีได้ผลักดันการติดตั้งใช้งานแบบอิงตามยอดดูเดิมบน Instagram ให้มีประสิทธิภาพสูงอย่างยิ่ง และทีมก็จำเป็นต้องรักษามาตรฐานเดียวกันนี้ในขณะที่เปลี่ยนไปใช้เฟรมเวิร์ก UI ใหม่ทั้งหมด

Instagram วัดเมตริกประสิทธิภาพหลายร้อยรายการ  สำหรับการนำ Compose มาใช้ 3 ข้อต่อไปนี้เป็นสิ่งที่สำคัญที่สุด

  • เวลาในการโต้ตอบ - ระยะเวลาระหว่างการเปิดหน้าจอจนถึงเวลาที่ใช้หน้าจอได้
  • เวลาในการโหลดจนเสร็จ - ระยะเวลาระหว่างการเปิดหน้าจอจนถึงเวลาที่เนื้อหาทั้งหมดโหลดจนเสร็จ (เช่น รูปภาพ)
  • ประสิทธิภาพการเลื่อน - หน้าจอเลื่อนได้อย่างราบรื่นเพียงใดโดยไม่มีเฟรมหลุด

ระบบจะติดตามเมตริกเหล่านี้ในรันไทม์ในเวอร์ชันที่ใช้งานจริง ซึ่งช่วยให้สามารถทำการทดสอบ A/B เพื่อเปรียบเทียบ UI ของ Compose ที่ย้ายข้อมูลแล้วกับ UI เดิม และประเมินผลกระทบด้านประสิทธิภาพของความพยายามนี้

วิธีทั่วไปในการเข้าถึงการย้ายข้อมูลเช่นนี้คือการเริ่มต้นจากเล็กๆ โดยย้ายคอมโพเนนต์ UI เพียงไม่กี่รายการ รวบรวมข้อมูล และศึกษาลักษณะการทำงานของคอมโพเนนต์เหล่านั้น แม้ว่าผลลัพธ์เบื้องต้นเหล่านี้จะมีประโยชน์ แต่ก็แสดงให้เห็นภาพเพียงบางส่วนเท่านั้น ซึ่งทำให้เกิดผลลัพธ์ที่ไม่ถูกต้องเกี่ยวกับการนำ Compose ไปใช้ เนื่องจาก

  • ไม่เป็นตัวแทน - คอมโพเนนต์ UI ที่ย้ายข้อมูลแล้ว 1 รายการสามารถให้ข้อมูลที่เป็นประโยชน์เกี่ยวกับประสิทธิภาพโดยรวมในหน้าจอหนึ่งๆ อย่างไรก็ตาม คอมโพเนนต์ต่างๆ จะทำงานแตกต่างกันด้วยเหตุผลที่ไม่ได้เป็นแบบทั่วไป คุณจึงไม่สามารถคาดการณ์จากคอมโพเนนต์นั้นได้เสมอไป
  • ค่าใช้จ่ายในการทำงานร่วมกัน - ชิ้นส่วน Compose ขนาดเล็กภายในโค้ดเบส View ขนาดใหญ่จะทำให้เกิดค่าใช้จ่ายในการเชื่อมต่อที่ไม่แน่นอนระหว่าง 2 ระบบ ค่าใช้จ่ายดังกล่าวทำให้การวัดผลคลาดเคลื่อน ดังนั้นผลลัพธ์ขนาดเล็กในช่วงแรกจึงไม่สะท้อนให้เห็นว่าการย้ายข้อมูลแบบเต็มรูปแบบจะเป็นอย่างไร

ผลลัพธ์คือการย้ายข้อมูลขนาดเล็ก แม้จะมีประโยชน์ แต่ก็ไม่ได้แสดงถึงผลกระทบทั้งหมดของ Compose เสมอไป ยิ่งย้ายข้อมูลพื้นผิวแบบครบวงจรโดยไม่มีการหยุดชะงัก ภาพรวมด้านประสิทธิภาพก็จะยิ่งชัดเจนและดีขึ้น

หน้าจอหลักใน Instagram Direct สร้างขึ้นโดยอิงตามรายการยาวๆ ของไอเทมประเภทต่างๆ ซึ่งเดิมทีใช้ RecyclerView สถาปัตยกรรมนี้อาศัยการแยกส่วนที่กำหนดเองเพื่อความสามารถในการปรับขนาด แต่ยังคงผูกติดอยู่กับวงจรของระบบที่อิงตาม View

Diagram 1.png

งานหลักของทีมคือการย้ายข้อมูลรายการในลิสต์หลายร้อยรายการทีละรายการไปยัง Compose ภายในสถาปัตยกรรมที่อิงตาม RecyclerView ที่มีอยู่ โดยจะเปิดตัวในเวอร์ชันที่ใช้งานจริงในกลุ่มเล็กๆ ที่เป็นอิสระภายใต้การทดสอบ A/B ทั้งหมดนี้โดยไม่มีการเปลี่ยนแปลงที่ผู้ใช้มองเห็นในประสบการณ์การรับส่งข้อความของผู้ใช้

ข้อเสียที่ใหญ่ที่สุดของการตั้งค่าดังกล่าวคือการพึ่งพาระบบ View เดิมอย่างมากผ่านสถาปัตยกรรมRecyclerViewหลัก แม้หลังจากย้ายข้อมูลรายการทั้งหมดไปยัง Compose แล้วก็ตาม ทีมจึงตัดสินใจลงทุนในการแทนที่สถาปัตยกรรมหลักที่อิงตาม RecyclerView ด้วยทางเลือกอื่นที่ใช้ Compose โดยเฉพาะอย่าง LazyColumn ซึ่งเป็นขั้นตอนถัดไปตามธรรมชาติ

ซึ่งหมายความว่าควรแยกคอมโพเนนต์ UI ของ Compose ออกจากเฟรมเวิร์กที่คอมโพเนนต์นั้นอยู่ภายใน ขณะเดียวกันก็ยังคงใช้งานร่วมกับทั้ง RecyclerView และ LazyColumn ได้ในเวลาเดียวกัน นอกจากนี้ ความสามารถในการสลับระหว่าง 2 อย่างนี้ที่รันไทม์ผ่านฟีเจอร์แฟล็กเพื่อเปิดใช้การทดสอบ A/B ก็มีความสำคัญไม่แพ้กัน

Diagram 2.png

แม้ว่ารายการ Compose ใหม่จะเข้ากันได้โดยกำเนิดกับ LazyColumn และสามารถเสียบเข้ากับโครงสร้างต้นไม้ขององค์ประกอบที่ไม่ขาดตอนได้ แต่เราได้สร้าง Interop API เพื่อเสียบรายการเหล่านั้นลงใน RecyclerView ด้วย ซึ่งช่วยให้เราสามารถเปิดตัวLazyColumnการตั้งค่าภายใต้การทดสอบ A/B ควบคู่ไปกับRecyclerView โดยใช้รายการ Compose เดียวกันซ้ำและปรับปรุงประสิทธิภาพโดยไม่รบกวนการสร้างและปรับแต่งฟีเจอร์ของทีมส่วนอื่นๆ

ขนาด ความซับซ้อน และความละเอียดอ่อนของ Instagram แม้แต่การถดถอยที่เล็กที่สุดก็เป็นความท้าทายที่ไม่เหมือนใครสำหรับ Jetpack Compose การแก้ไขปัญหาเหล่านี้ต้องใช้การทำงานร่วมกันอย่างใกล้ชิดและการทำงานร่วมกันแบบวนซ้ำ วิศวกรของ Google และ Meta ได้ร่วมกันวิเคราะห์เมตริกอย่างใกล้ชิดเพื่อระบุและออกแบบความสามารถใหม่ๆ ของ Compose ให้ตรงตามหรือสูงกว่าเกณฑ์เปรียบเทียบที่อิงตาม View การเป็นพาร์ทเนอร์ครั้งนี้ทำให้ Jetpack Compose มีฟีเจอร์ที่โดดเด่นต่อไปนี้เพิ่มเข้ามา ได้แก่ องค์ประกอบที่หยุดชั่วคราวได้ด้วย LazyLayoutCacheWindows และการติดตามระดับการมองเห็น 

องค์ประกอบที่หยุดชั่วคราวได้ด้วย LazyLayoutCacheWindows

การเรียบเรียงที่หยุดชั่วคราวได้ (เปิดใช้โดยค่าเริ่มต้นใน Compose 1.10) ช่วยให้สามารถเรียบเรียงรายการ Lazy List ที่มีค่าใช้จ่ายสูงได้ทีละเฟรมเพื่อป้องกันไม่ให้เกิดอาการกระตุก เมื่อใช้ร่วมกับ LazyLayoutCacheWindow (เพิ่มใน Compose 1.9) การทำงานร่วมกันนี้จะช่วยปรับปรุงความลื่นไหลในการเลื่อนได้อย่างมาก ในการทดสอบภายในที่ Meta เมื่อเร็วๆ นี้ การรวมการจัดองค์ประกอบที่หยุดชั่วคราวได้เข้ากับ LazyLayoutCacheWindow ที่มีช่องมองภาพเดียวช่วยลดเฟรมหลุดขนาดใหญ่ต่อนาที (LFD/m) ลงประมาณ 13% เมื่อเทียบกับ Compose แบบเดิม Cache Window เพียงอย่างเดียวช่วยลดการใช้พลังงานลงได้ประมาณ 8% เมื่อเทียบกับค่าพื้นฐานเดียวกัน LFD/m เป็นเมตริกภายในที่ Meta ใช้เพื่อติดตามการกระตุกที่สังเกตเห็นได้ขณะเลื่อน

Quote-Fabio.jpg


การใช้ LazyLayoutCacheWindow ในแอปจะเตรียมและเก็บไอเทมที่อยู่นอกหน้าจอไว้ภายในแถบที่อิงตามพิกเซลรอบๆ Viewport เพื่อให้ปัดได้อย่างรวดเร็ว หากต้องการใช้ประโยชน์จาก LazyLayoutCacheWindows ในแอป คุณสามารถใช้ Compose 1.13.0-alpha03 เวอร์ชันล่าสุดและตั้งค่าตามที่แสดงในตัวอย่างด้านล่าง

val cacheWindow = LazyLayoutCacheWindow(ahead = 150.dp, behind = 100.dp)
// OR
val cacheWindow = LazyLayoutCacheWindow(aheadFraction = 0.5f, behindFraction = 0.3f)

LazyColumn(state = state, cacheWindow = cacheWindow) {
    ...
}

การกำหนดค่าช่วงแคชทำได้ 2 วิธี ทั้ง 2 ตัวเลือกอธิบายสิ่งเดียวกัน นั่นคือปริมาณเนื้อหาที่อยู่นอกหน้าจอที่ควรคงไว้ แต่ใช้หน่วยต่างกัน

  • Dp: ความยาวสัมบูรณ์คงที่ ahead = 150.dp จะเก็บเนื้อหา 150dp ที่ประกอบขึ้นเลยขอบที่มองเห็นได้ไม่ว่าจะเป็นอุปกรณ์ใดก็ตาม
  • Float: เศษส่วนของวิวพอร์ต aheadFraction = 0.5fจะแสดงครึ่งหน้าจอถัดไปไว้ล่วงหน้า ดังนั้นจำนวนที่แน่นอนจะปรับขนาดตามความสูงของหน้าจอที่รองรับรูปแบบต่างๆ เช่น แสดงมากขึ้นบนแท็บเล็ตหรืออุปกรณ์แบบพับได้ที่กางออก และแสดงน้อยลงบนโทรศัพท์ขนาดกะทัดรัด 

ทีม Instagram ได้ปรับแต่งเศษทศนิยมแบบลอยตัวของหน้าต่างแคชให้เหมาะกับโครงสร้างเนื้อหาและขนาดไอเทมของ Direct โดยเฉพาะ เนื่องจากค่าที่เหมาะสมจะแตกต่างกันไปตามพารามิเตอร์ UI ที่เฉพาะเจาะจง การค้นหาความสมดุลที่เหมาะสมจึงต้องมีการทดสอบ

การบันทึกการแสดงผลด้วย onVisibilityChanged


API  onVisibilityChanged (เพิ่มใน Compose 1.9.0) เป็นอีกหนึ่งผลลัพธ์ที่สำคัญของการเป็นพาร์ทเนอร์ด้านเทคนิคระหว่าง Google กับ Meta ซึ่งจะช่วยให้พื้นผิว Jetpack Compose ขนาดใหญ่ทราบได้อย่างสอดคล้องกันว่าเมื่อใดที่ Composable จะปรากฏบนหน้าจอจริง ซึ่งจะแทนที่การใช้งานที่กำหนดเองและสร้างขึ้นเองที่ใช้ในอดีต เฉพาะใน Instagram Direct เท่านั้น เราใช้สัญญาณการมองเห็นเหล่านี้ในไฟล์หลายร้อยไฟล์เพื่อรองรับเมตริกคุณภาพของผลิตภัณฑ์ที่ขึ้นอยู่กับว่าองค์ประกอบ UI แสดงต่อผู้ใช้จริงหรือไม่

ประสิทธิภาพการเริ่มต้น

การนำ Jetpack Compose มาใช้กับ Instagram Direct ทำให้ประสิทธิภาพในส่วนอื่นๆ ของแอปดีขึ้นอย่างไม่คาดคิด รันไทม์ของ Jetpack Compose มีต้นทุนการวอร์มอัพที่คุณจ่ายเพียงครั้งเดียว และเนื่องจากการรับส่งข้อความเป็นส่วนที่มีการเข้าชมสูงซึ่งมักมีการเข้าชมในช่วงต้นๆ ของเซสชันผู้ใช้ ส่วนอื่นๆ ใน Instagram ที่ใช้ Compose จึงมีประสิทธิภาพดีขึ้นอย่างเห็นได้ชัด

เราได้เพิ่มประสิทธิภาพการทำงานของ Compose UI ใน Instagram Direct เองผ่านการใช้โปรไฟล์พื้นฐาน ซึ่งจะคอมไพล์เส้นทางโค้ดที่ใช้งานบ่อยล่วงหน้าในเวลาที่ติดตั้ง เพื่อให้ Compose แสดงผลได้อย่างรวดเร็วตั้งแต่การเปิดตัวครั้งแรก

บทเรียนจากการย้ายข้อมูล Instagram Direct ไปยัง Jetpack Compose 

  • Jetpack Compose ให้ผลตอบแทนจากการลงทุนในทันที: คุณไม่จำเป็นต้องใช้เวิร์กโฟลว์ AI ขั้นสูงเพื่อรับประโยชน์จาก Compose การลดโค้ดลงประมาณ 50% หมายความว่าโค้ดที่ต้องบำรุงรักษามีจำนวนน้อยลง และพื้นที่สำหรับข้อบกพร่องก็ลดลงด้วย
  • การออกแบบสถาปัตยกรรมที่สร้างขึ้นมาเพื่อ AI โดยเฉพาะทำให้เกิดผลลัพธ์ที่สำคัญ ซึ่งรวมถึงการลดเวลาในการดำเนินการของ AI Agent ลง 35%, การแลกเปลี่ยนระหว่างวิศวกรกับ Agent ลดลง 32% และต้นทุนโทเค็นลดลง 33%
  • แม้ว่าจะมี API การทำงานร่วมกันมากมายและรองรับการรวม View และ Compose เข้าด้วยกัน แต่ให้ตั้งเป้าที่จะย้ายข้อมูลพื้นผิวขนาดใหญ่กว่าคอมโพเนนต์ขนาดเล็กแต่ละรายการ ซึ่งจะช่วยให้ UI อยู่ภายในลำดับชั้นการจัดองค์ประกอบเดียวที่ไม่ขาดตอน และปลดล็อกการเพิ่มประสิทธิภาพที่ดีที่สุดทั้งหมดของ Compose
  • จับคู่ Pausable composition กับ LazyLayoutCacheWindow: การจับคู่ทั้ง 2 อย่างนี้จะให้ผลลัพธ์ที่ดีกว่าการใช้ cache windows เพียงอย่างเดียว หากมีเพียงหน้าต่างแคช ไอเทมที่มีขนาดใหญ่ก็ยังอาจพยายามคอมโพสในรอบเดียว ซึ่งอาจทำให้เกินงบประมาณเฟรม
  • ร่วมสนับสนุน Compose ด้วยตัวคุณเอง Meta ได้ร่วมมือกับทีม Jetpack Compose เพื่อนำความคิดเห็นและไอเดียมาใช้ใน Compose การทำงานบนชุดเครื่องมือโอเพนซอร์สหมายความว่าเราทุกคนจะได้รับประโยชน์เมื่อมีการแก้ไขข้อบกพร่องและปรับปรุงประสิทธิภาพจากส่วนกลาง ดังนั้น โปรดแจ้งให้เราทราบความคิดเห็นของคุณ

การปรับใช้ Jetpack Compose ช่วยให้เราได้รับประโยชน์อย่างมากในการพัฒนาที่ AI ช่วยเหลือ พร้อมทั้งลดความซับซ้อนของการออกแบบ UI ในแต่ละวันของ Instagram แนวทางแบบประกาศช่วยลดโค้ดสำเร็จรูป ทำให้การจัดการสถานะง่ายขึ้น และปรับปรุงประสิทธิภาพการทำงานของนักพัฒนาซอฟต์แวร์โดยรวม ทีมวิศวกรรมของ Instagram ตั้งตารอที่จะนำ Compose ไปยังแพลตฟอร์มอื่นๆ ในแอป และการทำงานร่วมกันอย่างต่อเนื่องระหว่าง Google กับ Meta เพื่อนำการปรับปรุงเพิ่มเติมมาสู่ผู้ใช้ Instagram และ Jetpack Compose 

หากยังไม่เคยลองใช้ Compose ที่ตอนนี้มีระบบช่วยเหลือโดย AI การย้ายข้อมูลไปยัง Jetpack Compose ก็จะง่ายขึ้นกว่าที่เคย

การรับทราบ ขอขอบคุณ Michal Zielinski และ Matthew Du จาก Meta รวมถึง Andrei Shikov และ George Mount จาก Google ที่ช่วยปรับปรุงประสิทธิภาพของ Compose ผ่านการทำงานร่วมกันระหว่าง Meta และ Google ขอขอบคุณ Gary Ye จาก Meta ที่ช่วยนำ Compose มาสู่ Instagram Direct และขอขอบคุณ Gopal Juneja จาก Meta ที่สนับสนุนความพยายามนี้ผ่านการใช้ Data Science

เขียนโดย:
อ่านต่อ