- การประเมิน เอเจนต์เขียนโค้ด แบบอัตโนมัติโดยมากมักดูอัตราความสำเร็จของงานบน codebase ที่กำหนดไว้ตายตัว แต่งานวิจัยนี้แยกวัดว่า ความสะอาด ของตัวโค้ดเปลี่ยนต้นทุนการสำรวจและการแก้ไขหรือไม่
- สร้าง repository แบบคู่ขั้นต่ำ 6 คู่ที่มีสถาปัตยกรรม dependency และพฤติกรรมภายนอกเหมือนกัน แต่ต่างกันเฉพาะการละเมิดกฎ SonarQube และ cognitive complexity พร้อมงานอิง hidden tests 33 งาน
- รันแต่ละงานด้วย Claude Code และ Claude Sonnet 4.6 ฝั่งละ 10 ครั้งในแต่ละคู่ repository รวมเป็น การทดลอง 660 ครั้ง โดยเอเจนต์ไม่รู้ว่ากำลังทำงานกับโค้ดฝั่งใด
- ความสะอาดของโค้ดไม่ได้เปลี่ยน อัตราผ่าน แต่ในโค้ดที่สะอาดกว่า ตัวชี้วัดเทียบเท่า token ลดลง 7~8% และการกลับมาเปิดไฟล์ซ้ำลดลง 34%
- ไม่ใช่แค่โมเดลหรือ prompt เท่านั้น แต่ สภาพของ codebase ยังเป็นตัวแปรเชิงปฏิบัติที่กำหนดต้นทุนการคำนวณและประสิทธิภาพการสำรวจของเอเจนต์
คำถามวิจัยและการตั้งปัญหา
- เอเจนต์เขียนโค้ดแบบอัตโนมัติกำลังแพร่หลายอย่างรวดเร็ว
- จากการสำรวจโปรเจกต์ GitHub 128,018 โปรเจกต์ในปี 2026 พบสัญญาณกิจกรรมของเอเจนต์ใน 22~29% ของโปรเจกต์ ณ ช่วงเวลาที่ยังไม่ถึง 1 ปีหลังการเปิดตัวเอเจนต์ที่ใช้งานได้จริงตัวแรก
- ต้นทุนการรันเอเจนต์ก็ไม่น้อย
- บน SWE-bench Verified งานเดี่ยวใช้โดยเฉลี่ยประมาณ 4 ล้าน token เมื่ออิง frontier LLM
- ในปริมาณการใช้งานทั้งหมด input token เป็นสัดส่วนส่วนใหญ่
- การประเมินเดิมมุ่งเน้นว่า แก้งานได้หรือไม่ บน benchmark อย่าง SWE-bench และงานวิจัยล่าสุดเริ่มวัดการใช้ทรัพยากรร่วมกับอัตราผ่าน
- การเปรียบเทียบทั่วไปคือการตรึง codebase ไว้ แล้วเปลี่ยนเอเจนต์หรือ scaffolding
- งานวิจัยนี้ทำกลับกัน คือคงเอเจนต์และงานไว้ แล้วเปลี่ยนเฉพาะ ความสะอาดของ codebase เพื่อเปรียบเทียบ
ความสะอาดของโค้ดและการสร้างคู่ขั้นต่ำ
- ความสะอาดของโค้ดถูกมองเป็นชุดคุณลักษณะที่เชื่อมโยงกับโค้ดที่บำรุงรักษาได้
- อ่านง่าย
- cognitive complexity ต่ำ
- helper แยกเป็นสัดส่วนดี
- ตั้งชื่อชัดเจน
- มี dead code, logic ซ้ำ และ accidental coupling น้อย
- งานวิจัยไม่ได้กำหนดความสะอาดของโค้ดเป็นนิยามเชิงรูปแบบที่เข้มงวด แต่ใช้จำนวนการละเมิดกฎของ static analysis จาก SonarQube เป็นตัวชี้วัดแทนแบบหลวม ๆ
- เครื่องมือที่ใช้คือ SonarQube Cloud Enterprise Edition
- ชุดกฎคือ “default quality gate”
- repository แบบคู่ขั้นต่ำถูกสร้างให้ต่างกันเฉพาะความสะอาดภายใน และให้เงื่อนไขต่อไปนี้ตรงกัน
- ภาษาและ framework เดียวกัน
- dependency เดียวกัน
- test เดียวกันหรือ test coverage เทียบเท่า
- พฤติกรรมภายนอกเหมือนกัน
- ความเท่าเทียมของพฤติกรรมหมายถึงเมื่อให้ input เดียวกัน ผลลัพธ์และการเปลี่ยนสถานะที่สังเกตได้จากภายนอกต้องเหมือนกัน
- การตรวจสอบจริงทำโดยให้ผ่าน test suite เดียวกันด้วย coverage เดียวกัน หรือผ่านคู่ test ที่ปรับชดเชยการเปลี่ยนแปลงที่สังเกตไม่ได้ เช่น refactoring
pipeline สร้างคู่ขั้นต่ำ: Slopify และ Vibeclean
- คู่ขั้นต่ำถูกสร้างแบบสองทิศทาง
- Slopify ทำให้ repository ที่สะอาดสกปรกขึ้น
- Vibeclean จัดระเบียบ repository ที่สกปรก
-
Slopify
- เปลี่ยน codebase ที่สะอาดให้เป็นเวอร์ชันเหมือนเติบโตมาโดยไม่มี code review หรือ linting
- เป้าหมายไม่ใช่โค้ดที่จงใจทำให้พัง แต่เป็นเวอร์ชันประวัติศาสตร์ทางเลือกในกรณีที่ไม่มี static analysis
- แต่ละขั้นตอนดำเนินการโดยเอเจนต์ตัวใหม่
- Build: build repository และทำให้ test ผ่าน จากนั้นตรึงคำสั่งไว้ใน
build instructions.md - Explore: สำรวจ repository และเขียน
summary.mdสำหรับแต่ละ directory ที่เป็นเป้าหมายการจัดระเบียบ - Transform: ใส่การละเมิดกฎ SonarQube ใน directory ที่กำหนด และรัน test ใหม่หลังแต่ละ pass เพื่อปฏิเสธการเปลี่ยนแปลงที่ทำให้พัง
- Build: build repository และทำให้ test ผ่าน จากนั้นตรึงคำสั่งไว้ใน
- เพิ่ม cognitive complexity ด้วยการ inline helper, ทำ logic ซ้ำตาม path, เพิ่ม dead code, รวมบาง module เป็นไฟล์เดียว เป็นต้น
-
Vibeclean
- จัดระเบียบ codebase ที่มีการละเมิดกฎจำนวนมากตามธรรมชาติ โดยคงพฤติกรรมภายนอกไว้
- รายการงานของเอเจนต์คือรายการ issue ที่ analyzer ตรวจพบ และแต่ละ issue เชื่อมกับช่วงของโค้ด
- ขอบเขตการแก้ไขจำกัดอยู่ที่ปัญหาที่ analyzer ระบุ และไม่ได้มุ่ง redesign ทั้งระบบ
- ทำงานสองขั้นตอน
- Build: ตรวจสอบคำสั่ง build และ test แล้วตรึงไว้ใน
build instructions.md - Clean: จัดระเบียบการละเมิดกฎ analyzer แบบเชิงกลไกทีละ module และตรวจสอบความเท่าเทียมของพฤติกรรมด้วย test หลังจัดการแต่ละ module
- Build: ตรวจสอบคำสั่ง build และ test แล้วตรึงไว้ใน
- ดำเนินการเช่นลบ literal string ซ้ำ, ลบโค้ดที่ถูก comment out, แทนที่ idiom ของ collection แบบ legacy, ลบ branch ที่เป็น dead code
- เมื่อ analyzer ชี้ไปที่โครงสร้างขนาดใหญ่มากจริง ๆ ก็เปลี่ยน dispatch switch กว่า 200 บรรทัดเป็น helper ที่มีชื่อ หรือแยก persistence helper ออกจาก class ขนาด 2,800 บรรทัด
- อย่างไรก็ตาม การแยกออกมาอาจไม่ได้กำจัด complexity แต่กระจายไปยัง method จำนวนมากขึ้น และโครงสร้างขนาดใหญ่มากที่สุดบางส่วนยังคงเป็น
wontfix
repository benchmark และการออกแบบงาน
- benchmark สร้างบน Harbor framework v0.4.0
- ใช้ repository แบบคู่ขั้นต่ำทั้งหมด 6 คู่
- เน้น Java 3 คู่ และเน้น Python 3 คู่
- บาง repository มีโค้ดภาษาอื่นเล็กน้อย
- ประกอบด้วย repository open source สาธารณะ 3 คู่ และ codebase ภายในของ SonarSource ที่ไม่เปิดเผย 3 คู่
- คู่ที่ไม่เปิดเผยทำหน้าที่เป็น การป้องกันการท่องจำ เผื่อ LLM ที่ประเมินอาจเคยฝึกด้วย repository สาธารณะ
- ตัวเลขสำคัญของฝั่งสะอาดและฝั่งสกปรกในแต่ละ repository มีดังนี้
sonar-sca*: issue 94 / 2,825, issue density 0.73 / 20.66, cognitive complexity density 30.6 / 56.5sonar-caas-poc*: issue 16 / 855, issue density 0.61 / 27.16, cognitive complexity density 179.8 / 218.9sonarcloud-codedatalake*: issue 199 / 1,319, issue density 4.36 / 34.39, cognitive complexity density 34.0 / 216.5commons-bcel: issue 694 / 2,711, issue density 12.60 / 49.46, cognitive complexity density 102.8 / 108.3genie: issue 152 / 1,262, issue density 1.28 / 10.81, cognitive complexity density 22.2 / 23.5ckan: issue 1,006 / 3,632, issue density 7.54 / 27.50, cognitive complexity density 69.3 / 76.5
- การออกแบบงานปฏิบัติตามกฎสามข้อ
- ผ่าน hotspot: วางงานให้ผ่านบริเวณโค้ดที่มีความต่างด้าน issue density และ cognitive complexity สูงในทั้งสองฝั่งของคู่
- คำอธิบายที่สังเกตได้จากภายนอก: ให้เฉพาะ input, output และ scenario ตัวอย่าง ไม่ให้ชื่อไฟล์ ชื่อ function หรือชื่อโครงสร้างภายใน
- test ผ่าน public surface: รัน hidden tests ผ่าน interface ที่แอปพลิเคชันมอบให้ผู้เรียก เช่น CLI, HTTP route, library/API
- การสร้างงานแบ่งกันระหว่างเอเจนต์กับมนุษย์
- เอเจนต์เปรียบเทียบ variant ที่สะอาดกับ variant ที่สกปรกเพื่อทำแผนที่ความต่าง
- เอเจนต์อีกตัวเขียนโครงร่างงานและความเป็นไปได้ในการทดสอบ
- มนุษย์เลือก แก้ไข และคัดสรรโครงร่างที่ดูสมเหตุสมผลและน่าสนใจ
- เอเจนต์ตัวที่สามสร้างคำสั่งจริง, hidden public-surface tests และ reference implementation สำหรับใช้ภายใน
- reference implementation ต้องผ่าน hidden tests ใน repository ทั้งสองฝั่ง
- repository ก่อนแก้ไขต้องไม่ผ่าน hidden tests
- งานที่ยังไม่เข้าเงื่อนไขหลังวนซ้ำสองรอบ จะถูกมนุษย์เขียนใหม่หรือนำออก
- จำนวนงานสุดท้ายคือ 33 งาน แบ่งเป็นสาม track
- งาน cognitive hotspot 13 งาน: ผ่านบริเวณ complexity หนาแน่นสูงของ method เดียวหรือ class เดียว
- งาน multi-module 14 งาน: ต้องแก้ไขข้าม module ตั้งแต่สอง module ขึ้นไป
- งาน calibration 6 งาน: ให้ทั้งสองฝั่งทำงานเรียบง่ายในบริเวณเดียวกัน เพื่อตรวจสอบว่ามีความเปลี่ยนแปลงที่ไม่เกี่ยวกับความสะอาดหรือไม่
การตั้งค่าการทดลองและตัวชี้วัด
- การทดลองทั้งหมดดำเนินการด้วย Claude Code โดยใช้ชุดเครื่องมือพื้นฐาน
- ตัวเลขที่รายงานมาจากการรัน Claude Sonnet 4.6
- Claude Haiku 4.5 ก็ถูกลองคร่าว ๆ กับชุดงานเดียวกัน แต่ถูกตัดออกจากผลหลักเพราะอัตราผ่านต่ำเกินไปจนอ่านความต่างของ footprint ได้ไม่ชัด
- เอเจนต์อ่านเฉพาะคำอธิบายงาน
- ไม่ได้รับ priming เพิ่มเติมเกี่ยวกับความสะอาดของโค้ด
- ไม่รู้ว่าตัวเองกำลังทำงานกับฝั่งใดของคู่ขั้นต่ำ
- แต่ละงานรันฝั่งละ 10 ครั้งในแต่ละคู่
- จำนวนการทดลองรวมคือ 33 × 2 × 10 = 660 ครั้ง
- แต่ละการรันทำใน sandbox แบบ containerized
- จำกัด CPU, memory, storage และ wall-clock time
- เข้าถึง public package registry ได้
- base image รวม toolchain, build cache และ service ตามแต่ละ repository
- ภายในคู่ มีเพียง source tree ที่ mount ที่
/appเท่านั้นที่ต่างกัน
- บันทึกตัวชี้วัด 10 รายการ
- อัตราผ่าน: สัดส่วนที่ hidden tests ผ่านในสถานะสุดท้าย
- input token: จำนวน token ที่โมเดลอ่านทุก turn โดยส่วนใหญ่เป็นเนื้อหาไฟล์และการส่งบทสนทนาก่อนหน้าซ้ำ
- output token: ผลลัพธ์ทั้งหมดที่โมเดลและ sub-agent สร้าง รวม prose, code, ร่องรอย reasoning และ tool call
- จำนวนอักขระ reasoning: เนื่องจาก Anthropic API ไม่เปิดเผย reasoning token แยกต่างหาก จึงนับจำนวนอักขระข้อความธรรมดาใน reasoning content block
- จำนวน turn ของบทสนทนา: จำนวนการแลกเปลี่ยน agent-tool ทั้งหมด
- จำนวน turn ก่อนการแก้ไขครั้งแรก: จำนวน turn ที่ใช้ก่อนการแก้ไฟล์ครั้งแรก
- จำนวนอักขระก่อนการแก้ไขครั้งแรก: จำนวนอักขระของบทสนทนาในช่วงเดียวกัน
- จำนวนไฟล์ที่อ่าน: จำนวนไฟล์ไม่ซ้ำที่เปิดดูระหว่างการรัน
- การกลับมาเปิดไฟล์ซ้ำ: จำนวนครั้งที่กลับไปอ่านไฟล์ที่เคยอ่านและแก้ไขแล้ว
- จำนวนบรรทัดที่แก้ไข: จำนวนบรรทัด source ที่ patch สุดท้ายเปลี่ยนแปลง
- การกลับมาเปิดไฟล์ซ้ำปรากฏใน flow เช่น
อ่าน → แก้ไข → อาจทำอย่างอื่น → กลับมาอ่านอีกครั้ง- งานวิจัยตีความว่าเป็นสัญญาณของ ความไม่แน่ใจ ต่อการแก้ไขก่อนหน้า มากกว่าการสำรวจอย่างกว้าง
- ตัวชี้วัด footprint ของเอเจนต์อาจแปรผันมากแม้รันงานเดียวกันซ้ำที่ temperature คงที่ จึงใช้การกรอง outlier
- ภายในแต่ละชุด
(งาน, ฝั่ง)การรันที่เบี่ยงจาก median ของการทำซ้ำ 10 ครั้งเกิน 50% จะถูกนำออกก่อนคำนวณค่าเฉลี่ย - ในทางปฏิบัติ ถูกนำออก 9.7% ของการรันทั้งหมด
- ภายในแต่ละชุด
- ตัวเลขระดับ dataset เป็น micro average จากงาน 33 งาน
- สำหรับแต่ละตัวชี้วัด รวมค่าเฉลี่ยฝั่งสะอาดและฝั่งสกปรกของแต่ละงาน แล้วคำนวณความต่างเป็นอัตราส่วน
- อัตราผ่านเป็นข้อยกเว้น โดยรายงานเป็นความต่างแบบจุดเปอร์เซ็นต์สัมบูรณ์ระหว่างฝั่งสะอาดกับฝั่งสกปรก
ผลลัพธ์: ส่งผลต่อต้นทุนการสำรวจมากกว่าอัตราความสำเร็จ
- ความสะอาดของโค้ดไม่ได้สร้างความเปลี่ยนแปลงอย่างมีนัยสำคัญต่อ อัตราผ่าน ของเอเจนต์
- ในโค้ดที่สะอาดกว่า ตัวชี้วัดเทียบเท่า token ลดลง 7~8%
- การกลับมาเปิดไฟล์ซ้ำ ลดลง 34% แสดงให้เห็นว่าโค้ดสะอาดสามารถลดต้นทุนการย้อนกลับไปอ่านซ้ำได้ แม้เป็นงานเดียวกัน
- ดังนั้นความสะอาดของโค้ดจึงมองได้ว่าเป็นอีกแกนหนึ่งที่กำหนดต้นทุนการรันเอเจนต์ ควบคู่กับการเลือกโมเดล harness และ prompt
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
เป็นคำถามที่น่าสนใจและควรศึกษา แต่ผมสงสัยมากกับ การออกแบบการทดลอง
ในการทดลอง พวกเขาใช้ Opus 4.6 สังเคราะห์ codebase แบบ “คุณภาพลดลง” หรือ “จัดระเบียบแล้ว” เพื่อใช้เปรียบเทียบกัน
ที่แย่กว่านั้นคือไม่ได้ควบคุมว่าได้ทำให้เทสต์ของแอปพลิเคชันพังหรือไม่
“อัตราการผ่านให้คะแนนสถานะสุดท้ายของเอเจนต์ตามเกณฑ์เทสต์ที่ซ่อนอยู่ซึ่งเราเขียนขึ้นสำหรับแต่ละงาน เราไม่ได้ตรวจสอบว่าเอเจนต์ทำให้เทสต์ที่ไม่เกี่ยวข้องซึ่งมีอยู่แล้วใน repository พังหรือไม่ และแม้ว่าวิธีแก้ของฝั่งสะอาดกับฝั่งรกจะผ่านเทสต์ที่ซ่อนอยู่ทั้งคู่ ก็อาจแตกต่างกันในเทสต์ที่ไม่ได้ถูกนำมาคิดคะแนน”
ถ้าไม่ควบคุมคุณภาพของผลลัพธ์สุดท้าย ข้อสรุปเกี่ยวกับ ปริมาณโทเคนที่ใช้ ก็แทบไม่มีความหมาย
ถ้าจะเปรียบเทียบอย่างเป็นธรรม ควรเขียนชุดเทสต์เดียวที่ทำงานได้กับทั้งสองโปรเจกต์ในแต่ละคู่
ไม่ได้หมายความว่างานวิจัยนี้ดี แต่ผมเข้าใจการตัดสินใจแบบนั้นได้ เพราะการผ่านเทสต์ไม่จำเป็นต้องสัมพันธ์กับประสิทธิผลของเอเจนต์เสมอไป
มันแค่แสดงสถานการณ์ที่ในเชิงฟังก์ชันดูเหมือนจะโอเค แต่ต้องจ่ายค่าโทเคนมากขึ้นเพื่อทำงานให้เสร็จ
ความเหนื่อยล้าจาก AI น่าเบื่อมานานแล้ว และตอนนี้มันก็เจ็บปวดเสียมากกว่า
จากประสบการณ์ของผม ความต่างของประสิทธิภาพเอเจนต์ระหว่าง codebase ที่เต็มไปด้วย dead code, โค้ดซ้ำ, เส้นทางสำรองที่ไปไม่ถึง, abstraction ที่รั่ว, design pattern ที่ยังไม่สุกงอม กับ codebase ที่ data flow ชัดเจน มี encapsulation และโครงสร้างสะอาดนั้นค่อนข้างมาก
ในโค้ดแย่ ๆ โมเดลแนวหน้าทุกตัวต้องผ่านหลายรอบของ code review, การตรวจคุณภาพ และการแก้ไข ส่วนในโค้ดดี ๆ ผมเห็นว่ามักทำถูกได้ตั้งแต่ครั้งที่ 1–2
เราสามารถให้ LLM รันสคริปต์เพื่อตรวจรายการเหล่านี้ และบังคับใช้สคริปต์เดียวกันเป็น pre-commit hook ได้
การใส่การตั้งค่าแบบนี้อย่างเข้มงวดในทุก codebase ที่ผมทำงานด้วยเป็นสิ่งที่ให้ผลมากที่สุดในการเขียนโค้ดแบบเอเจนต์
ผมเขียนรายละเอียดเพิ่มเติมเกี่ยวกับ linter หลายตัวที่ผมใช้ไว้ที่นี่: https://www.balajeerc.info/Use-Deterministic-Guardrails-for-...
ผมคิดว่าเอเจนต์ก็ย่อมทำงานกับ codebase ที่สะอาดได้ดีกว่า codebase ขนาดใหญ่ที่เละเทะอยู่แล้ว
เหมือนกับที่มันทำงานได้ดีขึ้นเมื่อมีสเปกที่เขียนดีและเข้าถึงเอกสารได้
ถึงจุดหนึ่ง pattern แย่ ๆ เหล่านั้นก็เริ่มติดมาที่ตัวผมด้วย
ที่นี่มี paper ที่พยายามตอบคำถามนี้อยู่ และคำให้การเชิงประสบการณ์ส่วนตัวเพียงทำให้ผู้อ่านมีอคติ ไม่ได้เพิ่มคุณค่าในการสรุปประเด็นอย่างเป็นกลาง
การอภิปรายที่มีประโยชน์ที่สุดน่าจะเป็นการที่ทุกคนอ่าน paper แล้ววิจารณ์ระเบียบวิธีหรือผลลัพธ์
แต่คนที่บ่นว่า LLM ไม่ค่อยดี โดยทั่วไปดูเหมือนเป็นประเภทที่มี codebase รก
เคล็ดลับที่ผมเห็นว่าใช้ได้ดีคือ ถ้าเป็น Python ให้สั่ง refactor ประมาณนี้
“Refactor โค้ด Python ให้เป็น Pythonic มากขึ้น เช่น ลด class และ singleton โดยเฉพาะถ้ามีการเพิ่มความเร็ว โค้ด Python ต้องเป็นไปตามมาตรฐานการจัดโครงสร้างโค้ดที่คาดหวังจากโค้ดของแพ็กเกจ Python โอเพนซอร์สยอดนิยม โดยไม่มี performance regression ใน benchmark”
สำหรับโค้ด Rust ผมลองใช้รูปแบบนี้
“codebase Rust ใน
/srcบวมจนมีหลายไฟล์ที่ยาวเกิน 1,000 บรรทัด ให้ refactor codebase Rust ให้สอดคล้องกับมาตรฐานการจัดโครงสร้างโค้ดที่คาดหวังจากโค้ด Rust โอเพนซอร์สยอดนิยม โดยไม่มี performance regression ใน benchmark”prompt แบบนี้ดูเหมือนจะทำให้ประสิทธิภาพเอเจนต์ดีขึ้น เพราะ a) จัดโครงสร้างโค้ดใหม่อย่างมีตรรกะ และ b) ชื่อไฟล์ให้ hint เชิงความหมายเกี่ยวกับตำแหน่งของโค้ดที่เกี่ยวข้อง
ในไฟล์บวม ๆ 5,000 บรรทัด เอเจนต์ไม่มีประสิทธิภาพเพราะต้องอ่านหลายก้อนเพื่อหาโค้ดที่เกี่ยวข้อง
benchmark performance ก็มักดีขึ้นหลัง refactor ด้วย โดยเฉพาะใน Rust ที่คอมไพล์ได้ ซึ่งอาจเป็นเรื่องบังเอิญ แต่ก็ไม่ใช่เรื่องให้บ่น
โดยพื้นฐานแล้ว เครื่องมือเขียนโค้ดแบบเอเจนต์มักลังเลที่จะลบโค้ด ต่อให้สั่งให้ลบก็เถอะ และจะพยายามทุกวิถีทางที่จะเก็บโค้ดเก่าไว้ หรือเพิ่มความซับซ้อนเพื่อให้โค้ดนั้นยังถูกเรียกได้ต่อไป
ถ้าคุณแค่กำลังทำ prototype มันน่ารำคาญมาก เพราะสุดท้าย dead code จะสะสมเยอะ และทำให้สับสนในภายหลังเมื่อพยายามเพิ่มฟีเจอร์
พอรู้เรื่องนี้แล้ว ก็แค่ขอให้มันกำจัด legacy ออกไป
การรักษา codebase ให้สะอาดจะกระตุ้นให้ AI ทำสิ่งที่ถูกต้อง ถ้ามีเทสต์เยอะ มันจะเพิ่มเทสต์มากขึ้นเมื่อสร้างฟีเจอร์ใหม่ และถ้ามีเอกสาร มันจะอัปเดตให้เองโดยไม่ต้องบอก
เมื่อ code harness ดีขึ้น สิ่งเหล่านี้จะค่อย ๆ ถูกฝังมาในตัว และคนที่มีประสบการณ์ prompt น้อยก็จะได้ผลลัพธ์ที่ดีได้ง่ายขึ้น
โดยปกติผมจะให้มันรีวิวก่อนแล้วทำรายการประเด็นรีวิว จากนั้นค่อยดูแต่ละรายการร่วมกัน แล้วผมตัดสินใจว่าใช่/ไม่ใช่ หรือเสนอการแก้ไขเพิ่มเติม
โดยเฉพาะถ้าไม่มี end-to-end tests ที่รอบคอบสำหรับทั้งระบบ
ยังไงกฎ style ของโค้ดก็ใส่ไว้ใน CLAUDE.md แล้ว
แนวทางที่เรียกว่า “ไปป์ไลน์เอเจนต์สำหรับจัดระเบียบรีโพซิทอรีที่เละเทะ” ดูแย่มาก และแค่นี้ก็น่าจะเพียงพอที่จะปฏิเสธงานวิจัยทั้งหมดแล้ว
ดูเหมือนว่าในงานนี้ ครึ่งหนึ่งของ คู่ขั้นต่ำ ถูกสร้างขึ้นด้วยวิธีแบบนั้น
ผมจะไม่เชื่อข้อสรุปใด ๆ เลยที่ต้องตั้งสมมติฐานว่ารีโพซิทอรีที่ AI “จัดระเบียบ” แล้วเป็นตัวแทนของโค้ดเบสที่ดีจริง ๆ
“ความสะอาด” ในที่นี้ไม่ใช่การขอให้เอเจนต์แค่เขียนโค้ดให้ดีขึ้น
เราให้รายการ การละเมิดกฎของ static analyzer 50~100 รายการพร้อมจำนวนบรรทัดของโค้ด แล้วขอให้กำจัดสิ่งเหล่านี้ออก
จากนั้นตรวจสอบว่าการละเมิดกฎถูกแก้ไขแล้วหรือไม่
การใช้ LLM เขียนโค้ดใหม่เพื่อกำจัดการละเมิดเหล่านี้เป็นแนวปฏิบัติที่ค่อนข้างเป็นที่ยอมรับ
แนวทางแบบ one-shot ที่ใช้ LLM ของ Sonar [1] เปิดใช้งานมานานกว่า 1 ปีแล้ว และแนวทางแบบเอเจนต์ล่าสุด [2] ก็ทำงานแบบเดียวกันได้ค่อนข้างดี
[1] https://www.sonarsource.com/solutions/ai/ai-codefix/
[2] https://www.sonarsource.com/products/sonarqube/remediation-a...
ผมคิดว่ามันย่อมส่งผลอย่างแน่นอน
ไม่มีโมเดลใดใส่โค้ดเบสจริงทั้งหมดไว้ในบริบทได้ และต้องไล่อ่านโค้ดเหมือนมนุษย์
คือใช้วิธีค้นหาและอ่านไฟล์
ถ้าไฟล์อยู่ในตำแหน่งที่คาดไว้ และถูกตั้งชื่อแบบที่โมเดลหรือมนุษย์น่าจะค้นหาเป็นอย่างแรก ก็จะเจอตั้งแต่ครั้งแรก แต่ถ้าไม่ใช่ ก็ต้องค้นหาลึกขึ้นและลองหลายครั้ง
แค่ดูทุกพาธ ข้ามส่วนที่จัดการไปแล้ว แล้วไปพาธถัดไปก็พอ
ค่อนข้างคล้ายกับวิธีที่นักพัฒนาทำ
ในงานที่คล้ายกันซึ่งกำลังทำอยู่ที่ NJIT เราก็เห็นผลลัพธ์คล้ายกัน เราเรียกสิ่งนี้ว่า การแพร่กระจายคุณภาพเชิงบริบท
ส่วนที่น่าสนใจตรงนี้คือสถานการณ์จริงที่พบได้บ่อยในอุตสาหกรรม เช่น โค้ดเบสที่มีคุณภาพปะปนกัน หรือโค้ดเบสที่มีทั้งแพตเทิร์นโค้ด legacy และแพตเทิร์น “ที่ดี” แบบใหม่กว่า จนทำให้เอเจนต์สับสนเรื่องธรรมเนียมปฏิบัติ
การออกแบบแบบคู่ขั้นต่ำจริง ๆ แล้วเป็นหนึ่งในจุดแข็ง เพราะพยายามแยก ความสะอาด ออกจากปัจจัยอื่นอย่างโครงสร้าง dependency และการทดสอบ แทนที่จะเปรียบเทียบข้ามรีโพซิทอรี
อย่างไรก็ตาม การใช้โค้ดที่ LLM สร้างให้ “เละ” นั้นค่อนข้างน่ากังขา เพราะไม่ใช่วิธีเชิงกลไกหรือมีมนุษย์ชี้นำ
คำวิจารณ์ที่ใหญ่ที่สุดคือการเลือกไม่ตรวจสอบชุดทดสอบทั้งหมดอย่างที่หลายคนชี้ไว้ถูกต้องแล้ว ข้ออ้างเรื่อง “ความเท่าเทียมเชิงพฤติกรรม” จะใช้ได้เท่าที่การทดสอบและ coverage รองรับเท่านั้น
เหตุผลที่สมมติฐานนี้น่าเชื่อมีสองข้อ 1) LLM เลียนแบบสิ่งที่เห็นในโค้ดเบส ดังนั้น garbage in, garbage out จึงสมเหตุสมผล 2) มันสอดคล้องกับสิ่งที่วิศวกรจำนวนมากรู้สึกโดยสัญชาตญาณจากการใช้โมเดลเหล่านี้ในช่วง 1~2 ปีที่ผ่านมา
งาน greenfield แทบจะง่ายกว่าการเข้าไปร่วมในโค้ดเบสที่วุ่นอยู่เสมอ และความยุ่งเหยิงเกิดจากการผสานรวมที่ซับซ้อนกับการดูแลระบบเพื่อวัตถุประสงค์ legacy
ต่อให้เอเจนต์เรียนรู้วิธีฝ่าดง stub และ โค้ด WET ที่ตัวเองทิ้งไว้ได้ เราอยากได้โค้ดเบสที่มนุษย์ตามไม่ทันจริง ๆ หรือว่ากำลังเกิดอะไรขึ้น?
ดังนั้นโดยส่วนตัวแล้ว อย่างน้อยผมก็อยากให้โค้ด เล่าเป็นโค้ด ว่ามันทำอะไร
แม้จะมี logic ในฟังก์ชันเล็ก ๆ สองตัวที่ดึงออกมาเป็น shared helper ได้ แต่บางครั้งโปรแกรมเมอร์มนุษย์จะไม่ทำ เพราะรู้ว่า abstraction นั้นจะรก และถ้าต้องเปลี่ยนหนึ่งในสองตัวเพียงเล็กน้อยก็อาจพังได้
การได้เห็นสิ่งนี้ถูกวัดเป็นตัวเลขน่าสนใจดี
โครงสร้างที่สะอาด ดูเหมือนจะลดภาระทางความคิดของทั้งมนุษย์และเอเจนต์ และจึงอธิบายได้ว่าการตั้งชื่อกับการแยกเป็นโมดูลสำคัญกว่าที่คิด
มันวัดเชิงปริมาณได้ยาก แต่ตัวชี้วัดคุณภาพโค้ดทั้งหมดก็พยายามจับสิ่งนี้ในท้ายที่สุด
เมื่อมองจากเกณฑ์นั้น ถ้าตัวชี้วัดคุณภาพโค้ดที่ใช้สมเหตุสมผล ก็เป็นข้อสรุปที่ไม่น่าประหลาดใจนัก
ถ้าตัวชี้วัดคุณภาพดีในบริบทของ coding agent ผลลัพธ์ที่ควรคาดหวังก็คือแบบนี้
โทเค็นจำนวนมากถูกใช้ไปกับการสำรวจโค้ด กระบวนการคือการค้นหาโค้ดหรือไล่ตามจุดที่ถูกเรียก เพื่อสร้างบริบทให้เพียงพอสำหรับทำงาน
ถ้าให้เอเจนต์เข้าถึง LSP ได้ในรูปแบบใดรูปแบบหนึ่ง และถ้าเป็น monorepo ก็ให้คำแนะนำแบบลำดับชั้นผ่านไฟล์อย่าง AGENTS.md จะลดการใช้โทเค็นในการสำรวจได้มาก
แต่โค้ดเบสที่กระจัดกระจายสุดท้ายก็ยังต้องมีการสำรวจในรูปแบบใดรูปแบบหนึ่งเพื่อแก้แต่ละงาน
และการสำรวจนี้ไม่ใช่แค่การใช้โทเค็นเท่านั้น ในแต่ละขั้นจะมีความหน่วงแบบไป-กลับซ้ำ ๆ ตั้งแต่เวลารอของ LLM, prefill, decoding, เอาต์พุตไปสู่การ parsing ของเอเจนต์, การเรียก tool, การตอบกลับของ tool แล้ววนกลับไปที่ LLM
บางส่วนทำขนานกันได้ แต่ในทางปฏิบัติส่วนใหญ่เป็นแบบลำดับ ทำให้งานช้าลงมาก
หากต้องการใช้เอเจนต์อย่างมีประสิทธิภาพ locality และโครงสร้าง คือหัวใจสำคัญ context window มีขีดจำกัดเสมอ และ attention ภายในนั้นก็ไม่ได้สม่ำเสมอ
จากประสบการณ์ของผม ทุกสิ่งที่ส่งผลต่อวิศวกรก็ส่งผลต่อเอเจนต์ด้วย
ไม่ว่าจะเป็น abstraction ที่ดี เมธอดที่มีขนาดเหมาะสม ชื่อที่ดี โครงสร้างภายในบริการและระหว่างบริการที่มีหลักการ รวมถึง unit test
สิ่งเหล่านี้ในอดีตเป็นงานของวิศวกร และมีไว้เพื่อทำให้ผู้อื่นมีส่วนร่วมกับโค้ดได้ง่ายขึ้น
ตอนนี้มันไม่ได้ช่วยแค่ผู้อื่น แต่ยังช่วยให้ เอเจนต์อื่น ๆ มีส่วนร่วมกับโค้ดได้ง่ายขึ้นด้วย