• เมื่อการรั่วไหลและการแครชยังคงเกิดขึ้นต่อเนื่องใน Zig ที่ไม่มีความปลอดภัยด้านหน่วยความจำ Bun จึงย้ายโค้ด 535,496 บรรทัด ไปยัง Rust ด้วย AI agent 64 ตัว ทำให้งานที่อาจใช้เวลา 1~2 ปีสั้นลงเหลือ 11 วัน
  • จุดเริ่มต้นของความสำเร็จคือ PORTING.md ยาว 600 บรรทัด จากนั้นจึงทำการแปลงแบบขนานแยกตามไฟล์ พร้อม การรีวิวเชิงปฏิปักษ์ 2 รอบ, แก้ข้อผิดพลาดจากการคอมไพล์, ทดสอบในเครื่อง, และผ่าน CI ตามลำดับ
  • การสร้างคอมมิต 6,500 รายการ ใช้ต้นทุนตามราคา API 165,000 ดอลลาร์ พร้อมโทเคนอินพุตที่ไม่แคช 5.9 พันล้าน, เอาต์พุต 690 ล้าน, และการอ่านโทเคนอินพุตจากแคช 72 พันล้านโทเคน
  • หากทำด้วยมือ คาดว่าวิศวกร 3 คนที่รู้จักโค้ดเบสดีจะต้องหยุดการปรับปรุงผลิตภัณฑ์, การแก้บั๊กและความปลอดภัย, และการพัฒนาฟีเจอร์ใหม่เป็นเวลาราว 1 ปี ทำให้การเขียนใหม่แทบเป็นไปไม่ได้
  • หากจะทำวิธีเดิมซ้ำอีก จำเป็นต้องมีวิศวกรที่เข้าใจโค้ดเบสอย่างลึกซึ้ง, ชุดทดสอบที่แข็งแรง พอจะเชื่อถือผลลัพธ์ได้, และความพร้อมจะรับต้นทุนโทเคนแม้ความสำเร็จยังไม่แน่นอน

เหตุผลที่ Bun เลือกเขียนใหม่เป็น Rust

  • Bun เป็น โปรเจ็กต์ production ที่ซับซ้อน ซึ่งให้ความสามารถมากกว่าการเป็น JavaScript runtime
    • การแปลง JavaScript·TypeScript·CSS, bundling, minify
    • test runner และ package manager ที่เข้ากันได้กับ npm
    • การ resolve โมดูล, WebSocket client, implementation ของ Node.js และโมดูลอีกหลายตัว
  • มียอดดาวน์โหลดรายเดือน 22 ล้านครั้ง และ Claude Code กับ OpenCode ก็พึ่งพา Bun ขณะที่ Vercel·Railway·DigitalOcean ให้การสนับสนุนโดยตรง
  • Zig ไม่ใช่ภาษาแบบ memory-safe จึงยังเกิด memory leak, การแครชจากปัญหาหน่วยความจำ, และการเขียนเกินขอบเขต heap แม้ใน Bun เวอร์ชันล่าสุด
    • ทีม Bun แพตช์คอมไพเลอร์ Zig และนำการทดสอบ memory leak แบบ end-to-end มาใช้ แต่ก็ยังไม่สามารถกำจัดปัญหาได้
    • ระหว่างจัดการอายุการใช้งานของค่าจาก garbage collection และค่าที่ต้องจัดการด้วยมือร่วมกัน ก็เกิดการรั่วไหลเล็ก ๆ และการแครชเป็นพัก ๆ
    • ทุกครั้งที่มีการจัดสรรหน่วยความจำ ต้องตรวจตำแหน่งที่จะ free, ความเป็นไปได้ของการ free ซ้ำ, การจัดการ JavaScript exception, และการมองเห็น pointer ของ conservative stack scanner
  • ใน Rust แบบปลอดภัย ปัญหา use-after-free และ double-free จะกลายเป็นข้อผิดพลาดตอนคอมไพล์ และปัญหาการลืม free ในเส้นทางข้อผิดพลาดสามารถจัดการได้ด้วยการ cleanup อัตโนมัติบนพื้นฐาน Drop
  • มีการพิจารณาวิธีนำ smart pointer แบบ Rust มาใช้ในโค้ดของ Bun เองเช่นกัน แต่ใช้งานได้แย่กว่า Rust และก็ยังให้การรับประกันแบบเดียวกันไม่ได้

