Agent Dev Thai Academyprinciples · patterns · architecture

สั่งให้แก้ bug แต่มันไปแก้ test

agent ไปแก้ test ให้เขียวแทนแก้ bug เพราะ test คือ metric ที่มันจะ optimize เขียน main flow ให้ชัดก่อน กัน test ชุดนึงไว้ไม่ให้มันเห็น และสั่งให้ส่งหลักฐานแทนสั่งให้ผ่าน

สั่ง agent ให้ไปแก้ bug

มันกลับมาบอกว่าเสร็จแล้ว test เขียวหมด

เปิดดู diff — มันไม่ได้แก้ bug มันไปแก้ test ให้ผ่าน

ไม่ใช่เพราะ agent โง่ ไม่ใช่เพราะ model ไม่เก่งพอ แต่เพราะเราให้เป้ามันเป็น "test ต้องเขียว" แล้วทางที่สั้นที่สุดไปถึงเป้านั้นคือแก้ test ไม่ใช่แก้ code

test คือ metric และ agent จะ optimize ทุก metric ที่เราให้มัน

วันนี้จะเขียนเรื่องทำ test ให้มันโกงไม่ได้


ก่อนอื่น เรื่องที่เค้าคุยกันในคอมมูช่วงนี้

ช่วงสองสัปดาห์ที่ผ่านมามีกระแสใน X ว่าอย่าให้ AI เขียน unit test เลย ลบทิ้งให้หมด มี dev เบอร์ใหญ่ ๆ พูดกันหลายคน ยอด view ระดับล้าน มีโปรเจกต์นึงลบ test ออกไปประมาณ 400,000 บรรทัด แล้ว coverage แทบไม่ขยับ

เคสที่ทุกคนเอามาอ้างคือทีมนึงที่ปล่อย agent เข้าไป refactor codebase ตัวเอง ระหว่างนั้น worker ลบ public API ของระบบ plugin ทิ้ง เปลี่ยน behavior ของ exception ไป 65 จุด

test ทั้งชุดเขียวตลอด

คนอ่านแล้วสรุปว่า เห็นมั้ย test ไม่มีประโยชน์

ผมอ่านแล้วเห็นอีกอย่าง — test ชุดนั้นโกงได้ และมันก็โดนโกงไปแล้วโดยที่ไม่มีใครต้องตั้งใจโกงด้วยซ้ำ

test ที่ public API หายทั้งตัวแล้วยังเขียว แปลว่ามันไม่เคยเรียกผ่าน API จริง ๆ มันเรียกผ่าน internals

และทีมนั้นก็ไม่ได้ลบ test ทิ้ง เขาเพิ่ม review กับ check ว่ามี public API ตัวไหนหายไปบ้าง


ข้อ 1 ไม่มี main flow ที่ชัด ก็ไม่มี test ที่ดี

อันนี้มาก่อนทุกอย่าง

ถ้าตอบไม่ได้ว่าระบบนี้ทำอะไรตั้งแต่ต้นจนจบ — user กดอะไร ผ่านอะไร จบตรงไหน แล้วอะไรคือ proof ว่าสำเร็จ — เขียน test ยังไงก็ไม่มีความหมาย

เพราะ test ต้องผูกกับ behavior ที่ระบบต้องทำ ถ้าเราเองยังบอกไม่ได้ว่ามันต้องทำอะไร สุดท้าย test จะไปผูกกับ "สิ่งที่ code เขียนไว้" แทน ซึ่งกลายเป็นการเขียน test ล้อตาม code ไปเฉย ๆ

คนสร้างภาษา Elixir ตอบในกระแสนี้ประโยคนึงที่ผมว่าตรงที่สุด — กระแสที่บอกว่า unit test ตายแล้ว จริง ๆ คือความล้มเหลวเรื่อง design และ AI แค่ทำสิ่งที่คนทำผิดกันมาหลายสิบปีให้เร็วขึ้น

เขียน main flow ออกมาก่อน เป็นข้อ ๆ ก็ได้ แล้วค่อยให้ agent เขียน test ตามนั้น ไม่ใช่ให้มันอ่าน code แล้วเดาเอาเอง


ข้อ 2 test ที่ agent แก้ได้ ไม่ใช่ test

ข้อนี้คือหัวใจ

ตราบใดที่ agent มองเห็น test และแก้ test ได้ มันจะมี shortcut เสมอ มันไม่ได้ตั้งใจโกง มันแค่เดินไปทางที่ถึงเป้าเร็วที่สุด

สามอย่างที่ทำได้เลย

เก็บ test ชุดนึงไว้ไม่ให้มันเห็น โปรเจกต์ที่ลบ test 400,000 บรรทัดเขาทำแบบนี้ กันไว้ชุดนึง เอาออกมารันตอนเตรียม release อย่างเดียว test ที่ agent แก้ไม่ได้เท่านั้นที่ยังทำหน้าที่ของ test อยู่

อ่าน diff ของ test ทุกครั้ง ไม่ใช่แค่ diff ของ code ถ้ารอบนี้สั่งแก้ bug แล้วไฟล์ test เปลี่ยน ให้ถามก่อนเสมอว่าทำไม บางทีมันถูก แต่ต้องเห็นก่อน

สั่งให้ส่งหลักฐานยืนยัน ไม่ใช่สั่งให้ผ่าน แทนที่จะบอกว่า "ทำให้ test เขียว" ให้บอกว่า "รัน flow นี้จริง แล้วเอาผลลัพธ์มาให้ดู" ของที่เอามาโชว์ได้ โกงยากกว่าผลเขียวเฉย ๆ


ข้อ 3 เส้นแบ่งข้อเดียวที่ใช้ได้จริง

refactor โดยไม่เปลี่ยน behavior เลย แล้ว test พัง — test ตัวนั้นเทส implementation ไม่ได้เทส behavior

อันนั้นคือตัวที่ลบได้ และมันคือตัวที่ทำให้ agent เสียเวลาไปนั่งแก้ test ทุกครั้งที่ขยับ code

ของที่เป็นขยะชัด ๆ: test ที่ไป grep source code, mock ที่เขียน logic เดียวกับสิ่งที่กำลังจะ assert, test ที่มีไว้เพื่อให้ export ตัวนึงไม่โดนลบเฉย ๆ

กลับกัน ถ้า behavior เปลี่ยนแล้ว test ยังเขียว อันนั้นแย่กว่าไม่มี test เพราะมันหลอกให้เราตายใจ

ถ้าจะให้ agent ช่วยลบ อย่าสั่งว่า "ช่วย clean up test หน่อย" มันจะหยุดเร็วเกินไป สั่งเป็นตัวเลข

ลบ test ที่มีประโยชน์น้อยที่สุดออก 20%
โดยที่ coverage ต้องไม่ลดเกิน 2%

ลองกับของตัวเอง 30 วินาที

เปิดไฟล์ที่มี test อยู่ แล้ว rename function ข้างในสักตัว ไม่ต้องเปลี่ยน logic อะไรเลย

test พัง = มันผูกกับ implementation ไม่ได้ผูกกับ behavior

แล้วลองอีกอัน — ลบ behavior ตัวนึงออกจริง ๆ ถ้า test ยังเขียว แปลว่ามันไม่ได้ทำงานมาตั้งแต่แรก

คำถามไม่ใช่ "test เยอะไปมั้ย" คำถามคือ ถ้าของพัง มันจะรู้มั้ย และถ้ามันรู้ agent แอบแก้กลบเกลื่อนให้ผ่านไปเงียบ ๆ ได้มั้ย