ในเดือนกรกฎาคม 2026 Robert C. Martin เขียนไว้ ว่าเขาไม่อ่านโค้ดที่สร้างโดย AI agent ของเขาอีกต่อไป
ข้อความนี้ได้รับความสนใจมาก ส่วนที่เหลือของโพสต์นั้นสำคัญกว่าสำหรับการเข้าใจวิธีการทำงานของเขา Martin ล้อมรอบ agent ด้วย unit test, Gherkin acceptance test, กระบวนการ QA, mutation testing, การตรวจสอบ coverage และ metric คุณภาพ โค้ดจะได้รับความมั่นใจจากเขาก็ต่อเมื่อผ่านระบบเหล่านั้น
เขาเคย พูดประเด็นคล้ายกัน เมื่อไม่กี่เดือนก่อน แทนที่จะตรวจสอบ implementation เขาดูที่ test coverage, โครงสร้าง dependency, cyclomatic complexity, ขนาดโมดูล และผล mutation-testing เมื่อมีคนเข้าใจผิดว่าเขาเลิกรีวิวโค้ดโดยสิ้นเชิง เขา ชี้แจง: "ผมรีวิวเยอะ — แค่ไม่ใช่ตัวโค้ด"
คำอธิบายของเขาสะท้อนปัญหาจริงในซอฟต์แวร์ที่ใช้ AI หนัก ๆ AI agent สามารถสร้างการเปลี่ยนแปลงได้เร็วกว่าที่คนจะอ่านทัน เมื่อปริมาณโค้ดที่ AI สร้างเกินขีดความสามารถในการรีวิวของทีม การตรวจสอบทีละบรรทัดจึงไม่สามารถเป็นแหล่งความมั่นใจเพียงอย่างเดียวได้อีก
เรากำลังเผชิญปัญหาเดียวกันนี้ที่ vm0 ตลอดแปดเดือนที่ผ่านมา Implementation ส่วนใหญ่ใน repository ของเราถูกเขียนโดย agent — หรือที่เรียกกันว่า vibe coding — ขณะที่วิศวกรหกคนยังคงรับผิดชอบต่อสิ่งที่โค้ดนั้นทำใน production
สรุปสั้น ๆ
vm0 คือโค้ดเบสที่เขียนด้วย vibe coding: agent เขียน implementation ส่วนใหญ่ และวิศวกรหกคนเป็นเจ้าของสิ่งที่มันทำใน production แปดเดือนผ่านไป repository มี 1,329,170 บรรทัดและ merged 630 pull request ในสัปดาห์เดียว ห้ากลไกหลักรับภาระคุณภาพที่การรีวิวทีละบรรทัดไม่สามารถรับได้อีกต่อไป
- สภาพแวดล้อมที่บรรจบกัน วิศวกรและ agent ทำงานใน dev container เดียวกัน และทุก pull request สามารถมี database branch ของตัวเอง คำสั่งที่ agent ใช้ได้ คนก็ใช้ได้
- ข้อจำกัดที่รันได้ TypeScript เข้มงวด, โมดูล API contract ที่ typed ประมาณ 135 โมดูล, Oxlint, ESLint และกฎสถาปัตยกรรม โดย CI ไม่ยอมรับ warning ใด ๆ กฎที่ตรวจสอบได้โดยเครื่องจะไม่ปล่อยให้รีวิว
- ทดสอบที่ boundary ใช้ testing trophy แทน test pyramid: integration test ผ่าน public module boundary, ใช้ PostgreSQL จริงและ migration จริง, mock เฉพาะที่ระบบภายนอก โค้ด test มีประมาณ 36 เปอร์เซ็นต์ของ repository
- วงจร feedback ที่สมบูรณ์ Agent สตาร์ทแอปและฐานข้อมูล, รัน migration, ขับ browser จริง แล้วรายงานคำสั่งที่รัน, path ที่ทดสอบ, และ screenshot PR ส่วนใหญ่ merge ในเวลาประมาณ 53 นาที
- การทำความสะอาดต่อเนื่อง Knip ลบ dead code อย่างกำหนดได้, workflow รายวันเคลียร์ AI slop ที่ linter หาไม่เจอ, pattern ที่เกิดซ้ำบ่อยจะถูกยกระดับเป็น lint rule หรือ type system
ความหมายที่แท้จริงของ vibe coding และ agentic coding
Vibe coding คือการ prompt LLM เพื่อขอโค้ด, รันสิ่งที่ได้, ขอให้เปลี่ยนแปลง และไม่สนใจโค้ดที่สร้างขึ้น Martin Fowler นิยาม ไว้อย่างแคบ และเน้นวลีว่า "ลืมไปเลยว่าโค้ดนั้นมีอยู่" เหมาะกับ prototype, ซอฟต์แวร์ที่ทิ้งได้ และเครื่องมือขนาดเล็กที่ผลกระทบจำกัด
Agentic coding ซึ่ง Fowler เรียกว่า agentic programming คือสิ่งที่ทีมทำเมื่อคาดว่าจะต้องดูแลผลลัพธ์ Agent อ่าน repository, แก้ไขไฟล์, รันทดสอบ และทำงานอิสระเป็นเวลานาน ขณะที่คนยังคงเป็นเจ้าของโครงสร้างและพฤติกรรม และรีวิวหลักฐานที่ run สร้างขึ้น: ผลทดสอบ, สัญญาณคุณภาพ, preview และพฤติกรรม production
ความแตกต่างไม่ใช่ปริมาณโค้ดที่โมเดลเขียน แต่คือจุดที่มนุษย์ให้ความสนใจ
| Vibe coding | Agentic coding | |
|---|---|---|
| ความอิสระของ agent | prompt, run, prompt อีกครั้ง | อ่าน repository, แก้ไข, ทดสอบ, ทำซ้ำ |
| ใครอ่านโค้ด | ไม่มีใคร | คนอ่านเฉพาะส่วนที่มีความเสี่ยง |
| อะไรที่ถูกตรวจสอบ | ดูว่า output ดูถูกต้องไหม | types, contracts, tests, previews, production signals |
| เหมาะกับ | prototype และเครื่องมือที่ทิ้งได้ | ซอฟต์แวร์ที่ต้องดูแลระยะยาว |
| ความล้มเหลวทั่วไป | โค้ดที่ไม่มีใครเข้าใจ | verification อ่อนเกินไปสำหรับปริมาณงาน |
Agent ที่แก้ไข repository เป็นหนึ่งในแนวโน้มที่กว้างขึ้น เราเคยพูดถึง ฝั่งที่ไม่ใช่นักพัฒนา แยกไว้แล้ว
เรามักเรียก vm0 ว่าเป็นโปรเจกต์ vibe-coded เพราะคำนั้นกลายเป็นคำย่อสำหรับซอฟต์แวร์ที่เขียนโดย AI เป็นหลัก ตามนิยามของ Fowler มันใกล้เคียงกับ agentic coding มากกว่า วิศวกรของเราพิมพ์ implementation น้อยลง แต่ยังคงเป็นเจ้าของสถาปัตยกรรม ต้นทุนการดูแล และพฤติกรรม production
เมื่อการสร้างโค้ดเร็วขึ้น ความรับผิดชอบเหล่านั้นจึงย้ายไปอยู่ในสภาพแวดล้อมพัฒนา, type system, ชุดทดสอบ และ workflow อัตโนมัติ
แปดเดือนของการเติบโตในโค้ดเบสที่เขียนด้วย vibe coding
repository ของ vm0 ถูกสร้างขึ้นในเดือนพฤศจิกายน 2025 ณ เวลาของ รายงานขนาดวิศวกรรมประจำสัปดาห์ล่าสุด มันมีอายุประมาณแปดเดือนครึ่ง
repository มี:
| Metric | Count |
|---|---|
| บรรทัด logic ที่ไม่ว่างเปล่า | 1,329,170 |
| บรรทัด production | 850,913 |
| บรรทัด test | 478,257 |
| ไฟล์ source | 5,549 |
| ไฟล์ test | 1,327 |
| package | 44 |
| commit บน main branch | 14,005 |
ตัวเลขเหล่านี้มาจากรายงานขนาดวิศวกรรมประจำสัปดาห์ของเราสำหรับสัปดาห์ที่ 20–26, 2026 วัดโดยตรงจาก monorepo โค้ด test มีประมาณ 36 เปอร์เซ็นต์ของโค้ดเบสที่วัดได้ ตัวเลขนี้เป็นสัดส่วนของปริมาณโค้ด ไม่ใช่ coverage แต่ช่วยให้เห็นว่ามี implementation สะสมรอบ ๆ verification มากแค่ไหน
ในสามสัปดาห์เต็มล่าสุด วิศวกรหกคนสร้าง commit 541, 640 และ 631 ตามลำดับ ในสัปดาห์ที่ 20–26 GitHub บันทึก pull request ที่ merge แล้ว 630 รายการ วิศวกรหกคนเป็นผู้เขียน 556 รายการ ที่เหลืออีก 74 มาจาก release automation
เวลามัธยฐานตั้งแต่เปิด pull request จน merge อยู่ที่ประมาณ 53 นาที เปอร์เซ็นไทล์ที่ 90 อยู่ที่ประมาณ 7.4 ชั่วโมง
ด้วยอัตราการเปลี่ยนแปลงนี้ การรีวิวโดยมนุษย์ยังมีประโยชน์แต่ไม่สามารถรับภาระคุณภาพทั้งหมดได้ เราต้องการสัญญาณอิสระในหลายจุดของวงจรชีวิตการเปลี่ยนแปลง
แนวทางปัจจุบันของเรามีห้าธีมหลัก: สภาพแวดล้อมที่บรรจบกัน, ข้อจำกัดที่รันได้, การทดสอบที่ boundary, วงจร feedback ที่สมบูรณ์ และการทำความสะอาดต่อเนื่อง
ทำให้สภาพแวดล้อมพัฒนาบรรจบกันด้วย dev container
ความล้มเหลวที่เกิดซ้ำยากต่อการทำซ้ำ มักมาจาก state ที่ไม่ปรากฏใน pull request
นักพัฒนาอาจมี environment variable ที่ไม่ได้บันทึก, เครื่องมือที่ติดตั้งแบบ global, ไฟล์ config เก่า หรือฐานข้อมูลที่รันมาหลายเดือน คนจะชินกับรายละเอียดเหล่านี้ แต่ agent มักมองไม่เห็น
เราค่อย ๆ เลิกถือว่าเครื่อง host คือสภาพแวดล้อมมาตรฐาน วิศวกรและ agent ทำงานใน dev container ภาพ development รวม toolchain ของโปรเจกต์, PostgreSQL, pgvector, Chromium และเครื่องมือ automation สำหรับ browser
CI รัน image toolchain ที่ versioned ซึ่งสร้างจาก Dockerfile หลายขั้นเดียวกัน ภาพ development และ CI มีจุดประสงค์ต่างกัน และ production ก็มีรูปแบบ deployment ของตัวเอง คุณสมบัติที่สำคัญคือ convergence: เวอร์ชันของเครื่องมือ, dependency และ runtime assumption ถูกกำหนดและ versioned ไว้ชัดเจน
สิ่งนี้ทำให้เราคาดหวังได้ตรงไปตรงมา คำสั่งที่ agent รันควรทำซ้ำได้โดยวิศวกรใน dev container เดียวกัน การตรวจสอบที่ผ่านใน development ควรรันด้วย toolchain ที่ใกล้เคียงกันใน CI
เรานำแนวคิดเดียวกันมาใช้กับข้อมูล preview ของ pull request แต่ละอันสามารถมี database branch ของตัวเองและรัน migration และ seed จริง Agent สามารถสร้างข้อมูล, เปลี่ยนแปลง, และทดสอบ destructive ได้โดยไม่ต้องยืมฐานข้อมูลของนักพัฒนาหรือรับ state จาก pull request อื่น
การเปลี่ยนแปลงสภาพแวดล้อมเดินทางผ่าน repository การอัปเกรดเครื่องมือ, เวอร์ชัน browser และ extension ของฐานข้อมูลถูกรีวิวและเผยแพร่เหมือนกับโค้ดแอปพลิเคชัน
ทำให้มาตรฐานวิศวกรรมรันได้ใน type และ lint rule
มาตรฐานที่เขียนช่วยให้คนเข้าใจการตัดสินใจด้านดีไซน์ แต่ไม่รับประกันว่าทุกการเปลี่ยนแปลงจะทำตาม โดยเฉพาะระหว่าง session ของ agent ที่ยาว
เรานำกฎที่เสถียรใส่ใน type system, linter และ CI เมื่อใดก็ตามที่กฎนั้นแสดงออกได้อย่างน่าเชื่อถือ
vm0 ใช้ TypeScript แบบ strict รวมถึง strict และ noUncheckedIndexedAccess บาง package เปิดการตรวจสอบเพิ่มเติมสำหรับค่าที่ไม่ได้ใช้และ implicit return
API ใช้ schema-first typed REST contract layer ที่สร้างด้วย type machinery ของ tRPC และ Zod schema Drizzle เชื่อมต่อการเข้าถึงฐานข้อมูลกับ type ของ TypeScript ณ เวลานี้ repository มี contract module ประมาณ 135 โมดูล
ตัวเลือกเหล่านี้ไม่สามารถจับการตัดสินใจผลิตภัณฑ์ที่ผิดได้ แต่ช่วยให้เห็น interface drift เร็วขึ้น เมื่อ field หรือ response เปลี่ยน ผู้เรียกที่เกี่ยวข้องมักจะ fail ระหว่าง static analysis ข้อผิดพลาดที่ได้มักจะเฉพาะเจาะจงพอที่ agent จะใช้ใน iteration ถัดไป
Linting ครอบคลุมกฎอีกกลุ่มหนึ่ง แพลตฟอร์มรัน Oxlint, การตรวจสอบที่รู้จัก type และ ESLint พร้อมกฎสถาปัตยกรรมเฉพาะโปรเจกต์ CI ไม่ยอมรับ warning ใด ๆ warning ที่ค้างอยู่จะกลายเป็นเสียงรบกวนที่ทั้งคนและ agent มองข้ามได้ง่าย
เมื่อปัญหาเกิดซ้ำ เราพิจารณาว่าควรตรวจสอบที่ไหน:
- type system แสดงออกได้ไหม?
- linter หรือเครื่องมือโครงสร้างตรวจจับได้แม่นยำไหม?
- ต้องใช้ judgment เชิงความหมายข้าม repository หรือไม่?
สองข้อแรกให้ feedback ที่เร็วและกำหนดได้กับทุกการเปลี่ยนแปลง กลุ่มที่สามเราจัดการด้วย workflow ที่เกิดซ้ำซึ่งจะอธิบายต่อไป
ลด pattern ที่ผิดพลาดในโค้ดที่ AI สร้าง
ฟีเจอร์บางอย่างของภาษาใช้งานได้จริงแต่ก็ปรากฏบ่อยในโค้ดที่ agent สร้างซึ่งเปราะบาง
try/catch สามารถทำให้ error boundary ไม่ชัดเจน เมื่อ agent เจอ failure การเพิ่ม catch block และ fallback เป็นวิธีง่าย ๆ ที่จะให้ path ปัจจุบันรันต่อแต่สูญเสีย error เดิม การผสม Promise .then() และ .catch() กับ async/await ทำให้ control flow กระจายหลายรูปแบบ
React's useEffect สร้างปัญหาแบบเดียวกันใน state management มักใช้เพื่อ copy state, sync แหล่งข้อมูลสองที่ หรือเข้ารหัส dependency ที่ดูยากจาก data model
core web platform จำกัด pattern เหล่านี้โดยปริยาย ข้อยกเว้นที่มีเหตุผลสามารถอยู่ได้โดยมีคำอธิบายข้าง ๆ
โค้ด production ใน core web platform ปัจจุบันไม่มี useEffect เราใช้ ccstate และ pattern ที่ไม่มี side effect อื่น ๆ เพื่อ model dependency และ side effect ให้ชัดเจนขึ้น ยังมีการเรียก useEffect ใน production บางส่วนใน monorepo โดยเฉพาะใน UI ที่ใช้ร่วมกันและโค้ด desktop ดังนั้นขอบเขตของข้ออ้างนี้สำคัญ
กฎเหล่านี้เติบโตจากความล้มเหลวที่เกิดซ้ำใน repository นี้ เมื่อ pattern สร้างปัญหาดูแลซ้ำ ๆ เราจะย้ายจากคำแนะนำรีวิวไปสู่ข้อจำกัดที่รันได้
ทดสอบที่ boundary ของ module: testing trophy ไม่ใช่ test pyramid
vm0 ไม่ใช้ test pyramid แบบดั้งเดิม คู่มือ testing ของเราบรรยาย testing trophy: static analysis เป็นฐาน, integration test เป็นชั้นหลัก, และ end-to-end test จำนวนน้อยสำหรับ user journey สำคัญ
unit test พบไม่บ่อย เราใช้เฉพาะกับ logic ที่สำคัญด้านความปลอดภัย, อัลกอริทึม และ state machine พฤติกรรมธุรกิจส่วนใหญ่ทดสอบผ่าน public boundary ของ module
API test เรียกแอปจริงผ่าน contract ข้อมูลทดสอบเตรียมผ่าน endpoint production เมื่อทำได้ และ assertion ทำผ่านพฤติกรรมสาธารณะ ทดสอบหลีกเลี่ยงการเข้าถึง service ภายในหรือแก้ไขตารางฐานข้อมูลโดยตรง เพราะจะผูกกับ implementation ปัจจุบัน
โครงสร้างพื้นฐานภายในยังคงใช้ของจริงเมื่อค่าใช้จ่ายสมเหตุสมผล:
- PostgreSQL และ pgvector
- database migration
- filesystem
- service ภายใน
- mock ที่ boundary ของระบบภายนอก
integration test เหล่านี้รันช้ากว่า unit test ที่แยกขาดและต้องการสภาพแวดล้อมที่สมบูรณ์กว่า แต่ช่วยลดช่องว่างระหว่าง "test ผ่าน" กับ "แอปทำงานนี้กับฐานข้อมูลจริงได้"
boundary test เปิดโอกาสให้ refactor ภายในขนาดใหญ่ Agent สามารถจัดระเบียบ module ใหม่, แยก service หรือเปลี่ยน data-access layer ขณะที่พฤติกรรมสาธารณะยังได้รับการปกป้อง
Uncle Bob ชอบผสมแบบอื่น โดยใช้ unit test, Gherkin และ mutation testing หนัก หลักการร่วมคือ verification อิสระ กระบวนการที่สร้างโค้ดไม่ควรเป็นแหล่งเดียวที่อ้างว่าโค้ดใช้ได้
ให้ agent มี feedback loop ที่สมบูรณ์
run ของ coding agent ในช่วงแรกมักจบด้วยรายงานคุ้นเคย: โค้ดถูกเปลี่ยนและ TypeScript ผ่าน; กรุณาสตาร์ทแอปและตรวจสอบหน้า
นั่นทำให้ครึ่งหนึ่งของวงจรพัฒนายังอยู่กับวิศวกร ใครสักคนต้องเตรียมฐานข้อมูล, รัน service, เปิด browser, สร้างข้อมูล, สังเกตความล้มเหลว และอธิบายกลับไปยัง agent
ตอนนี้ agent มีสภาพแวดล้อมพัฒนาพอจะทำงานเหล่านั้นเองได้มากขึ้น สามารถสตาร์ทแอปและฐานข้อมูล, รัน migration, สร้างข้อมูลทดสอบ, เข้า preview ของ pull request และโต้ตอบกับ browser จริง
การตรวจสอบผ่าน browser จับปัญหาที่ type check และ API test ไม่ครอบคลุมดี เช่น navigation ที่เสีย, loading state ที่ไม่จบ, error permission ที่เกิดเฉพาะใน flow สมบูรณ์ และ visual regression
เมื่อจบ run agent จะรายงานคำสั่งที่รัน, path ที่ทดสอบ และสิ่งที่สังเกตเห็น สำหรับการเปลี่ยน UI สามารถแนบ screenshot วิศวกรสามารถตรวจสอบหลักฐานนี้ก่อนตัดสินใจเปิด preview ด้วยตนเอง
screenshot ไม่ใช่ test และไม่พิสูจน์ว่าไม่มีข้อผิดพลาดอื่น แต่มันลดต้นทุนการสร้าง context ของ agent ใหม่ สำหรับการเปลี่ยนแปลง UI เล็ก ๆ ขั้นตอนที่ทำซ้ำได้และ screenshot สุดท้ายมีประโยชน์กว่าข้อความว่า "ควรจะแก้แล้ว"
คุณภาพงานของ agent ขึ้นกับ feedback ที่ได้โดยไม่ต้องรอคน
รักษา branch ให้สั้นด้วย trunk-based development
การสร้างโค้ดเร็วทำให้เกิด branch จำนวนมาก
feature branch ที่อยู่นานสะสม merge conflict, งานซ้ำ และ context ที่ล้าสมัย เมื่อ pull request ใหญ่ขึ้น การรีวิวจะยากและช้าลง เราใช้ trunk-based development เพื่อให้ branch สั้นและ integrate อย่างต่อเนื่องรอบ ๆ main
กฎของ main branch เราต้องการ:
- pull request สำหรับทุกการเปลี่ยนแปลง
- ประวัติเส้นตรงและ squash merge
- merge queue
- ตรวจสอบด้วย Turbo, Rust และ security
- ไม่มีการข้าม gate ที่จำเป็นเป็นประจำ
automation เดินตามเส้นทางเดียวกัน Agent อาจสร้าง pull request และงานบำรุงรักษาความเสี่ยงต่ำบางอย่างอาจ merge อัตโนมัติได้ แต่การเปลี่ยนแปลงยังต้องผ่าน CI และ merge queue
ขนาดและอายุของ pull request อธิบายได้ว่าทำไม 630 รายการ merge ได้ในสัปดาห์เดียว การเปลี่ยนแปลงเล็ก ๆ มี context น้อยกว่า ตรวจสอบง่ายกว่า และมีโอกาส conflict กับงาน product หรือ automated repair อื่นน้อยกว่า
Knip เป็น garbage collection ของ repository: ลบ dead code
การสร้างโค้ดเพิ่มไฟล์และ abstraction โดยธรรมชาติ การลบมักต้อง prompt แยก
หลัง refactor ไฟล์เก่าอาจยังอยู่ใน repository การลบ feature อาจเหลือ export, dependency และ entry point สิ่งเหล่านี้ไม่ค่อยทำให้ test พัง แต่ทำให้โค้ดเบสยากขึ้นเมื่อเวลาผ่านไป
เราใช้ Knip หาไฟล์, export, dependency และ entry point ที่ไม่ได้ใช้ TypeScript อาจยืนยันว่าโค้ดถูกต้อง; Knip ถามว่ายังมีส่วนร่วมในระบบหรือไม่
เศษซากเหล่านี้มีต้นทุนเพิ่มสำหรับ agent repository คือแหล่ง context สำคัญของ agent helper ที่ล้าสมัยหรือ implementation ที่ถูกทิ้งอาจดูเหมือน pattern ที่ผ่านการอนุมัติสำหรับ agent ถัดไป การรักษา context ให้สะอาดคือครึ่งหนึ่งของ context engineering: สิ่งที่ agent อ่านจาก repository สำคัญพอ ๆ กับ prompt ที่ได้รับ
การลบ dead code ยังปรับปรุง input สำหรับ run ในอนาคต Knip จัดการส่วนที่กำหนดได้ของงานนี้เร็วพอจะเป็น quality check ประจำ
workflow cleanup AI slop ที่เกิดซ้ำ
Knip และ ESLint มีขีดจำกัดที่ชัดเจน การเสื่อมสภาพหลายรูปแบบต้องใช้ context ของโปรเจกต์และ judgment เชิงความหมาย
เราใช้ "AI slop" เป็นป้ายเรียกเศษซากนี้: fallback ที่ไม่จำเป็น, abstraction ที่ซ้ำ, test ที่ข้าม public boundary หรือ branch ป้องกัน state ที่เป็นไปไม่ได้ แต่ละกรณีดูไม่เป็นอันตราย แต่รวมกันแล้วทำให้ repository เข้าใจยากขึ้นและให้ตัวอย่างไม่ดีแก่ agent ถัดไป
workflow vm0 หลายรายการ สแกนหา pattern เหล่านี้ตามกำหนดเวลา
AI-slop cleanup รายวันมองหาเศษซากใหม่และเลือกชุดแก้ไขที่มั่นใจสูงและเสี่ยงต่ำ workflow อื่นตรวจสอบ API test ที่เข้าถึง service ภายใน, antipattern ของ React และ ccstate และ technical debt ที่จัดการได้อย่างปลอดภัย
แต่ละ workflow จำกัดขอบเขตการเปลี่ยนแปลง เปิด pull request แล้วใช้ type check, lint rule, test และ merge queue ตามปกติ pull request ที่ตั้งให้ merge อัตโนมัติก็ยังต้องผ่าน gate เดิม

