1 คะแนน โดย GN⁺ 1 시간 전 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • เมื่อค่อย ๆ รีแฟกเตอร์ ชั้นการเข้าถึงข้อมูล Rust ขนาด 17,155 บรรทัด ที่เอเจนต์เขียนขึ้น จำนวนโทเค็นขาเข้าที่ต้องใช้สำหรับการเปลี่ยนฟังก์ชันแบบเดียวกันลดลงจาก 159,564 เหลือ 27,360 หรือ ลดลง 83%
  • ปริมาณโค้ดทั้งหมดแทบไม่เปลี่ยน แต่เมื่อแยกโค้ดที่เกี่ยวข้องออกเป็นไฟล์ที่มีความเชื่อมโยงกันสูง เอเจนต์จึงอ่านได้เฉพาะ ชุดไฟล์ขั้นต่ำ ที่จำเป็นต่อการแก้ไข
  • โทเค็นขาเข้าไม่ได้ลดลงมากนักจนกว่าไฟล์ที่ใหญ่ที่สุดจะเล็กลงพอ สุดท้ายชั้นข้อมูลถูกแยกเป็นไฟล์ Rust 19 ไฟล์ และลดขนาดไฟล์สูงสุดจาก 17,155 บรรทัดเหลือ 3,695 บรรทัด
  • โทเค็นขาออกและปริมาณงานที่ใช้พัฒนาฟังก์ชันแทบไม่เปลี่ยน และ Claude ก็ไม่สามารถเลือกหรือทำรีแฟกเตอร์ที่เหมาะสมได้อย่างสม่ำเสมอด้วยตัวเอง จึงต้องอาศัย คำแนะนำเชิงรุกจากมนุษย์ ทั้งในการวางแผนและการลงมือทำ
  • หากคิดตามราคาโทเค็นขาเข้าของ Sonnet 5 ที่ $3/MTok การประหยัดต่อการแก้ไข 1 ครั้งมีเพียงประมาณ $0.397 แต่ยืนยันได้ว่าต้นทุนมีแนวโน้มลดลงซ้ำ ๆ ทุกครั้งที่มีการแก้ไขชั้นการเข้าถึงข้อมูลในภายหลัง

ไฟล์ 17,155 บรรทัดที่เอเจนต์สร้างขึ้น

  • แอปพลิเคชันสำหรับสนับสนุนงานมีทั้งเว็บ UI ที่อัปเดตและค้นข้อมูลแบบไดนามิกได้, โมดัลและการบันทึกอัตโนมัติ, การเชื่อมต่อกับระบบภายนอก, งานแมชชีนเลิร์นนิงและการวิเคราะห์ข้อความ, งานเบื้องหลัง, และสภาพแวดล้อมดีพลอยอัตโนมัติ
  • จากโค้ดทั้งหมดราว 150,000 บรรทัด นั้น Rust มีประมาณ 120,000 บรรทัด ที่เหลือเป็น TypeScript และ Terraform โดยส่วนใหญ่เอเจนต์เป็นผู้เขียนผ่าน Claude Code และบางส่วนใช้ Cursor
  • นักพัฒนาแทบไม่ได้อ่านหรือรีวิวโค้ดเลย นอกจากบางครั้งที่เปิดดูด้วยความสนใจ
  • ชั้นการเข้าถึงข้อมูลเติบโตเกิน 6,000 บรรทัดจากการทำซ้ำการตั้งค่า HTTP request และการเข้ารหัส/ถอดรหัส JSON เดิม ๆ ในทุกคิวรีอ่าน/เขียน และสุดท้ายไฟล์ Rust เดียวก็มีขนาดถึง 17,155 บรรทัด
  • โมดูลนี้ไม่มีทั้งการลดความซ้ำซ้อนและภาษาภายใน ฟังก์ชันถูกแยกออกได้จำกัด และแทบไม่มีการแยกคลาส แต่มีอินเทอร์เฟซที่ต้องรักษาไว้และมีขอบเขตชัดเจน จึงเหมาะกับการทดลองรีแฟกเตอร์

วิธีวัดโดยทำการเปลี่ยนแบบเดียวกันซ้ำ ๆ

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

