31 คะแนน โดย xguru 2025-01-21 | 5 ความคิดเห็น | แชร์ทาง WhatsApp

การผสาน TDD(Test-Driven Development) กับ LLM

  • TDD เป็นแนวทางการพัฒนาที่เขียน unit test แบบครอบคลุมก่อนเริ่มเขียนโปรแกรม
  • เนื่องจากตัวทดสอบทำหน้าที่เสมือนสเปกโดยพฤตินัย เมื่อสุดท้ายทดสอบทั้งหมดผ่าน ก็สามารถพิสูจน์ความถูกต้องของโค้ดได้ในระดับหนึ่ง
  • โดยดั้งเดิมแล้ว TDD มักถูกวิจารณ์ว่าลดทอนประสิทธิภาพการทำงานหรือไม่มีประสิทธิผล
  • แต่เมื่อมี LLM เข้ามา กระบวนการเขียนเทสต์และแก้ไขโค้ดแบบวนซ้ำก็ทำได้ง่ายขึ้นมาก

วิธีที่ฉันใช้ LLM เป็นประจำ

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

จะทำให้เป็นอัตโนมัติได้ไหม?

  • เพื่อทำให้กระบวนการนี้เป็นอัตโนมัติ จึงนำแนวคิด event loop มาใช้เอง
  • หากระบุสเปกของฟังก์ชันที่จะพัฒนาและ function signature ไว้ในพรอมป์แรก โมเดลจะเสนอทั้ง unit test และร่างโค้ด
  • จากนั้นบันทึกโค้ดนี้ไว้ในไดเรกทอรี sandbox และรัน go test อัตโนมัติ
  • หากเทสต์ล้มเหลว ก็จะส่งโค้ดเดิมพร้อมผลการทดสอบ เช่น compile error หรือข้อมูลความล้มเหลว ไปกับพรอมป์ที่สอง (สำหรับการวนซ้ำ)
  • โมเดลจะใช้ข้อมูลเหล่านี้เพื่อเสนอเทสต์และโค้ด implementation ที่แก้ไขแล้วอีกครั้ง
  • ทำซ้ำกระบวนการนี้ไปเรื่อย ๆ จนกว่าเทสต์ทั้งหมดจะผ่าน
  • แนวทางนี้ช่วยให้ปรับปรุงแบบค่อยเป็นค่อยไปได้ โดยไม่ต้องสะสมบริบทมากเกินไป
  • โมเดลอาจล้มเหลวซ้ำ ๆ กับ test case เดิมได้ และในกรณีนั้นมนุษย์ควรช่วยชี้จุดปัญหาและให้คำใบ้
  • ต้องตระหนักถึงปัญหา "การไม่มีผู้เฝ้าดู" ซึ่งทำให้ต้องตั้งคำถามว่าเทสต์ที่ LLM สร้างขึ้นเข้มงวดพอหรือไม่
  • มีความเป็นไปได้ที่โค้ดและเทสต์จะมีข้อผิดพลาดเดียวกันหรือแชร์การออกแบบที่ไม่สมบูรณ์ร่วมกัน
  • ดังนั้นขั้นตอนที่มนุษย์ช่วยเสริม test case เพิ่มเติมจึงมีความสำคัญ
  • หากจำเป็น ก็อาจทดลองใช้เทคนิคอย่าง mutation testing กับ AI ได้เช่นกัน

