บทที่ 7 จาก 12 · Agentic Engineering — คู่มือปฏิบัติเขียนซอฟต์แวร์ระดับ Production ด้วย AI Agent
จากศูนย์ถึง Production — เส้นทาง 9 ขั้น
ตัวอย่างจริงของการเพิ่มฟีเจอร์เข้าระบบที่รันอยู่แล้ว ตั้งแต่สเปกจนถึง rollout พร้อมระบุว่าขั้นไหนคนทำ ขั้นไหน agent ทำ วิธีเขียนสเปกที่ agent ทำงานต่อได้จริง และวิธีรีวิวเมื่อโค้ดไม่ได้เขียนโดยคน
ตัวอย่างจริง: เพิ่มระบบคูปองใน checkout service ที่รันอยู่แล้ว แต่ละขั้นระบุว่าใครทำ ใช้เวลาเท่าไร และผลลัพธ์ที่ต้องได้คืออะไร
| ขั้น | งาน | ใครทำ | ผลลัพธ์ที่ต้องได้ |
|---|---|---|---|
| 0 | วางพื้นฐาน (ทำครั้งเดียวต่อ repo) | คน | AGENTS.md, verify.sh, hooks, memory/, PR template |
| 1 | เขียน spec | คน + agent ช่วยถาม | docs/specs/coupon.md ที่ตอบคำถามกำกวมได้ครบ |
| 2 | ซอยงาน | คน | รายการ 5–8 งานย่อย แต่ละงานปิด loop ได้ใน 30 นาที |
| 3 | RED — เขียน test | agent | test แดงที่คนอ่านและอนุมัติแล้ว |
| 4 | GREEN — implement | agent (loop) | gate เขียวทั้งชุด พร้อม output จริง |
| 5 | รีวิว | คน | ตรวจ spec, คุณภาพ test, blast radius — ไม่ใช่ทุกบรรทัด |
| 6 | Merge | คน | CI เขียวบน main, ADR อัปเดต, pitfalls อัปเดต |
| 7 | Canary + วัด | ระบบ | 5% เป็นเวลา 24 ชม. guardrail ทุกตัวปกติ |
| 8 | Rollout เต็ม | คนอนุมัติ | 100% แล้วลบ flag ภายใน 2 สัปดาห์ |
7.1 ขั้นที่ 1 — spec ที่ agent ทำงานต่อได้จริง
สเปกที่ดีไม่ใช่สเปกที่ยาว แต่คือสเปกที่ ไม่เหลือคำถามที่ต้องเดา เทคนิคที่ได้ผลคือให้ agent อ่านร่างแรกแล้วถามคำถามที่มันจะต้องเดาถ้าไม่ถาม
อ่านร่างสเปกนี้ แล้วบอกฉัน 10 คำถามที่คุณจะต้องเดาคำตอบเองถ้าลงมือ
implement ตอนนี้ เรียงตามความเสี่ยงที่การเดาผิดจะทำให้ผู้ใช้เสียหาย
สำหรับแต่ละข้อ ให้เสนอคำตอบที่คุณคิดว่าน่าจะถูกด้วย ฉันจะได้แค่ยืนยันหรือแก้
โครงสร้างสเปกที่ใช้ได้:
# Spec: Coupon Discount (v1)
## Goal
ลูกค้าใส่รหัสคูปองตอน checkout แล้วได้ส่วนลดตามชนิดของคูปอง
## In scope
- คูปองแบบ % (1–100) และแบบจำนวนเงินคงที่
- จำกัดจำนวนครั้งต่อคูปองทั้งระบบ และต่อผู้ใช้
- วันหมดอายุ
## Out of scope (v1) — ระบุให้ชัดเพื่อกัน agent ทำเกิน
- ใช้คูปองหลายใบพร้อมกัน
- คูปองเฉพาะสินค้าหรือเฉพาะหมวด
- คูปองแบบสร้างอัตโนมัติต่อผู้ใช้
## กติกาธุรกิจ
R1 ส่วนลดคิดจาก subtotal (ก่อนภาษี ก่อนค่าส่ง)
R2 ภาษีคำนวณจากยอดหลังหักส่วนลด
R3 ค่าส่งไม่ถูกลด
R4 ยอดสุทธิต่ำสุดคือ 0 — ส่วนลดเกินยอด ให้ตัดที่ยอด ไม่ทอนเงิน
R5 ปัดเศษที่ 2 ตำแหน่ง แบบ ROUND_HALF_UP ตอนคำนวณส่วนลดเท่านั้น
R6 คูปองถูกนับใช้เมื่อ order ชำระเงินสำเร็จ ไม่ใช่ตอนใส่รหัส
## เคสที่ต้อง reject (พร้อม error code ที่ตายตัว)
| เงื่อนไข | HTTP | code |
|-----------------------|------|---------------------|
| ไม่มีรหัสนี้ | 404 | COUPON_NOT_FOUND |
| หมดอายุ | 422 | COUPON_EXPIRED |
| ใช้ครบโควตาระบบ | 422 | COUPON_EXHAUSTED |
| ผู้ใช้คนนี้ใช้ครบแล้ว | 422 | COUPON_USER_LIMIT |
| ยอดขั้นต่ำไม่ถึง | 422 | COUPON_MIN_NOT_MET |
| ใส่มาสองใบ | 422 | COUPON_MULTIPLE |
## Invariants (ใช้เขียน property test)
I1 net_total >= 0 เสมอ
I2 discount + net_total == subtotal เสมอ
I3 ทศนิยมทุกค่าไม่เกิน 2 ตำแหน่ง
I4 จำนวนครั้งที่ใช้จริง <= โควตา เสมอ แม้มี request พร้อมกัน
## ประเด็นที่ตัดสินใจแล้ว (กัน agent ถามซ้ำ)
- concurrency: ใช้ SELECT ... FOR UPDATE บนแถวคูปอง ไม่ใช้ optimistic lock
เหตุผล: จำนวนคูปองต่ำ contention ไม่สูง และเราต้องการความถูกต้องเด็ดขาด (I4)
- ไม่ cache สถานะคูปองใน Redis ใน v1 — ความถูกต้องสำคัญกว่า latency
## Definition of done
- ทุก R และ I มี test อย่างน้อยหนึ่งตัว โดยชื่อ test อ้างรหัสกติกา (test_r4_...)
- verify.sh เขียว
- มี feature flag `coupon_v1` ที่ปิดได้ทันที
7.2 ขั้นที่ 2 — ซอยงานให้ loop ปิดได้
งานย่อยที่ดีคือ มีเกณฑ์ผ่านชัด แตะไฟล์น้อย และไม่ขึ้นกับงานที่ยังไม่ทำ
| งาน | สิ่งที่ทำ | test |
|---|---|---|
| T1 | domain model + validation ล้วน ไม่มี DB | test_coupon_domain.py |
| T2 | repository + migration (expand phase) | test_coupon_repo.py |
| T3 | การคิดส่วนลดกับภาษี R1–R5 | test_calculation.py |
| T4 | concurrency + โควตา I4 | test_concurrency.py |
| T5 | HTTP endpoint + error mapping | test_api_coupon.py |
| T6 | feature flag + shadow mode | test_rollout.py |
| T7 | observability: metric, log, alert | ตรวจด้วยตาใน staging |
ลำดับสำคัญคือ T1 ก่อนเสมอ เพราะมันคือที่ที่ความกำกวมของสเปกจะโผล่ ถ้า T1 ยาก แปลว่าสเปกยังไม่ชัดพอ ให้กลับไปแก้สเปก อย่าดันต่อ
7.3 ขั้นที่ 5 — รีวิวอย่างไรเมื่อโค้ดไม่ได้เขียนโดยคน
การอ่านทีละบรรทัดไม่ scale เมื่อ agent ผลิตโค้ดได้เร็วกว่าคนอ่าน 10 เท่า ต้องเปลี่ยนจุดโฟกัสของการรีวิว
| เดิม (โค้ดคนเขียน) | ใหม่ (โค้ด agent เขียน) |
|---|---|
| อ่านทุกบรรทัดหา bug | อ่าน test ทุกบรรทัด เพราะ implementation จะวิ่งเข้าหา test เสมอ |
| ตรวจ style และ naming | ปล่อยให้ linter ทำ ถ้าต้องคอมเมนต์เรื่อง style แปลว่า config ยังไม่ครบ |
| ถามว่าทำไมเขียนแบบนี้ | ถามว่า spec ตรงกับที่ธุรกิจต้องการไหม เพราะ bug ส่วนใหญ่ย้ายไปอยู่ที่สเปกแล้ว |
| ดู diff อย่างเดียว | ดู diff + ไฟล์ที่ไม่ควรถูกแตะแต่ถูกแตะ + dependency ใหม่ |
| เชื่อว่าผู้เขียนเข้าใจระบบ | ตรวจว่า agent ไม่ได้สร้าง pattern คู่ขนานกับของที่มีอยู่แล้ว |
คำถาม 7 ข้อที่ต้องตอบให้ได้ก่อนกด approve:
- test พวกนี้จะแดงไหม ถ้าฉันเอา requirement หลักออกจาก implementation (ถ้าไม่แดง = test ไร้ค่า)
- มีเคส reject ครบตามสเปกไหม หรือทดสอบแต่ happy path
- ไฟล์ที่ถูกแตะ อยู่ในขอบเขตของงานนี้ทั้งหมดไหม
- มี dependency ใหม่ไหม จำเป็นจริงไหม ใครดูแล
- มี migration ไหม ย้อนกลับได้ไหม ล็อกตารางนานแค่ไหน
- ฟีเจอร์นี้ปิดได้ทันทีไหมถ้าพัง
- blast radius และแผน rollback ในคำอธิบาย PR สมจริงไหม
เทคนิค mutation test แบบเร็ว
ถ้าไม่แน่ใจว่า test ชุดนี้มีคุณภาพจริงไหม ให้แก้ implementation ให้ผิดโดยเจตนาหนึ่งจุด เช่นเปลี่ยน
>=เป็น>แล้วรัน test ถ้าไม่มีตัวไหนแดง แปลว่า test ชุดนั้นไม่คุ้มค่าดูแล สั่ง agent ทำสิ่งนี้อัตโนมัติได้: “สร้าง mutant 10 ตัวจาก diff นี้ รัน test แล้วรายงานว่า mutant ไหนรอด”
สรุปบทนี้
เส้นทางจากสเปกถึง production มี 9 ขั้น และคนถือขั้นที่ 0, 1, 2, 5, 6 กับการอนุมัติขั้นที่ 8 สเปกที่ดีคือสเปกที่ไม่เหลือคำถามให้เดา ซอยงานให้ปิด loop ได้ใน 30 นาที และเปลี่ยนการรีวิวจากการอ่านโค้ดทีละบรรทัด มาเป็นการอ่าน test กับการตรวจขอบเขต
บทถัดไปว่าด้วย MCP ว่าควรใช้เมื่อไหร่ ออกแบบอย่างไร และช่องโหว่ prompt injection ที่มากับ tool output