การรัน agent ตามกำหนดเวลาไม่ฟรี และเราเคยเขียนแยกเรื่อง ควบคุมต้นทุน workflow ไม่พยายามเคลียร์ technical debt ทั้งหมดในรอบเดียว ชุดเล็ก ๆ รายวันตรวจสอบง่ายกว่าและรบกวนน้อยกว่าการ cleanup ใหญ่ทุกไม่กี่เดือน
เมื่อ workflow เจอ pattern เดิมบ่อยพอ เราพิจารณาย้าย check ไปที่ ESLint, Knip หรือ type system workflow เชิงความหมายทำหน้าที่เป็นที่สังเกตและปรับแต่ง rule ก่อนเปลี่ยนเป็น check ที่ deterministic และถูกกว่า
เปลี่ยน test ที่ flake ให้ซ่อมอัตโนมัติ
workflow อีกกลุ่มเริ่มจาก GitHub Actions ที่ fail
เมื่อ test fail บน main branch หรือ merge queue workflow จะอ่าน log, ตรวจสอบหลักฐาน flakiness และดู retry, timing และปัจจัยสภาพแวดล้อม ถ้าหลักฐานชี้ไปที่การซ่อมเฉพาะ จะอัปเดต test หรือ implementation, เปิด pull request และรัน CI เต็มอีกครั้ง
การผ่านเมื่อ retry ไม่ได้ทำให้ failure เดิมไม่เป็นอันตราย ทีมที่พึ่ง retry button จะค่อย ๆ เสียความเชื่อมั่นใน build สีแดง เมื่อเป็นเช่นนั้น check ที่ fail จะกลายเป็นเสียงรบกวนอีกแบบ
workflow ซ่อมอัตโนมัติเปลี่ยน failure ที่เกิดเป็นพัก ๆ ให้เป็น code change ที่ตรวจสอบได้ การวินิจฉัย, patch และการตรวจสอบยังคงมองเห็นได้ใน pull request วิศวกรสามารถตรวจสอบการเปลี่ยนแปลงที่เสี่ยงสูงกว่า ขณะที่การแก้ไขแคบ ๆ ที่มีหลักฐานแข็งแรงสามารถผ่าน merge queue ได้
ระบบคุณภาพของเรามีสามชั้นใหญ่ ๆ:
| Stage | Mechanisms | ประเด็นทั่วไป |
|---|---|---|
| ระหว่างเขียน | TypeScript, contract, Drizzle, ESLint | type error, interface drift, pattern โค้ดที่รู้จัก |
| ก่อน merge | Knip, integration test, ฐานข้อมูลจริง, preview, merge queue | dead code, พฤติกรรม module, ผล runtime สมบูรณ์ |
| หลัง merge | workflow vm0 ที่กำหนดเวลาและ event | AI slop, semantic antipattern, test flake, architectural drift |
แต่ละชั้นส่งต่อกัน ปัญหาที่ workflow เจออาจกลายเป็น static rule ความล้มเหลวที่เจอใน CI หรือ production อาจกลายเป็น test และแนวทางวิศวกรรมใหม่
จุดที่มนุษย์ให้ความสนใจในการรีวิวโค้ดที่ AI สร้าง
วิศวกร vm0 ยังอ่านโค้ด โดยเฉพาะการเปลี่ยนแปลงสถาปัตยกรรม, งานที่สำคัญด้านความปลอดภัย, การชำระเงิน และ migration ข้อมูล เราไม่ได้ตั้งกฎทีมว่า "ไม่อ่านโค้ดเลย"
สิ่งที่เปลี่ยนคือการจัดสรรความสนใจในการรีวิว การอ่านโค้ดเป็นเพียงหนึ่งสัญญาณในบรรดา contract, ขอบเขต test, พฤติกรรม preview, screenshot, metric คุณภาพ และการวินิจฉัย workflow
การตัดสินใจหลายอย่างยังต้องใช้ judgment ของผู้มีประสบการณ์:
- ข้อกำหนดครบถ้วนหรือไม่
- boundary ของ module ควรอยู่ตรงไหน
- failure ไหน recover ได้
- fault สร้างผลกระทบธุรกิจแค่ไหน
- security model เหมาะสมหรือไม่
- pattern ความล้มเหลวใหม่ที่กฎปัจจุบันยังไม่ครอบคลุมคืออะไร
การเปลี่ยนแปลงคล้ายกันเกิดขึ้นในฝั่งดีไซน์ ที่ design-as-code ย้ายการตัดสินใจด้านภาพเข้า repository เดียวกันและเส้นทางรีวิวเดียวกัน วิศวกรยังดูแลสภาพแวดล้อมรอบ agent เมื่อปัญหาเกิดซ้ำ เราตัดสินใจว่าจะเพิ่ม type constraint, lint rule, test หรือ workflow ที่เกิดซ้ำ ระบบพัฒนาได้กลายเป็น artifact วิศวกรรมที่สำคัญในตัวเอง
โค้ดที่อ่านง่ายยังสำคัญ ผู้อ่านถัดไปอาจเป็นวิศวกรหรือ agent อีกคน โค้ดที่พันกันใช้ context มากขึ้น, ขยายขอบเขตการเปลี่ยนแปลงในอนาคต และทำให้ verification ไม่น่าเชื่อถือ
ความปลอดภัยและการเปลี่ยนแปลงที่ไม่เร็วขึ้น
ความเร็วไม่ได้กระจายเท่ากัน vm0 มีรายการสั้นของการเปลี่ยนแปลงที่เดินตามจังหวะตัวเอง: งานที่สำคัญด้านความปลอดภัย, การชำระเงิน, migration ข้อมูล และการตัดสินใจสถาปัตยกรรม วิศวกรอ่านทีละบรรทัด ไม่ว่าใครหรืออะไรจะเป็นคนเขียน
ความเสี่ยงด้านความปลอดภัยของโค้ดที่ AI สร้าง จากประสบการณ์ของเรา ไม่ใช่ช่องโหว่แปลกใหม่ แต่คือโค้ดที่ดูสมเหตุสมผลแต่ไม่มีใครเป็นเจ้าของ การควบคุมที่สำคัญคือสิ่งปกติที่ทำอย่างสม่ำเสมอ:
- unit test ซึ่งปกติเราใช้น้อย จะใช้จงใจสำหรับ logic ที่สำคัญด้านความปลอดภัย, อัลกอริทึม และ state machine
- test mock เฉพาะที่ boundary ของระบบภายนอก โครงสร้างพื้นฐานภายในใช้ของจริง ดังนั้นการเปลี่ยนที่ทำให้ contract ภายในพังจะ fail ใน CI ไม่ใช่ production
- agent ทำงานใน dev container กับ database branch ต่อ pull request ไม่ใช่เครื่องนักพัฒนาและไม่ใช่ฐานข้อมูลที่แชร์
- merge queue และ check ที่จำเป็นไม่มีการข้ามเป็นประจำ รวมถึง pull request ที่ agent เปิดและตั้ง merge อัตโนมัติ
try/catchถูกจำกัดโดยปริยาย ดังนั้น failure จะไม่ถูก fallback ที่ agent เพิ่มกลบเพื่อให้ path รันต่อ
การจัดการ credential เป็นปัญหาดีไซน์แยกต่างหาก: เราอธิบาย broker pattern ที่เก็บ token ให้พ้นมือ agent ไว้ในบทความแยก
ทั้งหมดนี้ไม่ได้ทำให้โค้ดที่สร้างปลอดภัยในตัวเอง แต่มันจำกัดชุดการเปลี่ยนแปลงที่ต้องใช้การตัดสินใจของมนุษย์เป็นตัวควบคุมเดียว และทำให้ชุดนั้นชัดเจน
ตัวเลขเหล่านี้ไม่ได้แสดงอะไร
รายงานประจำสัปดาห์แสดงขนาด repository และความเร็วในการส่งมอบ แต่มันไม่ได้แสดงความน่าเชื่อถือของ production โดยตรง
การประเมินผลต่อคุณภาพ runtime ต้องใช้ availability, อัตรา error ใน production, จำนวน incident, อัตรา change-failure, ความถี่ rollback และ mean time to recovery การรัน CI สีเขียวอธิบายเพียงส่วนหนึ่งของกระบวนการส่งมอบ
เรายังคงรวบรวมตัวชี้วัดผลลัพธ์เหล่านั้น แนวปฏิบัติวิศวกรรมอธิบายว่าระบบจัดการความเสี่ยงอย่างไร ข้อมูล production แสดงว่าการจัดการนั้นได้ผลแค่ไหน
คำถามที่พบบ่อย
vibe coding คืออะไร? vibe coding คือการ prompt LLM เพื่อขอโค้ด, รันสิ่งที่ได้, ขอให้เปลี่ยนแปลง และไม่อ่านโค้ดที่สร้าง Martin Fowler นิยามแคบโดยตั้งใจ: นักพัฒนาควร "ลืมไปเลยว่าโค้ดนั้นมีอยู่" เหมาะกับ prototype, ซอฟต์แวร์ที่ทิ้งได้ และเครื่องมือที่ผลกระทบจำกัด
agentic coding คืออะไร? agentic coding คือรูปแบบการพัฒนาที่ใช้ AI ช่วยในระยะยาว ซึ่ง agent อ่าน repository, แก้ไขไฟล์, รันทดสอบ และ iterate เองเป็นเวลานาน คนยังคงเป็นเจ้าของสถาปัตยกรรมและพฤติกรรม และรีวิวหลักฐานที่ run สร้างขึ้นแทนที่จะอ่านทุกบรรทัด
vibe coding กับ agentic coding ต่างกันอย่างไร? ต่างกันที่ความสนใจ ไม่ใช่ผู้เขียน ใน vibe coding โค้ดจะไม่ถูกตรวจสอบเลย ใน agentic coding agent ทำงานอิสระ ขณะที่วิศวกรตรวจสอบหลักฐานรอบ ๆ เช่นผลทดสอบ, สัญญาณคุณภาพ, preview และพฤติกรรม production vm0 มักถูกเรียกว่า vibe-coded; ตามนิยามของ Fowler มันคือ agentic coding
AI slop คืออะไร? AI slop คือเศษซากที่โค้ดที่ AI สร้างทิ้งไว้: fallback ที่ไม่จำเป็น, abstraction ที่ซ้ำ, test ที่ข้าม public boundary, branch ป้องกัน state ที่เป็นไปไม่ได้ แต่ละกรณีดูไม่เป็นอันตราย แต่รวมกันแล้วทำให้ repository เข้าใจยากขึ้นและให้ตัวอย่างไม่ดีแก่ agent ถัดไป
รีวิวโค้ดที่ AI สร้าง 630 pull request ต่อสัปดาห์อย่างไร? ไม่อ่านทีละบรรทัด ที่ vm0 การรีวิวของมนุษย์จะอยู่ที่จุดที่ต้องใช้ judgment: สถาปัตยกรรม, งานที่สำคัญด้านความปลอดภัย, การชำระเงิน, migration ข้อมูล ส่วนอื่น ๆ ใช้กลไก: type และ contract ที่เข้มงวด, integration test ที่ boundary, ฐานข้อมูลจริงใน preview ต่อ pull request, การตรวจสอบผ่าน browser, Knip และ merge queue ที่ไม่มีการข้ามเป็นประจำ
แนวปฏิบัติที่ดีที่สุดสำหรับ vibe coding ที่สเกลใหญ่คืออะไร? ห้าข้อที่ใช้ได้ตลอดแปดเดือนที่ vm0: ทำให้สภาพแวดล้อมพัฒนาบรรจบกันเพื่อให้ agent และคนรันคำสั่งเดียวกัน, ทำให้มาตรฐานรันได้ใน type, linter และ CI แทนเอกสาร, ทดสอบที่ boundary ของ module กับโครงสร้างพื้นฐานจริง, ให้ agent มี feedback loop สมบูรณ์รวม browser และทำความสะอาดต่อเนื่องแทน cleanup ใหญ่เป็นครั้งคราว
ความเสี่ยงด้านความปลอดภัยของโค้ดที่ AI สร้างคืออะไร?
ความเสี่ยงทั่วไปไม่ใช่ช่องโหว่แปลกใหม่ แต่คือโค้ดที่ดูสมเหตุสมผลแต่ไม่มีใครเป็นเจ้าของ vm0 ให้ human review กับงานที่สำคัญด้านความปลอดภัย, การชำระเงิน, migration ข้อมูล ใช้ unit test สำหรับ logic เหล่านั้น, ให้โครงสร้างพื้นฐานภายในจริงใน test, จำกัด pattern อย่าง try/catch ที่กลบ failure และไม่อนุญาตให้ข้าม check ที่จำเป็นเป็นประจำ
โค้ดที่ AI สร้างก่อให้เกิด technical debt หรือไม่? มันก่อให้เกิดแบบเฉพาะ: โค้ดที่ยัง compile และผ่าน test แต่ไม่เข้าร่วมในระบบอีกต่อไป รวมถึงเศษซากเชิงความหมายที่ linter หาไม่เจอ Knip ลบส่วนที่กำหนดได้ workflow ที่เกิดซ้ำจัดการส่วนที่เหลือเป็นชุดเล็ก ๆ รายวัน และ pattern ที่เกิดซ้ำบ่อยจะกลายเป็น lint rule หรือ type constraint
โค้ดเบส vm0 ส่วนใหญ่เขียนโดย AI หรือไม่? implementation ส่วนใหญ่ วิศวกรหกคนเป็นผู้เขียน 556 จาก 630 pull request ที่ merge ในสัปดาห์ที่ 20–26, 2026 ที่เหลือเขียนโดย release automation และ agent เขียนโค้ดส่วนใหญ่ใน pull request เหล่านั้น สิ่งที่วิศวกรเป็นเจ้าของคือสถาปัตยกรรม, constraint และพฤติกรรม production
testing trophy คืออะไร และทำไมไม่ใช้ test pyramid? testing trophy วาง static analysis เป็นฐาน, integration test เป็นชั้นหลัก และ end-to-end test จำนวนน้อยด้านบน vm0 ใช้เพราะ test ที่เขียนผ่าน public boundary ของ module ยังคงปกป้องพฤติกรรมขณะที่ agent จัดระเบียบ implementation ใหม่ข้างใต้ ซึ่ง unit test จำนวนมากไม่ทำ
หา dead code ใน repository ที่ AI เขียนอย่างไร? การสร้างโค้ดเพิ่มไฟล์และ abstraction การลบมักต้อง prompt แยก vm0 รัน Knip เป็น check ประจำเพื่อหาไฟล์, export, dependency และ entry point ที่ไม่ได้ใช้ TypeScript ยืนยันว่าโค้ดถูกต้อง; Knip ถามว่ายังมีส่วนร่วมในระบบหรือไม่ ซึ่งเป็นคำถามสำคัญสำหรับ dead code
โค้ดที่เขียนด้วย vibe coding ปลอดภัยพอจะรันใน production หรือไม่? ขึ้นกับสิ่งที่ตรวจสอบ ไม่ใช่ใครเป็นคนพิมพ์ สัญญาณที่เราต้องการไม่เปลี่ยน: type และ contract, test ผ่าน public boundary, โครงสร้างพื้นฐานจริง และ pipeline ที่ต้องเป็นสีเขียว ตัวเลขในบทความนี้อธิบายขนาด repository และความเร็วในการส่งมอบ ความพร้อมใช้งาน, อัตรา error และอัตรา change-failure คือสิ่งที่ตอบคำถาม production และเรายังรวบรวมอยู่
แปดเดือนผ่านไป
แปดเดือนยังเร็วเกินไปจะสรุปวิธีสุดท้าย โมเดล, เครื่องมือ agent และ repository ยังเปลี่ยนแปลง และกฎกับ workflow ของเราก็เปลี่ยนตาม
สิ่งหนึ่งที่ชัดเจนแล้ว เมื่อการสร้างโค้ดเร็วขึ้น สภาพแวดล้อม, constraint, test และระบบ feedback รับภาระคุณภาพมากขึ้น วิศวกรใช้เวลาพิมพ์ implementation น้อยลง และใช้เวลากำหนดพฤติกรรม, ออกแบบ boundary และปรับปรุง verification มากขึ้น
โค้ดเบส vm0 จะเติบโตต่อไป Knip ลบเศษซากที่กำหนดได้ กฎ static ขวาง pattern ความล้มเหลวที่เราเข้าใจแล้ว integration test ปกป้องพฤติกรรม module workflow ที่เกิดซ้ำจัดการการเสื่อมสภาพที่เรายังแสดงออกเชิงกลไม่ได้
โค้ดยังสำคัญ แนวปฏิบัติที่ดีที่สุดในบทความนี้ไม่ใช่การเขียนโค้ดให้น้อยลง แต่คือการตัดสินใจว่าอะไรต้องเป็นจริงก่อนการเปลี่ยนแปลงจะอยู่บน main branch เราใช้หลักฐานที่รันได้โดยเครื่องมากขึ้นในการตัดสินใจว่าเปลี่ยนแปลงควรอยู่บน main branch หรือ implementation ควรอยู่ใน repository หรือไม่