ทำไมโปรเจ็กต์เขียนใหม่แบบเดิมจึงใช้เวลานาน

  • ระหว่างเขียนใหม่ ฟีเจอร์ก็ยังถูกเพิ่มเข้าไปในโค้ดเบสเดิมอย่างต่อเนื่อง จึงมักทำให้กำหนดเสร็จเลื่อนออกไปซ้ำ ๆ
    • งานที่คาดว่าใช้ 9 เดือน อาจผ่านไป 9 เดือนแล้วยังเหลืออีกประมาณ 6 เดือน
    • แม้ผ่านไป 15 เดือน ก็อาจยังเหลืองานอีกหลายเดือนเพราะต้องตามฟีเจอร์ใหม่ให้ทัน
    • ถึงจะโชคดี ก็อาจต้อง freeze ฟีเจอร์ 2 เดือน แล้วค่อยเสร็จในราว 18 เดือน ทำให้การประเมินแรกที่ 9 เดือนยืดเป็นเกิน 2 ปี
  • โค้ด Zig ของ Bun เมื่อตัดคอมเมนต์ออกแล้วมี 535,496 บรรทัด จึงคาดว่าทีมวิศวกรขนาดเล็กจะต้องใช้เวลาราว 1 ปีหากย้ายไปภาษาอื่น
  • การใช้เวลา 1 ปีโดยไม่มีการปรับปรุงที่ผู้ใช้มองเห็นไม่ใช่ทางเลือกที่สมจริง จึงตัดสินใจทดลองกับ Fable เพื่อดูว่าสามารถพิสูจน์ความเป็นไปได้ของการพอร์ตไป Rust ภายใน 1 สัปดาห์ได้หรือไม่

การออกแบบล่วงหน้าและการตรวจสอบเพื่อการพอร์ต

  • ในขั้นแรก มีการคุยกับ Claude ราว 3 ชั่วโมง ถึงวิธีแม็ปแพตเทิร์นของ Zig ให้ใกล้เคียงกับ Rust และสรุปออกมาเป็น PORTING.md ยาว 600 บรรทัด
  • แนวทางการพอร์ตมีข้อจำกัดเฉพาะเพื่อคงโครงสร้างการทำงานเดิมของ Bun
    • ไม่ใช้ tokio, rayon, hyper, async-trait, futures
    • ห้ามใช้โมดูลที่เข้าถึง I/O เช่น std::fs, std::net, std::process
    • เพราะ Bun เป็นเจ้าของ event loop และ system call ของตัวเอง จึงใช้ callback และ state machine แบบ Zig เดิมแทน async fn
    • หากชนกับ borrow checker ให้เก็บค่า scalar ที่จำเป็นไว้ในตัวแปรภายในก่อน จบการยืมแล้วจึงยืมใหม่
    • ห้ามใช้ raw pointer เพื่อหลบเลี่ยง borrow checker และให้ทิ้งบันทึกการพอร์ตไว้ในจุดที่มีการเปลี่ยนโครงสร้าง
  • จากทั้งหมด 1,448 ไฟล์ มีการเขียนใหม่ 3 ไฟล์ ก่อน แล้วให้ Claude รีวิวเชิงปฏิปักษ์ 2 รอบในเซสชันที่แยกจากงานแก้ไข