ผลการวัดในแต่ละขั้น

  • ในสถานะตั้งต้น ชั้นการเข้าถึงข้อมูลและไฟล์ที่ใหญ่ที่สุดมีขนาด 17,155 บรรทัด เท่ากัน โค้ด Rust ทั้งหมดมี 50,359 บรรทัด และการเปลี่ยนตัวแทนต้องใช้โทเค็นขาเข้า 159,564 โทเค็น โทเค็นขาออก 1,705 โทเค็น และเวลา 342 วินาที
  • หลังขั้นที่ 15 ชั้นการเข้าถึงข้อมูลมี 16,608 บรรทัด ไฟล์ที่ใหญ่ที่สุดมี 3,695 บรรทัด โค้ด Rust ทั้งหมดมี 49,812 บรรทัด โดยใช้โทเค็นขาเข้า 27,360 โทเค็น โทเค็นขาออก 2,113 โทเค็น และเวลา 454 วินาที
  • ในขั้นกลาง ๆ เมื่อไฟล์ที่ใหญ่ที่สุดเล็กลง โทเค็นขาเข้าก็ลดลงตาม
    • หลังขั้นที่ 7 แยก queries.rs แล้ว ไฟล์ที่ใหญ่ที่สุดเหลือ 15,670 บรรทัด และโทเค็นขาเข้าลดเหลือ 151,850
    • หลังขั้นที่ 8 แยก traits.rs แล้ว เหลือ 13,845 บรรทัด และโทเค็นขาเข้าเป็น 132,558
    • หลังขั้นที่ 12 แยก store/ แล้ว เหลือ 9,269 บรรทัด และโทเค็นขาเข้าลดลงถึง 104,080
    • หลังแยก store/ ครั้งสุดท้าย ไฟล์ที่ใหญ่ที่สุดเหลือ 3,695 บรรทัด และโทเค็นขาเข้าร่วงลงเหลือ 27,360
  • ชั้นการเข้าถึงข้อมูลสุดท้ายประกอบด้วย ไฟล์ Rust 19 ไฟล์ และไฟล์ที่ใหญ่ที่สุดกลายเป็นไลบรารีทดสอบ
  • ในการรีแฟกเตอร์เพิ่มเติม วิธีเดียวกันนี้ยังสามารถนำไปใช้กับไฟล์ทดสอบนั้นได้

เหตุผลที่โทเค็นขาเข้าลดลง 83%

  • โทเค็นขาเข้าของงานเดียวกันลดจาก 159,564 เหลือ 27,360 ประหยัดได้ 132,204 โทเค็น
  • เนื่องจากปริมาณโค้ดรวมของชั้นการเข้าถึงข้อมูลแทบไม่เปลี่ยน นี่จึงไม่ใช่ผลจากการมีโค้ดให้อ่านน้อยลง
  • เอเจนต์สามารถระบุชุดไฟล์ขั้นต่ำที่จำเป็นต่อการทำงานได้ และค่อย ๆ อ่านเฉพาะขอบเขตโค้ดที่เล็กลงเรื่อย ๆ ซึ่งยืนยันได้จากเอาต์พุตการคิดของ Claude Code และสรุปการอ่านไฟล์
  • หากเพียงแค่แบ่งไฟล์ให้ย่อยลงแบบสุ่ม ก็ยังอาจต้องอ่านหลายไฟล์เพื่อหาโค้ดที่เกี่ยวข้อง จึงยากจะได้ผลลัพธ์แบบเดียวกัน
  • การลดลงมากที่สุดเกิดขึ้นในช่วงแยกครั้งสุดท้าย แต่ทำได้ก็เพราะขั้นก่อนหน้าได้ดึงส่วนซ้ำออกมาและสร้างโครงสร้างแกนกลางที่ใช้ซ้ำ จนสามารถแยกไฟล์ออกได้
  • ลำดับนี้ไม่ได้ถูกออกแบบไว้ล่วงหน้าเพื่อเป้าหมายลดต้นทุน แต่เกิดจากกระบวนการรีแฟกเตอร์ทั่วไปที่เริ่มจากกำจัดความซ้ำซ้อนเฉพาะจุด แล้วเมื่อแกนกลางร่วมชัดเจนขึ้นจึงค่อยแตกออกเป็นไฟล์เล็ก ๆ

โทเค็นขาออกและผลด้านการเงิน

  • โทเค็นขาออกที่ถูกสร้างขึ้น ขณะเขียนการเปลี่ยนตัวแทนแทบไม่เปลี่ยน จึงแปลว่าการรีแฟกเตอร์ไม่ได้ลดขนาดของการเปลี่ยนจริง
  • ราคาของโทเค็นขาออกสูงกว่าโทเค็นขาเข้า 5 เท่า แต่ปริมาณจริงน้อยกว่ามาก
  • หากคำนวณตามราคาโทเค็นขาเข้าของ Sonnet 5 ที่ $3/MTok การประหยัดต่อการแก้ไข 1 ครั้งอยู่ที่ประมาณ 39.7 เซนต์
  • ยังไม่ชัดเจนว่าการประหยัดนี้จะสะสมในงานดีบัก ฟังก์ชันที่ซับซ้อนกว่า หรือการรีแฟกเตอร์ทั้งโค้ดเบสหรือไม่ และยังไม่ทราบว่าต้นทุนของการรีแฟกเตอร์เองมีเท่าไร
  • ก็ยังไม่ชัดเช่นกันว่าจะมีการรีแฟกเตอร์ที่ลดโทเค็นขาออกได้หรือไม่ เพราะในการเปลี่ยนตัวแทนที่เรียบง่าย สัญญาณรบกวนจากการสร้างโค้ดแบบไม่กำหนดแน่นอนกลบความต่างที่เกิดจากโครงสร้าง

