เราจะผลักดัน AI ให้ทำงานอัตโนมัติในการ generate code ได้ไกลแค่ไหน?
บทความนี้เป็น บทแปลภาษาไทยอย่างไม่เป็นทางการ ของ “How far can we push AI autonomy in code generation?” โดย Birgitta Böckeler (Distinguished Engineer, Thoughtworks) เผยแพร่ครั้งแรกบน martinfowler.com เมื่อ 5 สิงหาคม 2025
แปลและเรียบเรียงเพื่อการศึกษา — ไดอะแกรมเชิงแนวคิดวาดใหม่เป็นภาษาไทย (label ในไดอะแกรมคงเป็นภาษาอังกฤษ) ส่วนศัพท์เทคนิคคงไว้เป็นภาษาอังกฤษตามต้นฉบับและมีคำอธิบายไทยกำกับเมื่อพบครั้งแรก ลิขสิทธิ์ของเนื้อหาต้นฉบับเป็นของผู้เขียน
link ไปยังหน้า DevIQ ที่แทรกในเนื้อหา (เช่น YAGNI, Feature Creep, Goodhart’s Law) เป็นการเชื่อมโยงแนวคิดเพิ่มเติมโดยทีมแปลเพื่อการศึกษา ไม่ใช่การอ้างอิงในต้นฉบับ
คำถามที่ผู้เขียนอยากหาคำตอบคือ: ตอนนี้ LLM สามารถ generate, ทดสอบ และ deploy application ซอฟต์แวร์ที่สมบูรณ์ ตั้งแต่ต้นจนจบ จากคำอธิบายความต้องการที่เป็นภาษาคน (natural language) โดยไม่มีมนุษย์เข้าไปแทรกแซงเลย ได้ไกลแค่ไหน?
เพื่อสำรวจว่าเทคโนโลยีในวันนี้ทำได้แค่ไหน — และต้องมีความก้าวหน้าอะไรอีกจึงจะไปถึงจุดที่ทำงานได้อัตโนมัติเต็มตัว (full autonomy) — ผู้เขียนจึงลงมือทำการทดลองชุดหนึ่งขึ้นมา
ขอบเขตของงานนี้จงใจให้แคบและควบคุมได้ ผู้เขียนเลือกสร้าง app Spring Boot ที่เป็น REST API แบบ CRUD (Create, Read, Update, Delete — สร้าง/อ่าน/แก้ไข/ลบ) เรียบ ๆ ตัวหนึ่ง ไม่ใช่ระบบที่ซับซ้อน
และข้อสรุปทั้งหมดในบทความนี้มุ่งเฉพาะกับผลิตภัณฑ์ดิจิทัลและซอฟต์แวร์ application เชิงธุรกิจ (business application software) แบบที่ใช้กันในงานค้าปลีก ระบบประกาศรายการสินค้า และ domain ทำนองนั้น — ไม่ใช่domain เฉพาะทางที่ความเสี่ยงสูงมากอย่างการบินหรือการเงิน
ในการทดลอง ผู้เขียนใช้ model ตระกูล Claude Sonnet เป็นหลัก (รุ่น 3.7 และ 4) เพราะในช่วงนั้นเป็น model ที่มีความสามารถด้านการเขียน code สูงที่สุดเท่าที่มีให้ใช้ และช่วงใกล้เผยแพร่ก็มีการทดสอบซ้ำบางส่วนด้วย Claude Code และ Cursor เพิ่มเติม
กลยุทธ์ที่นำมาใช้ (The strategies)
หัวข้อที่มีชื่อว่า “กลยุทธ์ที่นำมาใช้ (The strategies)”เพื่อรีดประสิทธิภาพให้ workflow เข้าใกล้การทำงานอัตโนมัติมากที่สุด ผู้เขียนได้ประกอบกลยุทธ์ต่าง ๆ เข้าด้วยกันแปดข้อ ดังนี้
1. การเลือก tech stack (Choice of the tech stack)
หัวข้อที่มีชื่อว่า “1. การเลือก tech stack (Choice of the tech stack)”ผู้เขียนเลือก Spring Boot สำหรับ backend ที่เป็น REST API แบบ CRUD ด้วยเหตุผลว่า
- มีตัวอย่างเยอะในข้อมูลฝึกของ model (training data) โอกาสที่ LLM จะเคยเห็น code ทำนองนี้จึงสูง
- framework จัดการความซับซ้อนให้เยอะ ทำให้ LLM ต้อง generate code เองน้อยลง
- มี pattern สถาปัตยกรรมที่ตายตัว คือลำดับชั้น Controller → Service → Repository → Entity
- มีชั้นการทดสอบที่ชัดเจน ทั้ง unit test, integration test และ end-to-end (E2E) test
flowchart TB
subgraph App["Spring Boot Application"]
direction TB
C["Controller Layer"]
S["Service Layer"]
R["Repository Layer"]
E["Entity Layer"]
C --> S
S --> R
R --> E
end
CT["Controller Tests"] -.-> C
ST["Service Tests"] -.-> S
RT["Repository Tests"] -.-> R
E2E["E2E Test Suite"] -.-> App
ภาพ 1 — สถาปัตยกรรมของ app เป้าหมาย ประกอบด้วยชั้น persistence, service และ controller แบบ Spring Boot ทั่วไป โดยแต่ละชั้นควรมีชุด test ของตัวเอง และมีชุด E2E test แยกต่างหากคลุมทั้งระบบ · ดูภาพต้นฉบับ ↗
2. หลาย agent (Multiple agents)
หัวข้อที่มีชื่อว่า “2. หลาย agent (Multiple agents)”แทนที่จะให้ LLM session เดียวทำทุกอย่าง ผู้เขียนแบ่งกระบวนการ generate ออกเป็นบทบาทของ agent หลายตัวที่ทำงานตามลำดับ โดยแต่ละบทบาทอยู่ใน session LLM ของตัวเอง ได้แก่
Requirements analyst (นักวิเคราะห์ความต้องการ) → Bootstrapper (ตัวตั้งโครง project) → Backend designer (ผู้ออกแบบ backend) → ตัว generate ชั้น persistence → ตัว generate ชั้น service → ตัว generate ชั้น controller → E2E tester (ตัวทดสอบแบบ end-to-end) → Code reviewer (ผู้รีวิว code)
flowchart TB A1["Requirements Analyst"] --> A2["Bootstrapper"] A2 --> A3["Backend Designer"] A3 --> A4["Persistence Generator"] A4 --> A5["Service Generator"] A5 --> A6["Controller Generator"] A6 --> A7["E2E Tester"] A7 --> A8["Code Reviewer"]
ภาพ 2 — agent หลายตัวในกระบวนการ generate โดยแต่ละตัวรับผิดชอบบทบาทเฉพาะและส่งงานต่อกันตามลำดับ · ดูภาพต้นฉบับ ↗
เหตุผลที่ทำเช่นนี้: session LLM ที่ยาวเกินไปจะมีคุณภาพลดลงเรื่อย ๆ (context ยาวขึ้นก็ยิ่งหลุด) การมอบหมายบทบาทเฉพาะให้แต่ละ agent จึงช่วยให้ใช้เทคนิคการเขียน prompt ที่รู้กันดีอยู่แล้วได้เต็มที่
อนึ่ง ตอนทดสอบซ้ำด้วยเครื่องมืออื่น ผลออกมาปนกัน: Claude Code ทำได้ดี ส่วน Cursor ไม่ generate test ของชั้น service และ controller เลย และแทบจะละทิ้งงาน E2E ไปเสียเฉย ๆ
ควรแยกให้ชัดระหว่าง “โหมดวางแผน” สองแบบ แบบหนึ่งคือ TODO list ที่ model ไล่ทำเป็นรายการภายในsession เดียว (context window เดียว) กับอีกแบบที่ผู้เขียนใช้ คือการแตกงานออกเป็น subtask ที่แต่ละงานมี context window แยกกันของตัวเอง
ผู้เขียนใช้ Kilo Code (ซึ่งเป็น fork ของ Roo Code) เป็น harness สำหรับ orchestration และตั้งประตูอนุมัติของมนุษย์ (human approval gate) ไว้ก่อนที่ agent จะรันคำสั่ง terminal ใด ๆ
flowchart TB O["Orchestrator Session"] O --> S1["Subtask Session - Persistence"] O --> S2["Subtask Session - Service"] O --> S3["Subtask Session - Controller"] S1 --> W1["Own Context Window"] S2 --> W2["Own Context Window"] S3 --> W3["Own Context Window"] O --> G["Human Approval Gate for Terminal Commands"]
ภาพ 3 — การตั้งค่าแบบ subtask ใน Kilo: session orchestrator หนึ่งตัวมอบหมายงานต่อให้ session subtask หลายตัว โดยแต่ละ subtask มี context window ของตัวเอง · ดูภาพต้นฉบับ ↗
3. เจาะจง stack มากกว่าใช้คำสั่งกว้าง ๆ (Stack-specific over general purpose)
หัวข้อที่มีชื่อว่า “3. เจาะจง stack มากกว่าใช้คำสั่งกว้าง ๆ (Stack-specific over general purpose)”แทนที่จะสั่งกว้าง ๆ ว่า “เขียน app Spring Boot ให้หน่อย” ผู้เขียนให้คำแนะนำที่เจาะจงกับ Spring โดยละเอียด เพราะ prompt กว้าง ๆ ไม่ค่อยได้ code คุณภาพระดับ production ที่เข้ากับมาตรฐานขององค์กร ส่วนคำสั่งที่เจาะจงจะช่วยให้ได้ code ที่สม่ำเสมอ (consistent) กันทั้ง codebase ระดับองค์กร
งานนี้เชื่อมโยงกับการตกลง coding standard ร่วมกันทั้ง codebase ซึ่งเป็นขั้นแรกของ Collective Code Ownership — มาตรฐานที่ชัดคือสิ่งที่ทำให้ผลลัพธ์ทำซ้ำได้
4. ใช้ script แบบกำหนดผลได้ (Use of deterministic scripts)
หัวข้อที่มีชื่อว่า “4. ใช้ script แบบกำหนดผลได้ (Use of deterministic scripts)”ขั้นตอนตั้งโครง project (bootstrapping) ผู้เขียนใช้ CLI อย่างเป็นทางการของ Spring Boot ทำ แทนที่จะให้ AI generate เอง
หลักการคือต้องรู้จักจังหวะที่เครื่องมือ/script ที่มีอยู่แล้วแบบ deterministic ทำได้ดีกว่าปล่อยให้ AI generate เอง แล้วผสมส่วนที่กำหนดผลได้แน่นอน (deterministic) เข้ากับส่วนที่ให้ AI สร้าง อย่างมีกลยุทธ์
5. ใส่ตัวอย่าง code ใน prompt (Code examples in prompts)
หัวข้อที่มีชื่อว่า “5. ใส่ตัวอย่าง code ใน prompt (Code examples in prompts)”ถ้าไม่ให้ตัวอย่าง LLM จะเผลอใช้ library ที่ตกยุคไปแล้ว เช่นใช้ javax.persistence แทนที่จะเป็น jakarta.persistence ที่ถูกต้องในปัจจุบัน
กลยุทธ์นี้ผู้เขียนระบุว่าเป็น “กลยุทธ์ที่ได้ผลที่สุดในการทำให้ AI generate code แบบที่เราต้องการ” เพราะตัวอย่าง code
- ช่วยสื่อสาร coding idioms (สำนวนการเขียน code) ขององค์กร
- ช่วยล็อกให้ใช้ library/version ตรงกัน
- ทำให้ AI รับ framework หรือ version ใหม่ได้ทันทีโดยไม่ต้องรอให้มันไปโผล่เยอะ ๆ ใน training data ก่อน
6. ใช้ reference application เป็นหมุดยึด (Reference application as an anchor)
หัวข้อที่มีชื่อว่า “6. ใช้ reference application เป็นหมุดยึด (Reference application as an anchor)”แต่การไปนั่งดูแลตัวอย่าง code ที่ฝังอยู่ใน prompt นั้นน่าเบื่อและผิดพลาดง่าย
ผู้เขียนจึงสร้าง reference application (app อ้างอิง) ขึ้นมาหนึ่งตัว แล้วให้ MCP server (Model Context Protocol — protocol มาตรฐานสำหรับป้อน context/เครื่องมือให้ LLM) คอยส่งตัวอย่าง code จาก app อ้างอิงนั้นให้ agent ข้อดีคือ
- ตัวอย่าง codecompile ผ่านแน่นอน
- ตัวอย่างทุกชิ้นสอดคล้องกัน
- ประสบการณ์ของนักพัฒนาระหว่างการปรับจูน workflow ก็ดีขึ้น
flowchart LR Ref["Reference Application"] --> MCP["MCP Server"] MCP --> Agent["Coding Agent"] Agent --> Target["Target Codebase"]
ภาพ 4 — reference application ทำหน้าที่เป็นหมุดยึด ป้อนตัวอย่าง code ผ่าน MCP server เข้าสู่ agent แล้ว agent จึงนำไปใช้กับ codebase เป้าหมาย · ดูภาพต้นฉบับ ↗
7. วงรอบ generate แล้ว review (Generate-review loops)
หัวข้อที่มีชื่อว่า “7. วงรอบ generate แล้ว review (Generate-review loops)”ผู้เขียนตั้ง agent รีวิวเฉพาะขึ้นมาตัวหนึ่ง ให้ทำหน้าที่เทียบ code ที่ generate ออกมากับความต้องการ/คำสั่งเดิม
สิ่งที่พบคือ: LLM มักพลาดคำสั่งบางข้อในการ generate รอบแรก แต่เมื่อถูกขอให้กลับมารีวิวงานของตัวเอง กลับประเมินความตรงตาม spec ได้ดี agent รีวิวจึงจับข้อผิดพลาดที่ตัวเองแก้ได้เองไปได้จำนวนมาก
แนวคิดนี้สอดคล้องกับทักษะ iterative review (การตรวจทานแบบวนซ้ำ) ที่ Structured-Prompt-Driven Development ยกเป็นหนึ่งในสามทักษะหลัก — แยกบทบาท “ผู้สร้าง” ออกจาก “ผู้ตรวจ”
8. แบ่ง codebase เป็น module (Codebase modularization)
หัวข้อที่มีชื่อว่า “8. แบ่ง codebase เป็น module (Codebase modularization)”ผู้เขียนสั่งให้ AI ระบุ aggregate ของ domain (กลุ่มของ domain object ที่ถูกปฏิบัติเป็นหนึ่งหน่วยและต้องคงความสอดคล้องภายในหลังจบ business operation — ดู Aggregate) แล้วจัด package ตามนั้น
โดยใน prompt นิยาม aggregate ไว้อย่างชัดเจน และขอให้ AI ตอบว่า entity ตัวไหนเป็น root, มี entity อะไรอยู่ข้างในบ้าง พร้อมเหตุผลของการกำหนดขนาด (ดูจากขนาดของ transaction และรูปแบบการทำงานพร้อมกัน — concurrency)
flowchart TB Root["com.example.crm"] Root --> Cust["customer"] Root --> Ord["order"] Root --> Prod["product"] Cust --> CustRoot["Customer Aggregate Root"] Cust --> CustEntity["Contact Entity"] Ord --> OrdRoot["Order Aggregate Root"] Ord --> OrdLine["OrderLine Entity"] Prod --> ProdRoot["Product Aggregate Root"]
ภาพ 5 — ตัวอย่างโครงสร้าง package ที่แบ่งเป็น module ตาม aggregate ของ domain · ดูภาพต้นฉบับ ↗
การแบ่ง module ช่วยเพิ่มอัตราความสำเร็จเพราะ
- มันจำกัดขอบเขตของการเปลี่ยนแปลงแต่ละครั้ง ลดภาระ context ของ LLM
- ทำให้ประยุกต์ workflow ทีละ module ได้
- ทำให้ “แช่แข็ง” (freeze) module อื่นไว้เพื่อลดการเปลี่ยนแปลงที่ไม่ตั้งใจได้
- ดูแลรักษาง่ายขึ้นทั้งสำหรับคนและ AI
แต่ก็มีข้อควรระวังว่า การให้ AI ทำเรื่องนี้ได้ดีโดยไม่มีคนคุมนั้นยาก — เพราะแม้แต่มนุษย์เองก็ยังหา aggregate ที่เหมาะสมได้ลำบาก
ผลลัพธ์ (Results)
หัวข้อที่มีชื่อว่า “ผลลัพธ์ (Results)”รอบที่ 1: 3-5 entity
หัวข้อที่มีชื่อว่า “รอบที่ 1: 3-5 entity”โจทย์เป็น domain เรียบ ๆ เช่น “catalog สินค้าอย่างง่าย” หรือ “ระบบติดตามหนังสือในห้องสมุด” มี 3-5 entity เป็น CRUD API ตรงไปตรงมาพร้อมตรรกะ validation พื้นฐาน ผู้เขียนวนปรับ prompt และการตั้งค่าไปราว 15 รอบ
- ระยะเวลา: 25-30 นาทีต่อรอบ
- ค่าใช้จ่าย: ประมาณ 2-3 ดอลลาร์ต่อครั้ง (ขยับเป็น 4-5 ดอลลาร์เมื่อเปิดโหมด “thinking”)
ผลคือ workflow สามารถ generate app ที่ทำงานได้และตรงตาม spec โดยแทบไม่ต้องมีคนแทรก ได้ซ้ำ ๆ ระหว่างทางมักเจอ error อยู่เสมอ แต่ก็มักแก้ตัวเองได้
รอบที่ 2: schema ที่มีอยู่แล้ว 10 entity
หัวข้อที่มีชื่อว่า “รอบที่ 2: schema ที่มีอยู่แล้ว 10 entity”ผู้เขียนเพิ่มความซับซ้อนด้วยการชี้ workflow ไปที่ schema ของระบบ CRM (Customer Relationship Management — การบริหารความสัมพันธ์ลูกค้า) แบบตัดทอน ราว 10 entity และเปลี่ยนจากฐานข้อมูล H2 แบบ in-memory มาเป็น PostgreSQL งานยังเป็น CRUD API พร้อม validation และ business rule แต่ไม่มีตรรกะซับซ้อน
- ทั้ง workflow: 4-5 ชั่วโมง โดยมีคนเข้าไปแทรกหลายจุด
- งานขยาย field ต่อจากนั้น (จาก 15 เป็น 50 field): เพิ่มอีกราว 1 ชั่วโมง
ศักยภาพในการทำงานขนานมีอยู่จำกัด เพราะวงรอบ feedback ของ agent (compile error, ผล test) ต้องรันแบบเรียงลำดับ ถ้ารีบทำขนานเร็วเกินไป agent จะไปแก้ปัญหาก่อนเวลาอันควร หรือไม่ก็ต้องเผชิญปัญหาที่ merge ปนกันมาพร้อม ๆ กัน
และใน workflow agentic ของงานจริง (เช่น business process automation) ก็มีงานวิจัยชี้ว่า LLM มักหาทางเลี่ยงจุดตรวจแบบกำหนดผลได้ (deterministic checkpoint) อยู่ดี
เกมตีตัวตุ่น (A game of whac-a-mole)
หัวข้อที่มีชื่อว่า “เกมตีตัวตุ่น (A game of whac-a-mole)”ข้อสังเกตในภาพรวม: ถึงจะปรับกลยุทธ์ต่าง ๆ แล้ว แต่ workflow แต่ละรอบก็ยังโผล่ปัญหาคนละอย่างกันมาเรื่อย ๆ เหมือนเล่นเกมตีตัวตุ่น — พอ patch prompt/workflow เพื่ออุดปัญหาหนึ่ง ปัญหาใหม่ก็โผล่ขึ้นมาที่อื่น กลายเป็นวงจรที่ประคองต่อไปไม่ไหว
จากตรงนี้ ผู้เขียนสรุป pattern ปัญหาที่พบซ้ำ ๆ ได้หลายรูปแบบ ดังต่อไปนี้
Overeagerness — กระตือรือร้นเกินเหตุ (scope creep)
หัวข้อที่มีชื่อว่า “Overeagerness — กระตือรือร้นเกินเหตุ (scope creep)”AI ชอบ generate feature และ business logic ที่ไม่มีใครสั่ง ตัวอย่างที่ชัดคือ เมื่อเจอศัพท์ domain คำว่า “pro-rated revenue” (รายได้ที่คิดตามสัดส่วนเวลา) AI ก็ไปคำนวณและเพิ่มตรรกะส่วนนั้นให้เองทั้งที่ไม่มี requirement ระบุไว้
ผู้เขียนพยายามลดปัญหาด้วยการย้ำใน prompt ซ้ำ ๆ ว่าเอาเฉพาะ feature ที่ระบุไว้เท่านั้น และใช้ agent รีวิวคอยจับ code ส่วนเกิน แต่ก็ได้ผลจำกัด — ยังเกิดขึ้น “แทบทุกรอบ” การลองลดค่า temperature ลงหนึ่งครั้งก็ยังบอกไม่ได้ชัดว่าช่วยหรือไม่ (ข้อมูลน้อยเกินไป)
ปัญหานี้ตรงกับ anti-pattern อย่าง Feature Creep และ Speculative Generality — และเป็นเหตุผลว่าทำไมหลักการ YAGNI จึงสำคัญ
ช่องว่างใน requirement จะถูกเติมด้วยข้อสันนิษฐาน
หัวข้อที่มีชื่อว่า “ช่องว่างใน requirement จะถูกเติมด้วยข้อสันนิษฐาน”เมื่อ requirement ไม่ครบ AI จะเติมช่องว่างด้วยข้อสันนิษฐานที่มันปั้นขึ้นเอง ซึ่งอาจกระทบถึงข้อมูล production
ตัวอย่างเช่น field priority: String ตอนแรก AI มองว่ามีค่าเป็น "1", "2", "3" แต่ต่อมากลับเปลี่ยนเองเป็น "low", "medium", "high" โดยไม่มีใครสั่ง ข้อสันนิษฐานเหล่านี้ตกค้างอยู่ในข้อมูลทดสอบ และอาจทำให้ฐานข้อมูล production เพี้ยนได้
ทางที่ดีกว่าคือควรใช้ชนิดข้อมูลแบบ Enum ตั้งแต่ต้น (ปัญหานี้คือกลิ่น code Primitive Obsession นั่นเอง) และปัญหาจะทวีความรุนแรงเมื่อ app ซับซ้อนขึ้น
การป้องกันต้องอาศัย requirement ที่ครบและละเอียด รวมถึงระบุชุดค่าที่เป็นไปได้ ซึ่งในทางปฏิบัติทำได้ยาก เครื่องมือผู้ช่วยบางตัวช่วยชี้ช่องว่างของ requirement ได้ แต่ความเสี่ยงที่ requirement จะไม่ครบก็ยังอยู่
ปรากฏการณ์นี้คือรากของ anti-pattern Assumption-Driven Development โดยตรง
Brute force fixes — แก้ปัญหาแบบดันทุรัง
หัวข้อที่มีชื่อว่า “Brute force fixes — แก้ปัญหาแบบดันทุรัง”เมื่อเจอปัญหา AI มักเลือกทางลัดที่กระทบสถาปัตยกรรม ตัวอย่างเช่น
- เติม
@JsonIgnoreเพื่อกลบปัญหาการ serialize JSON ของความสัมพันธ์แบบ lazy-loaded - เพิ่มหน่วยความจำที่จัดสรรให้ เมื่อ build รันแล้วหน่วยความจำหมด
- ข้าม test (skip test) แทนที่จะแก้ให้ผ่าน
เรื่องนี้ผู้เขียนพูดตรง ๆ ว่า: “เราไม่รู้เลยว่าจะกันมันได้ยังไง” — และมันสะท้อนข้อจำกัดในการเข้าใจสถาปัตยกรรมของ model
ประกาศว่าสำเร็จ ทั้งที่ test ยังแดง (Declaring success in spite of red tests)
หัวข้อที่มีชื่อว่า “ประกาศว่าสำเร็จ ทั้งที่ test ยังแดง (Declaring success in spite of red tests)”AI เคยอ้างว่า build และ test ผ่านหมด ทั้งที่จริง test ยังแดง — ขัดกับคำสั่งชัด ๆ ว่างานจะถือว่าเสร็จก็ต่อเมื่อ test ผ่าน พฤติกรรมนี้เข้าข่าย anti-pattern Smoke and Mirrors คือทำให้ดูเหมือนเสร็จทั้งที่ยังไม่เสร็จ
วิธีลดปัญหาคือวางจุดตรวจแบบกำหนดผลได้ (deterministic checkpoint) ที่ห้าม workflow เดินต่อจนกว่า test จะเขียว
แต่มีข้อควรระวัง: งานวิจัยเกี่ยวกับ workflow agentic ในงาน business process automation ชี้ว่า LLM หาทางเลี่ยงจุดตรวจแบบนี้ได้ และในบริบทการ generate code มันอาจลบหรือข้าม testเพื่อให้ผ่านจุดตรวจไปเสียเอง
นี่คือตัวอย่างสด ๆ ของ Goodhart’s Law — เมื่อตัววัด (test ผ่าน) กลายเป็นเป้าหมาย มันก็เลิกเป็นตัววัดที่ดี
ปัญหาจาก static code analysis
หัวข้อที่มีชื่อว่า “ปัญหาจาก static code analysis”ผู้เขียนใช้ SonarQube วิเคราะห์ code ที่ generate ออกมาสองชุด (จากการทดลองสองรอบ) และพบปัญหาหลายระดับความรุนแรง สรุปเป็นตารางได้ดังนี้
| ปัญหาที่พบ | ความรุนแรง | หมวด | คำอธิบาย |
|---|---|---|---|
ควรใช้ Stream.toList() แทน Stream.collect(Collectors.toList()) | Major | Java 16 | แบบเดิมคืน list ที่แก้ไขได้ ทั้งที่ควรได้ list แบบ immutable |
if ซ้อนกันหลายชั้น ควรยุบรวม | Major | Code smell | ตรรกะยืดยาดเกิน โดยเฉพาะใน code validation/mapping |
| parameter ของ method ที่ไม่ได้ใช้ | Major | Code quality | รับ parameter เข้ามาแต่ไม่ได้ใช้ในตัว function |
| ปล่อยคอมเมนต์ TODO ค้างไว้ | Info | งานไม่เสร็จ | เช่น จุดที่ยังไม่ได้ implement จริง |
| string literal ซ้ำ (เกิน 10 ครั้ง) | Critical | Design | เสี่ยงตอน refactor ต้องไล่แก้ทุกจุด |
เรียก method ที่มี @Transactional ผ่าน this โดยตรง | Critical | Spring | annotation transaction จะไม่ทำงานเมื่อถูกเรียกตรง ๆ ภายใน class เดียวกัน |
ตัวอย่างคอมเมนต์ TODO ที่ถูกทิ้งค้างไว้ เช่น
// TODO: populated by joining with lead entity or separate service...นอกจากนี้ยังพบการใช้ null check ด้วย if พร่ำเพรื่อแทนที่จะใช้ Optional ผู้เขียนประเมินว่าปัญหาเหล่านี้เกี่ยวข้องกับความง่ายในการดูแลรักษาและความเสี่ยงจริง แม้จะเป็นระบบที่ AI ดูแลและให้บริการผู้ใช้เป็นล้านคนก็ตาม
จะเติม agent คอยไล่แก้ก็ได้ แต่ประเด็นจาก SonarQube แต่ละข้อมี “ความเกี่ยวข้องตามบริบท” ต่างกัน ทีมงานต้องใช้ดุลยพินิจตัดสินว่าอันไหน “won’t fix” (ตัดสินใจไม่แก้) ซึ่งต้องอาศัยความละเอียดอ่อนที่ AI ยังทำแทนไม่ได้
สรุปผลการทดลอง (Summary of the experiment)
หัวข้อที่มีชื่อว่า “สรุปผลการทดลอง (Summary of the experiment)”ผู้เขียนตั้งเกณฑ์รีวิวไว้ห้าข้อ และผลลัพธ์เทียบกับเป้าหมายเป็นดังนี้
| เกณฑ์ | เป้าหมาย | ผลที่ได้ |
|---|---|---|
| compile ได้ | code compile ผ่าน | ✓ ทำได้ |
| มี test ครบ | unit/integration/E2E, coverage มากกว่า 80% | ทำได้เป็นส่วนใหญ่ |
| ทำตามคำสั่ง | ใช้ library/pattern ตามที่ระบุ | ทำได้เป็นส่วนใหญ่ แต่เชื่อถือได้น้อยกว่าเรื่อง test |
| static code analysis | ไม่มีการละเมิดร้ายแรง | ยังเถียงกันได้ มีปัญหาที่กระทบการดูแลรักษาหลงเหลือ |
| function การทำงาน | ตรงตาม spec ไม่มีของแถม | ไม่สม่ำเสมอ และแย่ลงเมื่อขยับไปที่ 10 entity (เกมตีตัวตุ่น) |
โดยรวม: สำหรับ app เรียบ ๆ (3-5 entity) และเมื่อลงทุนกับกลยุทธ์อย่างหนัก AI ก็ generate code ที่ทำงานได้เป็นส่วนใหญ่ แต่พอเพิ่มความซับซ้อน (10 entity) ประกอบกับมีคำขอเปลี่ยนแปลง ปัญหาร้ายแรงก็โผล่: มันเดา business logic เอง, ลบ validation สำคัญทิ้ง, และข้อสันนิษฐานต่าง ๆ ก็ไม่สอดคล้องกัน
AI ยังไม่พร้อมที่จะสร้างและดูแลรักษา codebase ของซอฟต์แวร์เชิงธุรกิจให้ดูแลรักษาได้ในระยะยาว โดยปราศจากการกำกับดูแลของมนุษย์
สิ่งที่นำไปใช้ได้กับ workflow แบบ augmented ของคุณ
หัวข้อที่มีชื่อว่า “สิ่งที่นำไปใช้ได้กับ workflow แบบ augmented ของคุณ”แม้การทำงานอัตโนมัติเต็มตัวจะยังไม่พร้อม แต่สำหรับ workflow แบบ augmented (มนุษย์กับ AI ทำงานร่วมกัน) มีบทเรียนที่ใช้ได้ทันที
- prompt ที่นำกลับมาใช้ซ้ำได้ — ลงทุนออกแบบ prompt สำหรับงานเขียน code ที่ทำบ่อย ๆ ให้ดี แล้วเรียกใช้ได้ในผู้ช่วยเขียน code ตัวใดก็ได้ โดยไม่ต้องมี infrastructure agentic ที่ซับซ้อน
- reference application ผ่าน MCP — ได้ผลมากในการขยายมาตรฐานการเขียน code ไปทั้งทีม และยังทำ “reference drift detection” (การตรวจจับความเบี่ยงเบนจาก app อ้างอิง) เพื่อดันการเปลี่ยนแปลงล่าสุดไปใช้กับ code เป้าหมายได้
- ผนวก static code analysis เข้ากับกระบวนการ — สำคัญมากเมื่อ AI generate การเปลี่ยนแปลงชุดใหญ่ เพราะช่วยชี้ปัญหาได้เร็วกว่าให้คนไล่รีวิวเอง
- ยกระดับ abstraction ให้สูงที่สุด — generate ที่ระดับนามธรรมสูงสุดเท่าที่ทำได้ และเลือกใช้ script/codemod แบบกำหนดผลได้ แทนการให้ AI generate ทั้งหมด เมื่อทำได้
เกี่ยวกับกระบวนการพัฒนา workflow
หัวข้อที่มีชื่อว่า “เกี่ยวกับกระบวนการพัฒนา workflow”ระหว่างสร้าง workflow นี้ ผู้เขียนเจออุปสรรคเชิงปฏิบัติหลายอย่าง
- วงรอบ feedback ยาว — กว่าจะเห็นผลของการแก้ prompt ไหลลงไปกระทบปลายทางของ workflow ใช้เวลา 10-20 นาที ทำให้ debug ยากขึ้นไปอีก
- รักษาความสอดคล้องของ prompt ได้ยาก — เมื่อ prompt กระจายอยู่หลายที่ การคุมให้สอดคล้องกันเป็นเรื่องยาก reference application ช่วยได้แค่ในส่วนตัวอย่าง code ไม่ครอบคลุมคำสั่งทั่วไป และมักเจอความไม่สอดคล้องหลังรันทั้ง workflow จบไปแล้ว
- ประเมินความสำเร็จได้ยาก — E2E test ให้ความมั่นใจในระดับสูงแต่ไม่ครอบคลุมทั้งหมด และเมื่อ app โตขึ้น คำถามคือ แล้วใครจะรีวิว test ที่ AI generate ขึ้นมา?
- debug และตามรอยยาก — การไล่ย้อนจาก code ที่ generate กลับไปยัง requirement/prompt ต้นทางเป็นงานที่น่าเบื่อ และยิ่งแย่เมื่อ requirement และ workflow ใหญ่ขึ้น
- การทำงานร่วมกัน — ปัญหาข้างต้นทั้งหมดเป็นอุปสรรคต่อการทำงานเป็นทีม ยากที่จะมั่นใจว่าเพื่อนร่วมทีมจะไม่ไปทำของเดิมพัง และยากที่จะเห็นชัดว่ามีอะไรเปลี่ยนไปบ้าง
บทสะท้อนปิดท้าย (Final reflections)
หัวข้อที่มีชื่อว่า “บทสะท้อนปิดท้าย (Final reflections)”LLM ในอนาคตจะแก้ปัญหาเหล่านี้ได้ไหม?
หัวข้อที่มีชื่อว่า “LLM ในอนาคตจะแก้ปัญหาเหล่านี้ได้ไหม?”ผู้เขียนตั้งข้อสังเกตก่อนว่าความสามารถของ agent เขียน code พัฒนาไปไกลอย่างน่าประทับใจ ตัวอย่าง app Spring Boot เรียบ ๆ ในบทความนี้ยัง “เล็กน้อยจนน่าขำ” เมื่อเทียบกับสิ่งที่ผู้เขียนทำได้ด้วย AI ในช่วงครึ่งปีที่ผ่านมา
— กระนั้นก็ตาม ผู้เขียนก็ยังมองว่าปัญหาที่พบ (code เกิน, ข้อสันนิษฐานที่เปลี่ยนไปเปลี่ยนมา, การละเมิด static analysis, งานที่ทำไม่จบ, ทางลัดที่กระทบสถาปัตยกรรม) ล้วนไม่ใช่เรื่องเล็กสำหรับซอฟต์แวร์ production ที่ให้บริการคนเป็นล้าน
และเพราะเทคโนโลยีนี้เป็นแบบ non-deterministic (ไม่กำหนดผลตายตัว) จึงมี “ความน่าจะเป็นที่มองข้ามไม่ได้” ที่มันจะทำสิ่งที่เราไม่ต้องการ
Kent Beck เปรียบ agent เขียน code ว่าเป็น “จินนี่” (genies) ที่ให้พรตามคำขอ — แต่ให้ “ในแบบของมันเอง โดยฉวยทุกช่องโหว่ของถ้อยคำที่มนุษย์พูด”
ด้วยความหลากหลายมหาศาลของรูปแบบความล้มเหลวที่บันทึกไว้ ผู้เขียนจึงนึกไม่ออกว่าจะแก้ได้ด้วยการปรับปรุง model เพียงอย่างเดียว และนึกภาพไม่ออกว่าจะยอมรับการ deploy code กว่า 1,000 บรรทัดขึ้น service หลักแบบอัตโนมัติโดยไม่มีมนุษย์กำกับได้อย่างไร
คำถามที่ยังค้างอยู่
หัวข้อที่มีชื่อว่า “คำถามที่ยังค้างอยู่”จะเร่งวงรอบ human-in-the-loop ให้เร็วขึ้นได้อย่างไร? ผู้เขียนอ้างถึงคำปาฐกถาของ Andrej Karpathy ที่งาน AI Startup School ว่า “เรากำลังร่วมมือกับ AI — มัน generate แล้วมนุษย์ verify มันเป็นประโยชน์ของเราเองที่จะทำให้วงรอบนี้เร็วที่สุด และเราต้องจูง AI ไว้ด้วยสายจูง (keep the AI on a leash)”
ถ้าการ augment จะยังเป็นสิ่งที่หลีกเลี่ยงไม่ได้สำหรับซอฟต์แวร์ production เราก็ควรหันไปเร่งการ verify ของมนุษย์แทนที่จะรอ model ที่ดีกว่า คำถามคือ เราจะเข้าใจ changeset ก้อนใหญ่ได้เร็วแค่ไหน? ต้องการ test การวิเคราะห์ สรุป และภาพประกอบ (visualization) เพิ่มอีกไหม?
การเพิ่มการควบคุมกลับให้ผลตรงข้ามหรือเปล่า? ผู้เขียนยกการอภิปรายเรื่อง “Bitter Lesson” ของ Ethan Mollick ที่ว่า ความก้าวหน้าครั้งใหญ่ของ AI มักมาจากพลังคำนวณดิบ ๆ บวกกับการ generalize จนเอาชนะวิธีที่ใส่ความรู้อย่างประณีต (เช่นในหมากรุก)
แล้วเครื่องมือของเราควรโอบรับ “brute force” ทำนองเดียวกันไหม (agent หลายตัว เครื่องมือมากมาย กลยุทธ์ด้าน memory)?
ผู้เขียนลอง Claude Flow ทั้งกับ prompt แบบดิบและแบบมีโครงสร้าง แต่ก็ไม่ได้ผลดีกว่า workflow version ก่อน ๆ และด้วย “ความหลากหลายของปัญหาที่พบ” ผู้เขียนจึงยังนึกภาพไม่ออกว่าจะรับหน้าที่ on-call (เข้าเวรรับมือปัญหาระบบนอกเวลางาน) ให้กับ code ที่ถูก deploy แบบอัตโนมัติได้อย่างไร
code ของ workflow นี้เปิดเป็นสาธารณะบน GitHub (birgitta410/pushing-ai-autonomy-article)
ผู้เขียนขอบคุณทีมงาน Jigar Jani, Shraddha Surana และ Christopher Hastings รวมถึงชุมชน “AI-assisted software” ของ Thoughtworks สำหรับการแลกเปลี่ยนความเห็น (ได้แก่ Beto Frega, Brandon Cook, Folker Bernitt, James Lewis, Julien Deswaef, Kief Morris, Moritz Wilke, Vanya Seth และ Zhenjia Zhou)
บทความชิ้นนี้ต่อยอดกับแนวคิดในบทแปลอื่นในหมวดนี้ได้ดี — โดยเฉพาะ Spec-Driven Development (เขียน spec ให้ชัดก่อนส่งให้ AI) และ Structured-Prompt-Driven Development (ปฏิบัติต่อ prompt เป็นชิ้นงานชั้นหนึ่งและคง human-in-the-loop ไว้โดยตั้งใจ)
📄 อ่านต้นฉบับภาษาอังกฤษ: How far can we push AI autonomy in code generation? — Birgitta Böckeler, martinfowler.com (5 สิงหาคม 2025)