การทำงานแบบขนานของ AI agent 64 ตัว

  • แบ่งงานเพื่อให้ประมวลผลไฟล์ได้อย่างอิสระต่อกัน และรัน AI agent 64 ตัว แบบขนาน
  • ช่วงแรกเกิดการชนกัน เพราะหลาย agent แตะสถานะเดียวกันของ repository
    • agent ตัวหนึ่งรัน git stash แล้วอีกตัวรัน git stash pop กับ git reset HEAD --hard
    • หากจัด worktree แยกให้แต่ละ agent ก็จะติดปัญหาพื้นที่ดิสก์ไม่พอเพราะขนาดของ repository Bun และสุดท้ายก็ยังต้องคอมไพล์การเปลี่ยนแปลงรวมกันอยู่ดี
  • จึงแก้ workflow โดยห้ามคำสั่ง Git อย่าง git stash, git reset นอกจากคำสั่งที่คอมมิตไฟล์ที่กำหนดทันที และยังห้ามใช้ cargo กับคำสั่งที่ใช้เวลารันนาน
  • สุดท้ายแบ่งงานลงใน 4 worktree และในแต่ละอันให้ Claude 16 ตัวคอมมิตและ push ไฟล์
  • agent ใช้เวลา 2 วันในการย้ายโค้ด Zig 535,496 บรรทัด โดยแต่ละคอมมิตต้องผ่านการรีวิวเชิงปฏิปักษ์ 2 รอบก่อนนำเข้า

การแก้ข้อผิดพลาดจากการคอมไพล์และการทดสอบ

  • แม้การแปลงรอบแรกจะเสร็จแล้ว แต่โค้ดยังคอมไพล์ไม่ผ่าน จึงให้ Claude แก้ข้อผิดพลาดแยกตาม crate ซึ่งเป็นหน่วยคอมไพล์ระดับบนสุดของ Rust
  • แม้ชื่อขั้นตอนจะระบุว่ามีข้อผิดพลาดจากการคอมไพล์ราว 1,600 รายการ แต่ในคำอ้างอิงระบุว่าระหว่างแก้ circular dependency มีข้อผิดพลาดโผล่ออกมาราว 16,000 รายการ
  • กระบวนการแก้ข้อผิดพลาดถูกทำแบบขนานเช่นกัน
    • รัน cargo check ในแต่ละ crate
    • จัดกลุ่มเอาต์พุตตามไฟล์และบันทึกไฟล์ข้อผิดพลาด
    • แก้ข้อผิดพลาดจากการคอมไพล์ทั้งหมดของ crate นั้น
    • ผู้รีวิวเชิงปฏิปักษ์ 2 คนตรวจการเปลี่ยนแปลง
    • agent ผู้รับผิดชอบการแก้ไข 1 คนสะท้อนผลรีวิวเข้าไป
  • agent แก้ข้อผิดพลาดจากการคอมไพล์ตั้งแต่เที่ยงคืนถึง 11:30 น. โดยไม่มีมนุษย์แทรกแซง
  • หลังจากนั้นใช้เวลาอีกราว 2 วันเพื่อให้ชุดทดสอบขนาดใหญ่รันในเครื่องได้โดยไม่มีข้อผิดพลาดจากการคอมไพล์ และทุ่มเวลาเพิ่มอีกหลายวันเพื่อแก้เทสต์ที่ล้มเหลวให้ ผ่าน CI
  • หลังจากผ่านทุกเทสต์และยืนยันการทำงานแล้วจึงรวมการเปลี่ยนแปลง โดยตั้งแต่เริ่มวางแผนจนเสร็จสิ้นใช้เวลารวม 11 วัน
    • พอร์ตโค้ดประมาณ 550,000 บรรทัด
    • คอมมิต 6,500 รายการ
    • ใช้ agent 64 ตัว