การพัฒนาด้วย LLM และภาระทางการรับรู้ (cognitive load)

  • หากใช้ TDD ร่วมกับ LLM ก็คาดว่าน่าจะทำได้ไม่เฉพาะกับโจทย์อัลกอริทึมทั่วไป แต่รวมถึง codebase จริงที่มี dependency ด้วย
  • อย่างไรก็ตาม ควรแยกโครงสร้างโปรเจ็กต์ออกเป็นหน่วยย่อยมากขึ้นเพื่อเพิ่มความสามารถในการบำรุงรักษา และให้แต่ละไดเรกทอรี/แพ็กเกจสามารถทดสอบได้อย่างอิสระ
  • แนะนำให้แต่ละแพ็กเกจแยกเป็นไฟล์สำหรับนิยาม type หลัก (shared.go), ไฟล์ที่รับผิดชอบลอจิกเฉพาะ (x.go) และไฟล์เทสต์ (x_test.go) เพื่อลดภาระทางการรับรู้
  • ในกระบวนการใช้ AI แทนที่จะส่งโค้ดทั้งหมดให้โมเดลทุกครั้ง ควรคัดเลือกเฉพาะบางส่วนเพื่อช่วยให้โมเดลโฟกัสได้ดีขึ้น
  • วิธีนี้ช่วยเพิ่ม test coverage พร้อมกับลดระดับการยึดโยงกันระหว่างโมดูล และยังเป็นประโยชน์ต่อการบำรุงรักษาระยะยาว
  • แม้เป็นโปรเจ็กต์ใหญ่ ก็ควรออกแบบให้แตกเป็นหน่วยเล็กและชัดเจน โดยให้แต่ละหน่วยมีตรรกะที่สมบูรณ์พอ แต่จำกัดขอบเขตให้น้อยที่สุด

สรุป

  • เมื่อพิจารณาจากความเร็วในการพัฒนาของ AI ก็เป็นไปได้ว่าพรุ่งนี้อาจมีสถาปัตยกรรมใหม่เกิดขึ้นและก้าวข้ามข้อจำกัดของ LLM ไปได้เลย
  • ดังนั้นแทนที่จะรีแฟกเตอร์ legacy code ขนาดใหญ่ที่มีมากกว่า 100,000 บรรทัดอย่างฉับพลัน จึงแนะนำให้เริ่มสำรวจความเป็นไปได้ของการผสาน TDD กับ LLM จากงานขนาดเล็กก่อน
  • คาดว่าการผสาน TDD กับ LLM จะนำมาซึ่งการเปลี่ยนแปลงเชิงบวกทั้งในด้านการสร้างโค้ดอัตโนมัติและการควบคุมคุณภาพของเทสต์

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

 
jhj0517 2025-01-22

ทำให้อดคิดไม่ได้ว่าบริการ AI สำหรับนักพัฒนารายอื่น ๆ ใช้ pipeline แบบไหนกันอยู่

 
halfenif 2025-01-21

(พอเห็นอะไรแบบนี้แล้ว) อีกไม่นานก็คงรู้สึกเหมือนว่าจะถึงขั้นฝังสายกระตุ้นไฟฟ้าเข้าไปในสมองเพื่อเชื่อมกับสมองกลกันแล้วล่ะ

 
crawler 2025-01-21

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

 
rabolution 2025-01-21

ไมโครเอเจนต์ที่ Builder.io สร้างขึ้นก็เป็นแนวทางที่คล้ายกันครับ https://github.com/BuilderIO/micro-agent ผมเองก็เคยลองทำ TDD กับ LLM มาหลายครั้งเหมือนกัน แต่ต้องทำ abstraction ให้ดีด้วย เช่นพวก design system และต้องวาง convention กับ pattern ให้ชัดเจนด้วย ปกติแล้ว test case ผมมักจะเขียนเองครับ (อย่างน้อยก็เป็นภาษามนุษย์?) เหนือสิ่งอื่นใด อย่างที่บทความนี้พูดไว้ การออกแบบโมดูลที่มี coupling ต่ำและ cohesion สูงให้ดี เป็นสิ่งที่ช่วยให้ยัดบริบทเข้าไปใน context window ที่มีจำกัดได้จริงๆ

 
jamsya 2025-01-21

แม้ว่า LLM จะช่วยดูโค้ดในขอบเขตเล็ก ๆ ได้ดี แต่ในด้านการออกแบบโดยรวมและภาพใหญ่ยังค่อนข้างน่าเสียดายอยู่บ้าง
ดังนั้นวิธีค่อย ๆ ปรับปรุงไปพร้อมกับ TDD ก็ดูเป็นแนวทางที่ดีนะครับ