การรีแฟกเตอร์ที่ทำร่วมกับ Claude

  • Claude ไม่สามารถดูโค้ดแล้วเลือกการรีแฟกเตอร์ที่จะใช้ได้ด้วยตัวเอง และผลลัพธ์จริงก็สอดคล้องกับงานที่พรอมป์ต์สั่งโดยตรง
  • แม้ใน development harness จะมีขั้นรีแฟกเตอร์ระบุไว้อย่างชัดเจน แต่ Claude ก็ไม่ได้ใช้มันเพื่อปรับปรุงไฟล์ 17,155 บรรทัดนี้
  • ในขั้นวางแผน Claude Code พบการแยกฟังก์ชันเป็นขั้นแรก ขณะที่ Claude.ai ระบุได้ไกลถึงการแยกคลาสไคลเอนต์ทั้งหมด
  • สำหรับการเปลี่ยนแปลงเชิงกล ได้ใช้สคริปต์ Python ร่วมกับ grep และ sed แต่สคริปต์มักสับสนเพราะเรื่องการเยื้องบรรทัด
  • การแยกไฟล์ store ซึ่งให้คุณค่ามากที่สุด ถูกพลาดไปในการลองครั้งแรกและต้องนำกลับมาใช้ในขั้นถัดไป ทำให้จำนวนขั้นในผลการวัดไม่ตรงกับขั้นในแผนภาคผนวก
  • การทดลองทั้งหมดใช้เวลาประมาณ 8 ชั่วโมง และส่วนใหญ่ดำเนินไปแบบไม่มีคนเฝ้า
    • หลังผ่านไป 6 ชั่วโมง 40 นาที จึงพบขั้นที่ตกหล่นและเข้าไปแทรกแซง 1 ครั้ง
    • สาเหตุที่ทำให้การรันทดสอบช้ามาก ไม่ใช่ Wi-Fi โรงแรมที่ช้า แต่เป็น Cargo temporary build cache ที่ใหญ่เกินไป

พรอมป์ต์การเปลี่ยนตัวแทน

  • ซับเอเจนต์แต่ละตัวได้รับเพียงโค้ดเบสและเอกสารสถาปัตยกรรม แล้วต้องทำ ItemWatchStore asynchronous public trait แบบเดียวกัน
  • trait นี้มี 3 เมธอดดังต่อไปนี้
    • watch_item(&self, item_id: &str, user_id: &str) -> Result<()>
    • unwatch_item(&self, item_id: &str, user_id: &str) -> Result<()>
    • watched_items_for_user(&self, user_id: &str) -> Result<Vec<String>>
  • ข้อมูล watch ถูกเก็บในคอลเลกชัน item_watches ของ Firestore โดยมีฟิลด์ itemId, userId, createdAt
  • คืนค่า Vec<String> ของ item ID โดยไม่สร้าง Rust record struct แยกต่างหาก
  • ใน FakeStore เพิ่มฟิลด์ Vec<(String, String)> ในหน่วยความจำ และใน FirestoreStore ให้ติดตั้งใช้งานโดยคงรูปแบบ HTTP เดิมไว้
  • ในตอนท้ายของคำตอบ ให้พิมพ์ JSON ของไฟล์ที่อ่าน จำนวนอักขระ และจำนวนอักขระของคำตอบ พร้อมสั่งไม่ให้ commit โค้ด

