- เมื่อ AI เอเจนต์เข้ามาเป็นศูนย์กลางของการเขียนโค้ด แนวปฏิบัติที่เคยเป็นเพียง ‘ตัวเลือก’ อย่างการทดสอบ การทำเอกสาร และ static typing จึงกลายเป็นองค์ประกอบที่ขาดไม่ได้
- การครอบคลุมโค้ด 100% ถูกกำหนดเป็นข้อบังคับ เพื่อให้ทุกบรรทัดของโค้ดได้รับการตรวจสอบจริงและมีตัวอย่างการรันรองรับ
- โครงสร้างไดเรกทอรีและการตั้งชื่อไฟล์ ต้องชัดเจน เพื่อให้ LLM สำรวจโค้ดเบสได้ง่าย และสนับสนุนการแยกไฟล์เป็นหน่วยเล็ก ๆ
- สภาพแวดล้อมการพัฒนาที่รวดเร็ว ชั่วคราว และรองรับการทำงานพร้อมกัน ต้องถูกสร้างขึ้นเพื่อให้หลายเอเจนต์ทำงานแบบขนานได้
- หัวใจสำคัญคือการรักษาระบบนิเวศของโค้ดที่ AI เชื่อถือได้ด้วย ระบบชนิดข้อมูลแบบสแตติกและเครื่องมือควบคุมคุณภาพอัตโนมัติ
AI กับความจำเป็นของ ‘โค้ดที่ดี’
- เป็นเวลานานที่นักพัฒนามองว่า การทดสอบ การทำเอกสาร โมดูลขนาดเล็ก และ static typing เป็นเกณฑ์ของโค้ดที่ดี แต่ในความเป็นจริงมักถูกละไว้
- แต่ AI เอเจนต์ไม่สามารถจัดระเบียบโค้ดได้ดีด้วยตัวเอง ดังนั้นแนวปฏิบัติเหล่านี้จึงจำเป็นอย่างยิ่ง
- เพื่อไม่ให้เอเจนต์ทำงานไปในทิศทางที่ผิด จึงจำเป็นต้องมี การตั้ง guardrail ที่ชัดเจนและการบังคับใช้อย่างจริงจัง
- หากมี guardrail ที่แข็งแรง LLM จะค่อย ๆ ลู่เข้าสู่เส้นทางที่ถูกต้องเท่านั้น แต่ในสภาพแวดล้อมที่ไม่สมบูรณ์ มันจะขยายปัญหาให้รุนแรงขึ้น
การครอบคลุมโค้ด 100%
- ทีมงานกำหนดให้ การครอบคลุมโค้ด 100% เป็นข้อบังคับ ซึ่งไม่ใช่แค่เพื่อป้องกันบั๊ก แต่เพื่อ ยืนยันการทำงานของโค้ดทุกชิ้นที่เอเจนต์เขียนขึ้น
- ที่ระดับ 95% หรือ 99.99% จะยังไม่ชัดเจนว่าโค้ดที่ไม่ถูกทดสอบมาจากไหน แต่ที่ 100% จะสามารถ ระบุทุกบรรทัดที่ยังไม่ได้รับการตรวจสอบได้อย่างชัดเจน
- รายงานการครอบคลุมถูกใช้เป็นรายการงานที่ต้องทดสอบ และ เมื่อ LLM แก้ไขโค้ด จะต้องแสดงตัวอย่างที่รันได้จริงเสมอ
- แนวทางนี้ยังให้ผลพลอยได้ เช่น การลบโค้ดที่ไม่มีทางถูกเรียกใช้ การระบุ edge case อย่างชัดเจน และการเพิ่มประสิทธิภาพของ code review
เนมสเปซและโครงสร้างไฟล์
- เอเจนต์ สำรวจโค้ดเบสผ่านระบบไฟล์ ดังนั้นโครงสร้างไดเรกทอรีและชื่อไฟล์จึงทำหน้าที่เป็นอินเทอร์เฟซที่สำคัญ
- เส้นทางที่ชัดเจนอย่าง ./billing/invoices/compute.ts ส่งข้อมูลได้มากกว่า ./utils/helpers.ts อย่างมาก
- ควรใช้ ไฟล์ขนาดเล็กที่กำหนดขอบเขตไว้อย่างชัดเจน เพราะช่วยให้ LLM โหลดทั้งไฟล์เข้าเป็นบริบทได้ และ ป้องกันประสิทธิภาพตกลง
- การจัดโครงสร้างแบบนี้นำไปสู่ การเพิ่มความเร็วและความแม่นยำในการสำรวจของเอเจนต์
สภาพแวดล้อมการพัฒนาที่รวดเร็ว ชั่วคราว และรองรับการทำงานพร้อมกัน
- จากเดิมที่ใช้สภาพแวดล้อมพัฒนาแบบเดี่ยว การพัฒนาแบบอิงเอเจนต์กำลังเปลี่ยนไปเป็น รูปแบบที่จัดการหลายโปรเซสแบบขนาน
- Fast: ขั้นตอนการทดสอบและการตรวจสอบต้องรันได้อย่างรวดเร็ว โดยทีมได้ปรับแต่งให้ assertion มากกว่า 10,000 รายการ เสร็จภายใน 1 นาที
- เพิ่มความเร็วด้วยการทำงานขนานระดับสูง การแยกสภาพแวดล้อมอย่างเข้มงวด และ เลเยอร์แคชสำหรับการเรียก third-party
- Ephemeral: สร้างสภาพแวดล้อมใหม่ได้ภายใน 1~2 วินาที ด้วยคำสั่ง
new-feature <name> พร้อมตั้งค่าอัตโนมัติและเริ่มให้เอเจนต์ทำงาน
- หากยังต้องตั้งค่าด้วยมือ ความถี่ในการใช้งานจะลดลงอย่างมาก ดังนั้นการทำให้เป็นอัตโนมัติทั้งหมดจึงเป็นหัวใจสำคัญ
- Concurrent: เพื่อให้รันหลายสภาพแวดล้อมพร้อมกันได้ จำเป็นต้องมี การตั้งค่าเพื่อป้องกันการชนกันของพอร์ต DB แคช ฯลฯ
- อาจใช้ Docker หรือจัดการการแยกสภาพแวดล้อมด้วย environment variable
ระบบชนิดข้อมูลแบบ end-to-end และการควบคุมคุณภาพอัตโนมัติ
- ทำ แนวปฏิบัติที่ดีให้เป็นอัตโนมัติให้มากที่สุด เพื่อลดอิสระของ LLM และรักษาคุณภาพให้สม่ำเสมอ
- ตั้งค่า linter และ formatter อัตโนมัติ อย่างเข้มงวด เพื่อให้มีการแก้ไขอัตโนมัติทุกครั้งที่ LLM ทำงานเสร็จ
- แนะนำให้ใช้ ภาษาแบบ static type โดยเฉพาะการใช้ระบบชนิดข้อมูลที่แข็งแรงของ TypeScript
- ใช้ ชื่อชนิดข้อมูลที่มีความหมาย อย่าง
UserId, WorkspaceSlug, SignedWebhookPayload เพื่อสื่อเจตนาของโค้ดให้ชัดเจน
- ใช้ OpenAPI เพื่อรักษาความตรงกันของชนิดข้อมูลระหว่างฟรอนต์เอนด์และแบ็กเอนด์
- ใช้ระบบชนิดข้อมูลและ trigger ของ Postgres เพื่อคงความถูกต้องของข้อมูล และสร้างไคลเอนต์ที่ปลอดภัยด้านชนิดข้อมูลด้วย Kysely
- ไคลเอนต์ third-party ทั้งหมดก็ควรมี นิยามชนิดข้อมูลที่ถูกต้อง หรือถูกห่อใช้งานในรูปแบบที่เหมาะสม
บทสรุป: นิยามใหม่ของคุณภาพโค้ดในยุค AI
- เอเจนต์เป็น นักเขียนโค้ดที่ไม่เหนื่อยล้าและมีความสามารถสูง แต่ประสิทธิภาพของมันขึ้นอยู่กับคุณภาพของสภาพแวดล้อม
- ‘โค้ดที่ดี’ ไม่ใช่ทางเลือกอีกต่อไป แต่เป็น เงื่อนไขล่วงหน้าที่ทำให้ AI ทำงานได้อย่างถูกต้อง
- แม้การตั้งค่าเริ่มต้นอาจดูเป็นภาระ แต่นี่คือ การลงทุนที่จำเป็นซึ่งถูกเลื่อนมานาน
- ภายใต้การสนับสนุนจากผู้นำด้านการพัฒนา ควรมุ่งสู่ การสร้างโค้ดเบสที่เป็นมิตรกับ AI
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
กับดัก ของการทำให้ได้ coverage 100% คือ ถ้าโค้ดกับเทสต์ถูกเขียนโดยเอเจนต์ตัวเดียวกัน ก็อาจตกอยู่ในภาวะ ตรวจสอบตัวเองแบบขัดแย้งกันเอง
ถ้าเอเจนต์สร้างตรรกะที่ผิด แล้วก็เขียนเทสต์ที่ใช้ตรวจตรรกะผิดนั้นออกมาผิดเหมือนกัน เทสต์ก็จะผ่าน แต่โค้ดจริง ๆ ยังมีข้อผิดพลาดอยู่
coverage แบบนี้จะมีความหมายก็ต่อเมื่อมีการเขียนเทสต์ก่อนโค้ด หรือมีมนุษย์ตรวจอย่างเข้มงวดเท่านั้น
ไม่อย่างนั้นมันก็แค่สร้าง ความน่าเชื่อถือปลอม ๆ ขึ้นมา
แก่นสำคัญคือให้ คนหลายคนที่มีวิธีคิดต่างกัน มาตรวจจุดบอดของกันและกัน
ต่อให้มี AI หลายโมเดล สุดท้ายก็ควรมองว่าเป็น ‘จิตใจ’ เดียวกันอยู่ดี
ให้คนเขียนเทสต์ให้โค้ดของ AI, ให้ AI เขียนเทสต์ให้โค้ดของคน หรือให้รีวิวโค้ดซึ่งกันและกัน จะดีกว่า
แต่แม้ในหมู่มนุษย์เองก็ยังมีกรณีที่ความสัมพันธ์เชิงอำนาจทำให้สะท้อนออกมาแค่ความเห็นของคนเดียว และ AI ก็ไม่ได้ช่วยกันเรื่องนั้น
ต้องใส่บั๊กเข้าไปโดยตั้งใจแล้วดูว่ามันล้มเหลวหรือไม่
นี่ไม่ใช่ปัญหาเฉพาะของ AI มนุษย์ก็เหมือนกัน
ถึงอย่างนั้น การที่ AI ทำให้นักพัฒนาจำนวนมากได้เรียนรู้ หลักวิศวกรรมที่ถูกต้อง ก็ถือเป็นเรื่องดี
SQLite กับซอฟต์แวร์การบินก็ตั้งเป้าระดับนี้
อย่างไรก็ตาม นี่ยังไม่ใช่สมมติฐานที่ผ่านการยืนยันทางวิชาการ
เพราะงั้นจึงควรใช้ integration test หรือ UI automation test เพื่อตรวจสอบสถานการณ์การใช้งานจริงของผู้ใช้
การใช้ข้อมูลจากสภาพแวดล้อม production หรือการทดสอบใน shadow environment ก็ช่วยได้เช่นกัน
ก่อนยุค LLM การตั้งค่ามันยุ่งยาก แต่ตอนนี้ ROI ดีขึ้นแล้ว
อย่างที่ Uncle Bob เน้นไว้ การลงทุนกับการจัดโครงสร้างเทสต์เป็นเรื่องสำคัญ
LLM อาจเขียนเทสต์ที่ซ้ำ ๆ แต่ถ้าขอ มันก็ใช้ หลักการ DRY หรือแพตเทิร์น factory ได้ดีเหมือนกัน
นี่เป็นวิธีที่ผมเริ่มลองตั้งแต่เมื่อวาน คือเขียนสเปกด้วย TLA+/PlusCal ก่อน แล้วให้ Codex นำสเปกนั้นไป implement ตรง ๆ
ผมสั่งให้ยึดตามสเปกอย่างเดียวโดยไม่ต้องสร้างสรรค์
โค้ดที่ออกมา หน้าตาไม่สวยแต่ถูกต้อง, และเร็วกว่าการแปลด้วยตัวเองมาก
แต่ถ้าการ optimize ยังไม่ดีพอหรือรกเกินไป ผมก็จะแก้บางส่วนเอง
โดยเฉพาะตอนนี้กำลังลองกับ lock-free data structure แต่ Codex ยังชอบใส่ lock มาอยู่ เลยต้องคอยแก้เอง
สุดท้ายผมก็โฟกัสกับตรรกะเชิงคณิตศาสตร์ ส่วน AI รับหน้าที่รายละเอียดการ implement
นี่แหละคือเวิร์กโฟลว์ในอุดมคติแบบ “spec ก่อน โค้ดทีหลัง”
เห็นด้วยกับ บทความของ Martin Kleppmann
ในโมเดลรุ่นใหม่ ๆ มันใช้ได้ผลจริง และ ความคุ้มค่าด้านต้นทุน ก็ดีขึ้น 10~100 เท่า
นี่ดูเหมือน ภาพหลอน หรือไม่ก็ สำนวนขายของ
ถ้าบั๊กในระบบจริงกับภาระการบำรุงรักษายังบังคับให้เกิดโค้ดที่ดีไม่ได้ AI ก็ทำไม่ได้เหมือนกัน
AI ระดับปัจจุบันกลับมีแนวโน้มจะทำให้โค้ดแย่ลงด้วยซ้ำ
แค่เรื่องความยาวของเมธอดก็ยังตกลงกันไม่ได้เลย ดังนั้นจึงไม่มี เกณฑ์คุณภาพสากล
แม้แต่ตัวชี้วัดอย่าง test coverage ก็ปั่นแต่งได้ง่าย และถ้าใช้ผิดก็ยิ่งเป็นโทษ
โดยเฉพาะเมื่อ AI เป็นคนเขียนเทสต์ มันอาจสร้าง ความมั่นใจปลอม ๆ ได้
ผมคิดว่าการพัฒนาซอฟต์แวร์อาจเป็น การประยุกต์ใช้ LLM ที่ใช้งานได้จริงเพียงด้านเดียว
เพราะมันสร้าง feedback loop ได้เร็วกว่าแขนงอื่น
วางแผนกับ LLM แล้วอีกไม่กี่ชั่วโมงก็รู้ว่าล้มเหลว จากนั้น LLM ก็จะบอกว่า “เพราะงั้นถึงทำแบบนั้นไม่ได้”
มันคล้ายกับสร้างบ้านตามมาตรฐานไฟฟ้าแบบอเมริกัน แล้วค่อยไปพบปัญหาตอนติดตั้งเครื่องล้างจานมาตรฐานแคนาดา
ซอฟต์แวร์ค่อนข้างปลอดภัยกว่าเลยมีการพัฒนาแบบนิรนามได้ แต่ตอนนี้กำลังเกิด วัฒนธรรมการลงนามรับผิดชอบ ขึ้นมา
ต่อไปอาจมีแค่คนที่เขียน โค้ดความเสี่ยงสูงและมีนวัตกรรม เท่านั้นที่ได้ค่าตอบแทนสูง
AI ก็ยังคงปล่อย โค้ดเพี้ยน ๆ ออกมา เราก็ดีบัก แล้วมันก็ปล่อยของเพี้ยนใหม่ เป็น ลูปไม่รู้จบ เท่านั้นเอง
แค่ต้องให้ มาตรฐานกระแสไฟ ตรงกันก็ปลอดภัย
ถึงอย่างนั้นผมก็ยังคิดว่าอย่างน้อย unit test ก็ช่วยยืนยันจุดเชื่อมกับโลกจริงได้
ตอนนี้ผมกำลังใช้ LLM เพื่อเรียน วงจร RLC และ inerter
หลายคนตกใจกับการที่ LLM สร้างโค้ดได้เร็ว แต่ ความเร็วหรือปริมาณไม่ใช่คอขวดของคุณภาพ
การปฏิวัติที่แท้จริงจะเกิดขึ้นเมื่อ AI สร้าง โค้ดที่แม่นยำกว่ามนุษย์ ได้
คุณค่าที่แท้จริงมาจากการ รู้ว่าโค้ดทำงานอย่างไร
ในการประชุมที่วิศวกรคนอื่นกำลังเดากันอยู่ ช่วงเวลาที่มีคนหนึ่งเปิดโค้ดจริงให้ดูนั้นแหละมีค่าที่สุด
‘โค้ดที่ดี’ อาจเป็นแค่โค้ดที่ถูกปรับให้เหมาะกับ working memory อันจำกัดของมนุษย์ ก็ได้
แต่โมเดลสามารถมองเห็นบริบททั้งหมดพร้อมกัน จึงไม่มีข้อจำกัดแบบนั้น
ถ้า context window ใหญ่ขึ้น 100 เท่า การถกเถียงแบบนี้อาจสำคัญน้อยลง
ผมกังวลว่าถ้าบังคับให้ LLM ทำ coverage 100% มันจะทำให้ สมมติฐานที่ผิดถูกตรึงแน่น
แต่ถ้ายังมีการรีวิวโดยมนุษย์ ก็น่าจะพูดได้ว่า “อันนี้ผิด ลบเทสต์แล้วเขียนใหม่กันเถอะ” ใช่ไหม?
LLM ยังช่วยเขียน PRD แบบสัมภาษณ์ไปด้วย เพื่อให้ ขอบเขตและความคาดหวัง ชัดเจนขึ้น
“best practice” เปลี่ยนไปตามสภาพแวดล้อมทางเทคนิค
ตอนนี้การเขียนโค้ดง่ายขึ้นแล้ว ดังนั้น coverage 100% อาจมีประโยชน์กับ LLM มากกว่าเดิม
เทสต์ช่วยให้ LLM มี เป้าหมายที่ชัดเจน และทำให้การโต้ตอบหลังจากนั้นปลอดภัยขึ้น
แต่ละเทสต์อ้างอิง bug ticket ในอดีต และรับประกันว่าการแก้ไขยังคงอยู่
ถ้าให้สถานการณ์กับ LLM มันมักสร้างโค้ดที่มีคุณภาพใกล้เคียงกันเป็นส่วนใหญ่
ต่างจากงานศิลปะเชิงสร้างสรรค์ ซอฟต์แวร์คืออุตสาหกรรมที่เหมาะกับการทำอัตโนมัติเป็นพิเศษ
ตอนเห็นบทความ ผมนึกว่าจะพูดว่า “ถ้าอยากให้ AI มีประสิทธิภาพ เราต้อง เขียนโค้ดให้ดี”
ในความเป็นจริง Claude มักพลาดกับ ชื่อตัวแปรที่กำกวมหรือโค้ดที่ไม่เป็นตรรกะ
ถ้าชื่อตัวแปรเป็น “iteration_count” แต่จริง ๆ เก็บผลรวม AI ก็จะโดนหลอก
สุดท้ายแล้ว โค้ดที่สะอาด ก็มีประโยชน์ทั้งกับ AI และมนุษย์
เนื่องจาก AI ใช้เอกสารภายในเป็นทรัพยากรในการเรียนรู้ ตอนนี้ เอกสารที่อัปเดตแล้ว จึงถูกมองว่าเป็นสิ่งจำเป็น
เมื่อก่อนมันเป็นเรื่องลำดับความสำคัญต่ำ แต่ตอนนี้ มีผลต่อคุณภาพของโมเดลโดยตรง
อย่างไรก็ตาม เมื่อเวลาผ่านไป ส่วนนี้ก็น่าจะดีขึ้น
แบบนั้นโอกาสที่ LLM จะ implement ได้ถูกตั้งแต่ครั้งเดียวจะสูงขึ้น
บทความนี้เผยให้เห็น ความเข้าใจด้านวิศวกรรมแบบตื้น ๆ ของบริษัทสาย prompt
coverage 100% ไม่ได้ตรวจทุกชุดของอินพุต
มันแค่รันทุกบรรทัดด้วยตัวอย่างไม่กี่แบบเท่านั้น
สุดท้ายแล้วสิ่งที่ต้องการคือ formal proof แต่ต้นทุนมันสูงมหาศาลและ LLM ก็ไม่มีประโยชน์กับเรื่องนี้
ตรงกันข้าม การสร้าง สภาพแวดล้อมพัฒนาแบบตอบสนองรวดเร็ว ผ่านเทสต์อาจเปิดยุคทองใหม่ได้
ถ้ามีปัญหากับ coverage ก็ค่อยขยายทีหลังได้
ทางที่ดีคือตั้งระบบเทสต์ให้ ครอบคลุมที่สุดเท่าที่ทำได้ ตั้งแต่ต้น
ตอนนี้ก็มีความพยายามมากมายในการเชื่อมมันเข้ากับ proof assistant
ต่อให้ในสเปกจะมีข้อผิดพลาดเล็กน้อย ส่วนใหญ่ก็ยังให้ ผลลัพธ์ที่ใช้งานได้