ต้นทุนและผลลัพธ์เมื่อเทียบกับการทำด้วยมือ

  • อ้างอิงตามราคา Fable API ต้นทุนรวมของการเขียนใหม่ทั้งหมดคือ 165,000 ดอลลาร์
    • โทเคนอินพุตที่ไม่แคช 5.9 พันล้าน
    • โทเคนเอาต์พุต 690 ล้าน
    • การอ่านโทเคนอินพุตจากแคช 72 พันล้าน
  • Anthropic ขาย API token แบบบวกมาร์จิน จึงทำให้ต้นทุนภายในจริงต่ำกว่านี้
  • แม้ค่า API จะใกล้เคียงเงินเดือนฐานรายปีของวิศวกรซอฟต์แวร์องค์กรระดับกลางในสหรัฐฯ แต่ก็ประเมินว่าวิศวกรคนหนึ่งที่รับเงินเดือนระดับนั้นไม่อาจทำผลงานระดับเดียวกันได้ภายใน 11 วัน
  • สิ่งนี้ยังสอดคล้องกับความเห็นของ Mitchell Hashimoto ที่ว่า Fable ทำได้ดีเป็นพิเศษกับ งานยากที่ต้องโฟกัสและมี reward function ชัดเจน
  • หากทำด้วยมือ คาดว่าจะต้องใช้วิศวกร 3 คนที่รู้จักโค้ดเบสทั้งหมดอย่างสมบูรณ์เป็นเวลาประมาณ 1 ปี
    • ในช่วงนั้นจะเดินหน้าปรับปรุงความเข้ากันได้กับ Node.js, แก้บั๊กและปัญหาความปลอดภัย, และทำฟีเจอร์ใหม่ได้ยาก
    • ทางเลือกที่สมจริงกว่าคือไม่เขียนใหม่ แล้วเดินหน้าแก้บั๊กด้านหน่วยความจำในระบบเดิมต่อไป

เงื่อนไขสำหรับการนำไปใช้กับโปรเจ็กต์อื่น

  • หาก AI สามารถย่นการเขียนใหม่หรือ migration ที่ปกติใช้ 1 ปีให้เหลือระดับ 1 สัปดาห์ได้ ก็จะทำให้โปรเจ็กต์ที่ก่อนหน้านี้พิจารณาได้ยากกลายเป็นสิ่งที่ทำได้จริง
  • หากจะนำ workflow ของ Bun ไปใช้ซ้ำ ต้องมี 3 เงื่อนไข
    • วิศวกร ที่รู้จักโค้ดเบสอย่างลึกมากและมีแรงขับในการลงมือทำ
    • ชุดทดสอบ ที่แข็งแรงพอให้เชื่อถือว่าการผ่านเทสต์สะท้อนการทำงานจริง
    • ความพร้อมจะลงทุนกับต้นทุนโทเคนจำนวนมาก แม้ยังไม่รู้ล่วงหน้าว่าจะสำเร็จหรือไม่
  • งานซ้ำ ๆ อย่างการย้ายโค้ดเป็นสิ่งที่ LLM รับมือได้ค่อนข้างดี ดังนั้นหากมีเทสต์ที่ดีและมีวิศวกรที่สามารถจัดระเบียบปัญหาได้ โอกาสสำเร็จก็สูง
  • ไม่ใช่ว่าทุกโปรเจ็กต์จะต้องใช้เงิน 165,000 ดอลลาร์
    • ถ้าเป็นโปรเจ็กต์ที่ง่ายกว่า ต้นทุนก็อาจต่ำลง
    • อาจใช้โมเดลที่แพงที่สุดกับการวางแผนระดับสูง และใช้โมเดลที่ถูกกว่าสำหรับการเขียนโค้ดและรีวิว
  • แม้ migration ที่ขับเคลื่อนด้วย AI จะเร็วขึ้นเรื่อย ๆ แต่ความเร็วระดับนี้จะเกิดขึ้นได้เฉพาะใน โปรเจ็กต์ที่วิศวกรรมวางมาดี แบบ Bun เท่านั้น

ยังไม่มีความคิดเห็น

ยังไม่มีความคิดเห็น