แผนรีแฟกเตอร์ที่ใช้

  • ขั้นที่ 1 — แยกคลาส FirestoreClient

    • แยกความรับผิดชอบระหว่างการประสานคิวรีโดเมนกับการส่ง Firestore HTTP
    • ย้าย reqwest::Client, project_id, MetadataAuth, การจัดการ URL และ auth header ไปยังโครงสร้างใหม่
    • ตามแผนจะลดโค้ดใน implementation ของ FirestoreStore ลงราว 1,200 บรรทัด และเพิ่มในไคลเอนต์ราว 120 บรรทัด
  • ขั้นที่ 2 — แยกฟังก์ชัน extract_doc_id, new_link

    • ทำให้การดึง ID ของ document parser 20 ตัว และการสร้าง Link ที่ซ้ำ 62 ครั้งกลายเป็นส่วนกลางร่วมกัน
    • คาดว่าจะลดได้ราว 500 บรรทัด
  • ขั้นที่ 3 — แยกฟังก์ชัน pipeline ของ link query

    • ทำให้รูปแบบการรวบรวมผลลัพธ์คิวรีราว 15 จุด และการค้นหา target ID เดี่ยวราว 8 จุด กลายเป็นส่วนกลางร่วมกัน
    • คาดว่าจะลดได้ราว 200 บรรทัด
  • ขั้นที่ 4 — แยกฟังก์ชันเงื่อนไขลิงก์ของ FakeStoreInner

    • แยกการแปลง inner.links.iter() ที่ซ้ำอยู่ในราว 15 เมธอดออกเป็น 2 เมธอด
    • คาดว่าจะลดได้ราว 120 บรรทัด
  • ขั้นที่ 5 — เพิ่มฟังก์ชันสร้างค่า Firestore

    • แทนที่นิพจน์ json! ของสตริง ไทม์สแตมป์ ฯลฯ ที่ซ้ำกว่า 128 ครั้ง ด้วยการเรียกฟังก์ชัน 4 ตัว
    • เปลี่ยน macro หลายบรรทัดให้เป็นการเรียกแบบบรรทัดเดียว คาดว่าจะลดได้ราว 80 บรรทัด
  • ขั้นที่ 6 — แยก FieldsBuilder

    • รวมรูปแบบการสร้าง field map ของ encoder ราว 20 ตัวไว้ใน builder
    • ลด encoder จากราว 40 บรรทัดเหลือราว 12 บรรทัด คาดว่าจะลดรวมได้ 500~600 บรรทัด
  • ขั้นที่ 7 — แยก queries.rs

    • ย้ายค่าคงที่ LinkQuery 32 ตัวและ type ที่เกี่ยวข้องไปยังโมดูลแยก
    • โดยไม่เปลี่ยนจุดเรียกใช้งานเดิม จะลด mod.rs ได้ราว 800 บรรทัด
  • ขั้นที่ 8 — แยก traits.rs

    • ย้าย public trait 17 ตัวและ type ของข้อผิดพลาดที่เกี่ยวข้อง แล้ว re-export กลับ
    • ลด mod.rs ลงราว 1,900 บรรทัด แต่ไฟล์ใหม่ก็จะมีราว 1,900 บรรทัดเช่นกัน
  • ขั้นที่ 9 — แยก traits/ ตามโดเมน

    • แบ่ง trait ออกเป็น planning.rs, content.rs, people.rs, system.rs
    • โดยไม่เปลี่ยน definition และ call site จะจำกัดขนาดไฟล์ไว้ราว 300~650 บรรทัดต่อไฟล์
  • ขั้นที่ 10 — แยก codec.rs

    • ย้าย document encoder/decoder, parser, FieldsBuilder, และฟังก์ชันสร้างค่าออกไป
    • หลังขั้นที่ 6 จะกลายเป็นโมดูลขนาดราว 400~500 บรรทัด และลด mod.rs ลงราว 500 บรรทัด
  • ขั้นที่ 11 — แยก fake_store.rs

    • ย้าย FakeStore, FakeStoreInner, และ implementation ของ 18 trait ออกไป
    • ลด mod.rs ลงราว 4,700 บรรทัด
  • ขั้นที่ 12 — แยก implementation ของ FirestoreStore

    • ใน store/mod.rs คงไว้เฉพาะ struct, constructor, FirestoreClient, MetadataAuth แล้วแยก implementation ของ trait เป็นไฟล์ตามโดเมน
    • เปลี่ยนไฟล์ขนาดราว 10,000 บรรทัดให้เป็น 10 ไฟล์ที่มีขนาด 120~650 บรรทัดต่อไฟล์ และทำให้ mod.rs เป็นไฟล์ re-export ราว 100 บรรทัด
  • ขั้นที่ 13 — วางชุดทดสอบไว้คู่กับโมดูลเป้าหมาย

    • ไม่เปลี่ยนโค้ดทดสอบ แต่ย้ายไปไว้ใต้แต่ละไฟล์ implementation
    • ลด mod.rs ลงราว 2,000 บรรทัด และเพิ่มชุดทดสอบที่เกี่ยวข้อง 200~700 บรรทัดในแต่ละไฟล์

ข้อจำกัดและการทดลองต่อไป

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

1 ความคิดเห็น

 
GN⁺ 1 시간 전
ความคิดเห็นจาก Hacker News
  • น่าสนใจที่ แนวปฏิบัติที่ดีของนักพัฒนา ซึ่งบริษัท IT ส่วนใหญ่เคยมองข้าม กำลังถูกคิดค้นขึ้นใหม่ในฐานะแนวปฏิบัติที่ดีของ AI
    เมื่อก่อนถ้าบอกให้วางเอกสารไว้ในโค้ด ให้บอกบริบททั้งหมดของโปรเจกต์แทนที่จะโยนแค่งานใน Jira และให้รีแฟกเตอร์เพื่อผลิตภาพระยะยาว ก็มักถูกมองว่าน่าเบื่อ
    ตอนนี้พอบอกเรื่องเดียวกันว่าเอกสารของ AI ควรอยู่ในโค้ดและ CLAUDE.md อย่าควบคุมละเอียดผ่านพรอมป์ และมารีแฟกเตอร์เพื่อผลิตภาพของ AI กัน กลับถูกมองว่าน่าสนใจ

    • การทำให้ AI agent ทำงานเดิมอย่างสม่ำเสมอนั้นง่ายกว่าทำกับเพื่อนร่วมงานมนุษย์มาก
      มนุษย์แม้จะรู้วิธีที่ถูกต้อง แต่ก็อาจยุ่งหรือสมาธิหลุด ส่วน agent ไม่เบื่องานซ้ำซาก ดังนั้นขั้นตอนที่พิสูจน์แล้วว่าได้ผลกับมนุษย์แต่ยากจะใช้ให้ต่อเนื่อง จึงเป็นไปได้จริงในทางปฏิบัติ
      มีการให้ agent แยกกันเขียน implementation และ test จากสเปกร่วม แล้วให้ agent ตรวจสอบอีกตัวมาทวนสอบ เพื่อไม่ให้ผลลัพธ์ของกันและกันปนเปื้อนกัน นี่คือการนำ วิศวกรรมคลีนรูม ที่ IBM พัฒนาขึ้นสำหรับมนุษย์ในทศวรรษ 1980 มาใช้กับ AI อย่างกว้างขวางและสม่ำเสมอ
    • บุคคลที่เกี่ยวข้องกับบทความนี้คือ Martin Fowler ผู้เขียนหนังสือ 《Refactoring》 เมื่อกว่า 20 ปีก่อนและทำให้คำนี้เป็นที่รู้จักแพร่หลาย
      นี่ไม่ใช่การเอาแนวปฏิบัติเก่ามาห่อใหม่ให้ดูเหมือนของใหม่ตามกระแส AI แต่เป็นการแสดงพร้อมหลักฐานว่าแนวปฏิบัติที่ดีซึ่งมีอายุกว่า 20 ปียังคงใช้ได้อยู่
    • ความแตกต่างใหญ่ระหว่างก่อนและหลัง AI คือมนุษย์มี ความสามารถในการจัดการบริบทระยะยาว ที่ค่อนข้างดี
      agent ต้องเรียนรู้บริบทใหม่ในแต่ละเซสชัน ทำให้คุณค่าของแนวปฏิบัติที่ดีสูงขึ้นมาก และเห็นผลทันที
    • ต้องขอบคุณกระแส AI ที่ทำให้สามารถขอ budget สำหรับ งานปรับปรุงประสบการณ์นักพัฒนา ที่อยากทำได้ แต่ก็น่าขมขื่นตรงที่เหตุผลมันผิด
      ถึงอย่างนั้น การสั่งให้ CLI รัน 100 ครั้งในหนึ่งชั่วโมงเพื่อทดสอบ usability ของ flag ใหม่ก็เป็นเรื่องดี
    • มนุษย์แม้จะต้องเจอกับเอกสารเก่า ๆ ใน SharePoint บริบททั้งหมดที่ได้ยินผ่าน ๆ ระหว่างประชุม และลำดับความสำคัญของการรีแฟกเตอร์ที่ต่ำ ก็ยังพอสร้างผลลัพธ์ออกมาได้ แม้คุณภาพและกำหนดการจะแย่ลง
      ในทางกลับกัน ถ้า AI ไม่มีฐานแบบนี้ ก็จะทำงานได้แย่มากหรือไม่ทำงานเลย ดังนั้น แนวปฏิบัติทางวิศวกรรมที่ดีต่อสุขภาพ จึงไม่ใช่มาตรการปรับปรุงระยะยาว แต่เป็นเงื่อนไขตั้งต้นที่จำเป็น
      แม้ในความเป็นจริงจะไม่มีผลสุทธิเลยก็ตาม การใส่ AI เข้าไปใน workflow ก็มีประโยชน์ตรงที่สร้างเหตุผลให้เริ่มนำแนวปฏิบัติการพัฒนาที่ถูกต้องมาใช้ได้
  • ชอบที่บทความนี้วิจารณ์อย่าง เป็นรูปธรรมและเชิงปริมาณ โดยอิงจากวิธีที่เครื่องมือ AI ถูกใช้จริง
    บทความที่แสดงด้วยค่าที่วัดได้ว่า AI ทำอะไรไม่ได้ มีประโยชน์กว่าบทความที่ถกความเสี่ยงทางสังคมแบบลอย ๆ โดยไม่มีกรณีใช้งานจริงมาก
    ด้วยเหตุผลเดียวกัน รายงาน ที่สัมภาษณ์สมาชิก Boko Haram เพื่อศึกษาว่า AI ถูกนำไปใช้กับการก่อการร้ายอย่างไร ก็ชวนประทับใจเช่นกัน

  • ชอบ การรีแฟกเตอร์ ด้วยตัวเองโดยไม่ใช้ AI มากจริง ๆ
    แม้จะไม่มีการเปลี่ยนแปลงที่มองเห็นได้ แต่รู้สึกพอใจที่ทำให้เว็บไซต์ซึ่งตอนนี้ยังไม่เห็นผลงานชัดเจน กลายเป็นสิ่งที่จัดการได้ง่ายขึ้นมากในอนาคต
    กระบวนการค้นหาว่าโค้ดอ้อมแปลก ๆ ในอดีตกำลังแก้ปัญหาที่มี pattern ที่เป็นที่ยอมรับแล้วอยู่ แล้วค่อยย้ายไปสู่แนวปฏิบัติที่ดีโดยไม่สร้างหนี้ทางเทคนิคใหม่ เป็นเหมือนการเล่น puzzle ที่สนุก
    เคย implement ทุกอย่างเองอย่างลวก ๆ แม้กระทั่ง authentication และได้เรียนรู้กลไกภายในด้วยวิธีที่ยาก ผลก็คือมีเรื่องให้รีแฟกเตอร์เล่นไปอีก 10 ปีข้างหน้า

    • หลังอ่าน 《Refactoring》 ของ Fowler เคยสงสัยว่าจะได้ผลกับโค้ดด้านวิทยาศาสตร์และงานวิจัยไหม แต่หลังจากลองใช้กับโครงสร้างโค้ดที่ไม่พอใจ มุมมองก็เปลี่ยนไปโดยสิ้นเชิง
      เข้าใจอย่างเป็นรูปธรรมว่าคำว่า codebase คือระบบหนึ่งระบบ หมายถึงอะไร และเริ่มมองโค้ดในระดับสูงเหมือนเป็นองค์กรหรือใยตาข่ายที่ต่อเนื่อง ซึ่งสามารถดึงและกดให้เปลี่ยนรูปได้
      การที่ AI อาจตัดกระบวนการเรียนรู้นี้ออกไป ทำให้ปัญหาของ junior developer เด่นชัดขึ้น หากต้องการได้ intuition ก็ไม่มีทางอื่นนอกจากลงไปขุดลึกด้วยตัวเอง และแม้ Naur จะเตือนไว้เมื่อ 40 ปีก่อน บทเรียนนี้ก็ยังถูกลืมซ้ำแล้วซ้ำเล่า
    • อาจเป็นเพราะได้รับ รางวัลโดปามีน คล้ายตอนดูหน้าจอ Disk Defragmenter ของ Windows 98
    • ความรู้สึกนั้นคือ ความภูมิใจในฐานะช่างฝีมือ คนที่เข้าใจไม่ต้องอธิบาย ส่วนคนที่ไม่เข้าใจ อธิบายอย่างไรก็ไม่เข้าถึง
    • สงสัยว่าระหว่างรีแฟกเตอร์ ได้สร้าง test suite ไว้เป็นตาข่ายนิรภัยเพื่อกัน regression มากแค่ไหน
    • สนุกกับกระบวนการศึกษาหลาย ๆ refactoring pattern และกรณีใช้งานจริง
  • เมื่อเอเจนต์ทำรีแฟกเตอริง มองว่า การมีมนุษย์เข้ามามีส่วนร่วม เป็นสิ่งจำเป็น
    แม้โมเดลสำหรับตรวจทานอาจหาส่วนที่โมเดลสร้างเน้นงานช่วงแรกแล้วพลาดไปได้ แต่ก็ยังน่าสงสัยว่ามันจะเข้าใจเป้าหมายของโปรเจกต์ทั้งหมดและวิธีที่โค้ดเชื่อมโยงกันจริง ๆ จนแยกแยะความซ้ำซ้อนหรือโครงสร้างที่งดงามกว่าได้หรือไม่
    การให้เอเจนต์เขียนโค้ดรับหน้าที่รีแฟกเตอริง คล้ายกับการขอให้ศัลยแพทย์อุบัติเหตุช่วยเพิ่มสมรรถภาพทางกีฬาให้ และถ้าจะทำให้ถูกต้องต้องมีมุมมองแบบองค์รวม
    แค่แบ่งไฟล์ใหญ่เป็นหลายไฟล์ถือเป็นรีแฟกเตอริงแบบผิวเผินเท่านั้น หากไม่มีทฤษฎีว่าโค้ดใดควรอยู่ด้วยกัน และอะไรควรถูกดึงออกมาเป็นฟังก์ชันยูทิลิตี ก็ใกล้เคียงกับการเอาจำนวนใหญ่ไปหั่นเป็นจำนวนเล็ก ๆ แล้วบวกกลับเข้ามาใหม่ มากกว่าจะเป็นการแยกตัวประกอบ
    เอเจนต์บางครั้งสร้างระบบที่เก็บและคำนวณค่าที่ดึงมาจาก API อยู่แล้วซ้ำอีก แต่มนุษย์สามารถมองทั้งโปรเจกต์แล้วระบุอย่างแม่นยำได้ว่ามีข้อมูลที่ต้องใช้ในคีย์บางตัวของ JSON อยู่แล้ว

    • LLM ปัจจุบันก็ทำได้ดีพอ หากสั่งรีแฟกเตอริงที่เฉพาะเจาะจงกับช่วงโค้ดหนึ่ง ๆ เช่น ความต้องการซับซ้อนอย่างให้ใช้ functools.partial แทน data class เพื่ออิมพลีเมนต์ Command pattern ก็จัดการได้
      แนวคิดที่ว่าเส้นแบ่งไฟล์แสดงขอบเขตของซับซิสเต็มเชิงตรรกะเพื่อให้อนุมานง่ายขึ้น และเนื้อหาในไฟล์อื่นโดยพื้นฐานถือเป็นสิ่งทึบ ก็มีอยู่มากในข้อมูลฝึก
      มองว่าประโยชน์ของโครงสร้างนี้ไม่ได้มีแค่คุณลักษณะบังเอิญของการรับรู้มนุษย์ แต่ยังมีด้านที่เป็นภววิสัยด้วย
    • เคยรีแฟกเตอริงที่น่าจะใช้เวลาหลายเดือนให้เสร็จส่วนใหญ่ในประมาณหนึ่งสัปดาห์ ด้วยวิธีให้มนุษย์อยู่ข้าง ๆ คอยอธิบายทิศทาง
      ประสบการณ์รีแฟกเตอริง วิธีรับมือกับโค้ดเบสของคนอื่น และการที่ในฐานะผู้ออกแบบเดิมสามารถอธิบายเจตนาในอดีตและปัจจุบันได้ ล้วนช่วยมาก
      เป็นภาษานอกกระแสที่มีความเปราะบางด้าน dependency ต่ำและ LLM จัดการง่าย หลังจากตัดอคติฝั่ง JavaScript และ Python ออกแล้ว ความเร็วงานก็เพิ่มขึ้นมาก
      เป็นโปรเจกต์ JSR-223 ที่เขียนสคริปต์ด้วยภาษายอดนิยมหลายภาษา แต่ทั้งหมดรันบน JVM ตามข้อกำหนดของสภาพแวดล้อม: https://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform
    • การประเมินว่าเอเจนต์ทำรีแฟกเตอริงไม่ได้เป็นความเห็นที่ล้าสมัยแล้ว ตอนนี้มันเก่งมาก
      ในตลาดจ้างงานนี้ ต้องใช้ เอเจนต์เขียนโค้ดที่อิงโมเดลแนวหน้ารุ่นล่าสุด และรู้ความสามารถกับข้อจำกัดของมันอย่างแม่นยำ หากให้การประเมินในทางตรงข้ามในการสัมภาษณ์ อาจเป็นเหตุให้ไม่ผ่านได้
  • บริบทที่กระชับ ไม่ได้แค่ลดการใช้โทเค็น แต่ยังปรับปรุงการให้เหตุผล และทำให้ใส่เลเยอร์ได้มากขึ้นในบริบทเดียวเพื่อจัดการอย่างชาญฉลาด
    การรีแฟกเตอริงไปสู่ abstraction ที่ดี สร้างซอฟต์แวร์ที่ generalize ได้ดีกว่า และมีแนวโน้มถูกต้องไม่เฉพาะกับกรณีที่ทดสอบ แต่รวมถึงกรณีที่อนุมานแทรกและอนุมานขยายด้วย
    มีทฤษฎีสารสนเทศและคณิตศาสตร์แบบเบย์สที่รองรับเรื่องนี้ และการที่ซอฟต์แวร์ซึ่งมีประสิทธิภาพทางเศรษฐกิจและพลังงานสูงขึ้นกลับแม่นยำขึ้นด้วยนั้น ดูเหมือนเป็นความบังเอิญที่ลงตัวอย่างยิ่ง
    แก่นสำคัญคือ การลดเอนโทรปีของโค้ด

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

    • ผมกันงบเวลาไว้ต่างหากสำหรับ การกำจัดโค้ดลวก ๆ และตอนนี้ก็กำลังทำงานนั้นอยู่ในหน้าต่างข้าง ๆ
      โดยรวมแล้วประโยชน์จาก AI นั้นมาก แต่ก็ต้องใช้เวลาจัดระเบียบ และยังคิดว่าต้องอ่านทุกบรรทัดอยู่ดี
      เคยเลื่อนการตรวจทานบางส่วนออกไปเพื่อไม่ให้ขวางความคืบหน้าของทีมอื่น แล้วภายหลังต้องชำระหนี้เทคนิคมากกว่าปกติ แต่การปลดล็อกคอขวดก่อนก็คุ้มค่า
      AI ทำให้การดึงหนี้เทคนิคมาใช้ล่วงหน้าง่ายขึ้นในทุกความหมาย และถ้านำทางดี ๆ ก็ช่วยปลดหนี้ได้ค่อนข้างดีด้วย เพียงแต่ผลลัพธ์แตกต่างกันไปตามแต่ละคน: https://news.ycombinator.com/item?id=49035455
    • ในสภาพตั้งต้นก็เห็นผลแบบเดียวกัน แต่เมื่อกำหนดทิศทางรีแฟกเตอริงให้เฉพาะเจาะจง ก็สามารถทำให้เกิดโค้ดที่แยกส่วนดีขึ้นได้
      การแสดงตัวอย่างโค้ดที่จัดโครงสร้างดี หรือใช้ repository โอเพนซอร์สให้ดูว่าอะไรควรทำและไม่ควรทำ ช่วยได้มาก
  • ประโยชน์ทางเศรษฐกิจส่วนใหญ่ของรีแฟกเตอริงมาจาก ความเข้าใจของมนุษย์ที่ดีขึ้น มากกว่าการประหยัดโทเค็น
    ทำให้แก้ incident call ตอนตี 3 ได้เร็วขึ้น ลดบั๊กที่หลุดเข้า production และออกของได้เร็วกว่าคู่แข่ง
    เหนือสิ่งอื่นใด เมื่อเข้าใจระบบ ก็จะเต็มใจรับผิดชอบและเป็นเจ้าของ ทำให้เมื่อเกิดปัญหาก็แก้ได้เร็วขึ้น และกระโจนเข้าไปปรับปรุงส่วนที่ควรปรับอย่างกระตือรือร้น

  • ประเด็นหลักคือ รีแฟกเตอริงลดการใช้โทเค็น และเป็นเรื่องดีที่บทความไม่ได้ถกในเชิงนามธรรม แต่ระบุผลเป็นตัวเลข
    อย่างไรก็ตาม Fowler กล่าวไว้ใน 《Refactoring》 ว่าเงื่อนไขจำเป็นของรีแฟกเตอริงคือชุดทดสอบที่แข็งแรง และผมมองว่าประโยชน์จริง ๆ อยู่ตรงนี้ ไม่เกี่ยวกับ AI
    การทดสอบที่ดีป้องกัน regression ที่มนุษย์หรือหุ่นยนต์สร้างขึ้น และทิ้ง specification ที่ทั้งสองฝ่ายอ่านได้ไว้ในรูปโค้ด: https://www.oreilly.com/library/view/refactoring-improving-the/9780134757681/

    • บทความลงใน martinfowler.com แต่ไม่ได้เขียนโดย Martin โดยระบุผู้เขียนเป็น Giles Edwards-Alexander ซึ่งเป็น CTO ของ Thoughtworks
    • แก่นจริง ๆ คือเงินที่ประหยัดได้มีเพียง ระดับไม่กี่สิบเซนต์ เท่านั้น
      หากคำนวณราคา Sonnet 5 ที่ 3 ดอลลาร์ต่อ MTok จำนวนเงินที่ประหยัดได้จากการเปลี่ยนแปลงในอนาคตที่แตะเลเยอร์การเข้าถึงข้อมูลคือ 39.7 เซนต์
      เมื่อพิจารณาการลดราคาของ OpenAI โมเดลเปิด และราคาต่อโทเค็นที่จะลดลงในระยะยาว อาจไม่คุ้มกับต้นทุนการให้ senior developer ที่คิดราวชั่วโมงละ 100 ดอลลาร์มาคอยนำทางรีแฟกเตอริง
  • โค้ดที่เอเจนต์สร้างกลายเป็นก้อนขนาดมหึมาที่มีแต่เอเจนต์เท่านั้นที่อ่านและเข้าใจได้ และสิ่งสำคัญในทางปฏิบัติคือความเป็นจริงนั้นเอง มากกว่าจะเป็นฟีเจอร์ บั๊ก หรือคุณสมบัติที่เกิดขึ้นเอง
    เรากำลังกลับไป พึ่งพาเครื่องมือ AI อีกครั้งเพื่อจัดการกับโค้ดที่สร้างโดยเครื่องมือ AI
    อย่างไรก็ตาม มนุษย์เองก็สร้างไฟล์ขนาดใหญ่จนน่ากลัวและ monorepo กันมาแล้ว และเพราะ LLM งานแก้ไขและรีแฟกเตอร์สิ่งเหล่านี้จึงเพิ่งเริ่มอยู่ในขอบเขตที่รับมือได้
    ในสถานการณ์ที่ codebase ใหญ่และยุ่งเหยิงเกินกว่ามนุษย์จะเข้าใจ LLM อาจช่วยเราได้ และโดยส่วนตัวแม้จะเกลียดไฟล์ยักษ์ แต่ก็รู้สึกขมขื่นที่หลักการจัดระเบียบแบบ Fowler อาจไม่สำคัญอีกต่อไปแล้ว

    • คำกล่าวที่ว่าโค้ดของเอเจนต์มีแต่เอเจนต์เท่านั้นที่เข้าใจได้นั้น โดยพื้นฐานแล้วไม่เป็นความจริง และถ้ารู้สึกเช่นนั้น แปลว่าใช้ LLM ผิดวิธี
    • โค้ดแย่ ๆ มีอยู่มาตั้งแต่เดิม แต่ AI เป็นปัญหาใหม่ที่ขยาย ขอบเขตความเสียหายจากการจ้างคนผิด ได้ 1,000 เท่า
      แม้แต่พนักงานระดับปานกลางหรือคนที่ทำได้ดีเป็นครั้งคราว ก็อาจถูกทำให้ลงมือทำสิ่งที่เมื่อก่อนทำไม่ได้เพราะขั้นตอนของบริษัทได้รวดเร็วราวสายฟ้า จนกลับกลายเป็นพนักงานแย่ ๆ ได้
    • แม้เป็น codebase ที่ LLM เขียน 100% ก็ไม่มีปัญหาในการอ่านหรือหาตำแหน่งที่ต้องการ และไม่ได้ยากกว่าตอนเขียนเอง
    • ยังไม่เคยเห็นกรณีที่โค้ดที่เอเจนต์สร้างแย่กว่าโค้ดที่แย่ที่สุดที่มนุษย์สร้าง
      ถ้าเอเจนต์สร้างไฟล์หรือฟังก์ชันขนาดมหึมา แค่สั่งว่าอย่าทำแบบนั้นมันก็ทำตาม
  • ผมมองว่า การรีแฟกเตอร์เป็นหนึ่งในสัญญาณที่ดีที่สุดของทีมพัฒนาที่แข็งแรง
    การรีแฟกเตอร์มีประโยชน์ในตัวเอง แต่เจ้าของผลิตภัณฑ์หรือบอร์ดงานฟีเจอร์มักมองไม่เห็นคุณค่านั้น
    หากทีมรีแฟกเตอร์เพื่อสุขภาพโดยรวมของซอฟต์แวร์ นั่นหมายความว่านักพัฒนารู้สึกสบายใจที่จะเสนอแนวทางเพื่อซอฟต์แวร์ที่ดี และข้อเสนอนั้นได้รับการพิจารณาอย่างจริงจัง
    ความเสื่อมของซอฟต์แวร์จะรุนแรงที่สุดเมื่อทีมไม่มีแรงจูงใจหรืออำนาจในการทำให้วิสัยทัศน์เรื่องซอฟต์แวร์คุณภาพสูงเป็นจริง และหากทีมสามารถทำตามดุลยพินิจของตนเกี่ยวกับความเป็นเลิศได้ โดยทั่วไปก็ถือเป็นสัญญาณที่ดี
    แน่นอนว่ามีกรณีทำเกินไป เช่น เขียนใหม่ทั้งหมดจาก Ruby ไป Node ไป Rust แล้วกลับมาเป็นเทคโนโลยีที่เป็นมิตรกับเอเจนต์อีกครั้ง แต่ในสภาพแวดล้อมองค์กร ทีมที่รู้สึกว่าไม่ได้รับอนุญาตให้ปรับปรุงนั้นพบได้บ่อยกว่ามาก