ใช้งานใน Production
จากทดลองสู่การใช้งานจริง — hooks, permissions, security
บทที่ 5 | ทำให้ Claude Code จบงานระดับ Production กลุ่มเป้าหมาย: Developer ทุกระดับที่ต้องการสร้าง Production-grade Software ด้วย Claude Code
การแก้ปัญหา Loop แบบลึก — Root Cause Analysis
บทที่ 4 สอน Protocol หยุด Loop บทนี้ไปลึกกว่านั้น โดยสอนวิธีวิเคราะห์ต้นเหตุ (Root Cause) อย่างเป็นระบบ และออกแบบโค้ดล่วงหน้าให้เกิด Loop น้อยที่สุด
Framework 5 Whys สำหรับ Loop Debugging
เทคนิค “5 Whys” จาก Toyota Production System ใช้ได้ผลดีมากกับการ debug ด้วย Claude ไม่จำเป็นต้องถาม “ทำไม” ครบ 5 ครั้งทุกครั้ง แค่ถามจนถึง root cause จริงๆ
ตัวอย่าง: แก้บักด้วย 5 Whys จริง
# สถานการณ์: Payment ล้มเหลวบ้างประปราย
> Why 1: ทำไม payment ถึง fail?
→ Stripe API return error "card_declined"
> Why 2: ทำไม card ถึงถูก decline?
→ Stripe บอกว่า insufficient_funds แต่ user บอกว่าเงินพอ
> Why 3: ทำไม Stripe ถึงเห็น insufficient_funds ทั้งที่เงินพอ?
→ ดู request payload พบว่า amount ที่ส่งไปเป็น 1000
แต่ควรเป็น 10.00 (หน่วยต่างกัน: บาท vs สตางค์)
> Why 4: ทำไม amount ถึงส่งผิดหน่วย?
→ Code ใน checkout.ts ไม่ได้ convert จาก บาท → สตางค์
ก่อนส่งให้ Stripe (Stripe ใช้หน่วยที่เล็กที่สุดของสกุลเงิน)
> Why 5: ทำไม conversion ถึงขาดไป?
→ Developer อ่าน doc ผิด คิดว่า Stripe ใช้หน่วยหลัก
และไม่มี unit test ที่ verify ค่า amount
# Root Cause: ขาด unit test + documentation ไม่ชัด
# Fix จริง: แก้ conversion + เพิ่ม test + เพิ่ม comment ใน CLAUDE.md
Prompt ให้ Claude ทำ 5 Whys
> ทำ 5 Whys Analysis สำหรับบักนี้:
[อธิบายอาการบัก]
ทำแบบนี้:
1. Why 1: อธิบาย symptom ที่เห็น
2. Why 2-5: ถามต่อเรื่อยๆ จาก answer ก่อนหน้า
3. หยุดเมื่อถึง root cause จริง (ไม่ต้องครบ 5 ถ้าเจอก่อน)
4. แยกระหว่าง "Fix Symptom" กับ "Fix Root Cause"
5. เสนอ solution สำหรับ root cause
6. เสนอวิธีป้องกันไม่ให้เกิดซ้ำ (systematic fix)
Debugging Playbook — คู่มือแก้บักแต่ละประเภท
แทนที่จะ describe ปัญหากว้างๆ ให้ระบุประเภทบักก่อน แล้วใช้ playbook ที่เหมาะสม:
| Playbook A: Logic Bug — ผลลัพธ์ผิด แต่ไม่มี Error |
|---|
ยากที่สุดในการ debug เพราะไม่มี error message ให้ follow Claude มักวน loop กับบักประเภทนี้
> Logic Bug Analysis:
สิ่งที่เห็น: [input] → [actual output]
สิ่งที่ต้องการ: [input] → [expected output]
ให้ทำดังนี้:
1. Trace execution path: อธิบายทีละ step ว่า code ทำอะไร
ตั้งแต่รับ input จนถึงส่ง output
2. หา divergence point: ตรงไหนที่ actual กับ expected เริ่มต่างกัน?
3. เพิ่ม assertion checks ตลอดทาง:
console.assert(value === expected, `Step N: got ${value}, want ${expected}`)
4. รัน และดูว่า assertion ล้มเหลวที่ step ไหน
อย่าแก้โค้ดจนกว่าจะ identify divergence point ได้
| Playbook B: Performance Bug — ช้าผิดปกติ |
|---|
> Performance Investigation:
อาการ: [ส่วนไหนช้า] ช้าประมาณ [X วินาที/ms]
Target: ต้องเร็วกว่า [Y วินาที/ms]
ทำตามลำดับนี้:
Phase 1 — Measure ก่อนแก้:
เพิ่ม timing ใน @[file]:
- console.time("total")
- console.time("db-query")
- console.time("data-transform")
- console.time("response")
แล้วรัน 3 ครั้ง บันทึกค่าเฉลี่ย
Phase 2 — Identify bottleneck:
step ไหนใช้เวลามากที่สุด?
Phase 3 — Optimize เฉพาะ bottleneck:
อย่า optimize ส่วนที่เร็วอยู่แล้ว
Phase 4 — Measure อีกครั้ง:
เปรียบเทียบก่อน/หลัง ต้อง [Y ms] ตาม target
| Playbook C: Memory Leak — RAM ค่อยๆ เพิ่มจนระบบช้า |
|---|
> Memory Leak Investigation:
อาการ: memory เพิ่มขึ้นเรื่อยๆ เมื่อใช้งานนาน
Step 1 — Confirm มี leak จริง:
เพิ่ม memory monitoring:
setInterval(() => {
const mem = process.memoryUsage();
console.log(`RSS: ${Math.round(mem.rss/1024/1024)}MB`,
`Heap: ${Math.round(mem.heapUsed/1024/1024)}MB`);
}, 5000);
Step 2 — หา pattern:
memory เพิ่มหลัง action ไหน?
ทุก request? หลัง upload? หลัง WebSocket connect?
Step 3 — วิเคราะห์ @[file] หา patterns เหล่านี้:
- Event listeners ที่ไม่ได้ removeEventListener
- setTimeout/setInterval ที่ไม่ได้ clearTimeout/clearInterval
- Global variables ที่เก็บ array/object ไว้ตลอด
- Cache ที่ไม่มี eviction policy
- Closure ที่ capture object ใหญ่
Step 4 — Fix และ verify ว่า memory คงที่หลังรัน 30 นาที
| Playbook D: Concurrency Bug — ผิดเฉพาะ high traffic |
|---|
> Concurrency Analysis:
อาการ: ปกติดี แต่พอมี concurrent users จะมีปัญหา
Step 1 — Reproduce locally:
สร้าง load test script ที่ส่ง concurrent requests:
for (let i = 0; i < 50; i++) {
promises.push(fetch("/api/[endpoint]"));
}
await Promise.all(promises);
Step 2 — วิเคราะห์ shared state:
หา variables ใน @[file] ที่:
- เป็น module-level (ไม่ใช่ request-level)
- ถูก read และ write ใน handler เดียวกัน
- ไม่มี locking/synchronization
Step 3 — เสนอ fix:
- Database-level locking: SELECT FOR UPDATE
- Optimistic locking: version field + retry
- Queue: serialize concurrent operations
- Idempotency key: ป้องกัน duplicate processing
อธิบาย tradeoffs ของแต่ละ option ก่อนเลือก
Anti-Loop Patterns — ออกแบบให้ Loop เกิดน้อยลง
วิธีที่ดีที่สุดในการหลีกเลี่ยง Loop คือออกแบบโค้ดและ prompt ให้ดีตั้งแต่ต้น:
Pattern 1: Contract-First Development
กำหนด Interface และ Contract ชัดเจนก่อน implement ทำให้ Claude มี specification ที่ชัดเจน ไม่ต้องเดา:
# แทนที่จะบอก "สร้าง payment service"
# ให้เขียน contract ก่อน แล้วให้ Claude implement
> นี่คือ Contract ที่ต้องการ:
// Input
interface ProcessPaymentInput {
userId: string;
amount: number; // หน่วย: บาท (เช่น 99.50)
currency: "THB" | "USD";
description: string;
idempotencyKey: string; // ป้องกัน duplicate charge
}
// Success Output
interface ProcessPaymentSuccess {
success: true;
transactionId: string;
amount: number; // ยืนยัน amount ที่ charge จริง
timestamp: Date;
}
// Error Output
interface ProcessPaymentError {
success: false;
errorCode: "CARD_DECLINED" | "INSUFFICIENT_FUNDS" |
"INVALID_CARD" | "PROCESSING_ERROR";
message: string; // สำหรับ log (ไม่แสดง user)
userMessage: string; // สำหรับแสดง user (ภาษาไทย)
retryable: boolean; // บอก client ว่า retry ได้ไหม
}
implement processPayment(input: ProcessPaymentInput)
: Promise<ProcessPaymentSuccess | ProcessPaymentError>
ใช้ Stripe เป็น payment processor
convert amount จาก บาท → สตางค์ก่อนส่ง Stripe
Pattern 2: Error Budget Strategy
กำหนดล่วงหน้าว่า error ประเภทไหน Claude ควร retry เองได้ และประเภทไหนต้องหยุดแล้วถาม:
# เพิ่มใน CLAUDE.md
## 🔄 Error Handling Strategy
### Claude retry เองได้ (ไม่ต้องถาม):
- TypeScript type error → แก้ type ให้ถูกต้อง
- Import path ผิด → แก้ path
- Missing await → เพิ่ม await
- Unused variable → ลบออก
### Claude ต้องถามก่อน retry:
- Logic error (ผลลัพธ์ไม่ตรงที่คาด) → อธิบายความเข้าใจก่อน
- Database schema mismatch → แสดง schema ที่เห็น แล้วถาม
- External API error → แสดง error code + ถาม intent
### Claude ต้องหยุดและรายงาน:
- Security-related issue → อย่าแก้เองเด็ดขาด
- แก้เกิน 2 รอบแล้วยังไม่ได้ → สรุปสิ่งที่ลอง + ขอข้อมูลเพิ่ม
- ต้องแก้ไฟล์นอก scope → ขออนุมัติก่อน
Pattern 3: Checkpoint System
แบ่งงานเป็น checkpoint เล็กๆ และ verify แต่ละ checkpoint ก่อนไปต่อ ป้องกัน snowball effect ที่บักสะสม:
> สร้าง User Registration System ด้วย Checkpoint:
Checkpoint 1: Database schema
- สร้าง Prisma schema สำหรับ User
- รัน migration
- ✓ verify: npx prisma db pull แล้วดู schema
STOP → รอ approve ก่อนไป Checkpoint 2
Checkpoint 2: Validation layer
- สร้าง Zod schema สำหรับ registration input
- เขียน unit test ครอบคลุม valid/invalid cases
- ✓ verify: npm test ต้องผ่านทั้งหมด
STOP → รอ approve ก่อนไป Checkpoint 3
Checkpoint 3: Service layer
- สร้าง registerUser() function
- hash password, check duplicate email
- ✓ verify: integration test กับ test database
STOP → รอ approve ก่อนไป Checkpoint 4
Checkpoint 4: API Route
- สร้าง POST /api/auth/register
- ✓ verify: curl test ทุก case
ถ้า checkpoint ไหนล้มเหลว → หยุด ไม่ไป checkpoint ถัดไป
การใช้ Claude Debug Claude
เทคนิคที่ทรงพลังมาก: ให้ Claude ตัวใหม่วิเคราะห์ปัญหาโดยไม่รู้ว่า Claude ตัวไหนเขียนโค้ดนั้น
# Session 1: Claude A สร้าง code และเจอบัก
# Claude A แก้ไม่ได้ใน 2 รอบ
# เปิด Session ใหม่ (Claude B)
> [Fresh session - ไม่มี context เดิม]
ผมได้รับ code นี้มาและมันมีบัก:
[วาง code ทั้งหมด]
อาการ: [อธิบาย]
Error: [copy error]
วิเคราะห์ code นี้จากมุมมองภายนอก:
1. สิ่งที่ code นี้พยายามทำคืออะไร?
2. เห็น design issue อะไรไหม?
3. บัคน่าจะอยู่ที่ไหน?
4. ถ้าคุณเขียน code นี้ใหม่จาก scratch
จะเปลี่ยน approach อะไร?
# Claude B มักเห็นปัญหาที่ Claude A มองข้ามไป
# เพราะไม่มี cognitive bias จากการเขียนโค้ดนั้นเอง
💡 เมื่อไหร่ควรใช้ Claude Debug Claude
ใช้เมื่อ: Claude วน loop แก้ไม่ได้เกิน 2 รอบ
ใช้เมื่อ: โค้ดซับซ้อนมากจนอธิบายยาก
ใช้เมื่อ: ต้องการ second opinion ก่อน merge
ไม่ต้องใช้: บักง่ายๆ หรือแก้ได้ในรอบแรก
Production Quality — ตั้งแต่เริ่มต้นจนถึง Live
Production code ไม่ได้หมายความแค่ “รันได้” แต่หมายถึงโค้ดที่ reliable, maintainable, observable และ recoverable ส่วนนี้จะไล่ทุกขั้นตอนตั้งแต่ design จนถึงผู้ใช้งานจริง
ขั้นตอนที่ 1 — Requirements & Design
งานส่วนใหญ่ล้มเหลวที่ขั้นตอนนี้ ไม่ใช่ที่ implementation ให้ Claude ช่วย clarify requirements ก่อนเสมอ
# Prompt สำหรับ clarify requirements
> ก่อน implement ขอ clarify requirements:
Feature ที่ต้องการ: [อธิบาย]
ช่วยถามคำถามที่จำเป็นก่อน implement:
1. Happy path: ขั้นตอนปกติเป็นอย่างไร?
2. Edge cases: กรณีพิเศษที่ต้อง handle?
3. Error cases: เกิดอะไรขึ้นถ้า X ล้มเหลว?
4. Performance: ต้องรองรับ concurrent users กี่คน?
5. Security: ใครเข้าถึงได้? Data sensitivity ระดับไหน?
6. Rollback: ถ้า feature นี้พัง rollback ยังไง?
หลังจาก clarify แล้ว สรุป acceptance criteria
ที่ใช้ verify ว่า feature เสร็จและถูกต้อง
Design Document ที่ Claude ช่วยสร้าง
> สร้าง Design Document สำหรับ [Feature] ประกอบด้วย:
## Summary
อธิบาย feature ใน 2-3 ประโยค
## Architecture
- Data flow diagram (text format)
- Components ที่เกี่ยวข้อง
- External dependencies
## API Contract
- Endpoints ใหม่ (input/output types)
- Error responses
## Database Changes
- Tables/columns ที่เพิ่ม/แก้
- Migration strategy
- Rollback plan
## Testing Strategy
- Unit tests ที่ต้องเขียน
- Integration tests
- E2E scenarios
## Risks & Mitigations
- ความเสี่ยงที่เห็น
- วิธีลด risk
## Acceptance Criteria
✓ [criterion 1]
✓ [criterion 2]
ขั้นตอนที่ 2 — Implementation with Quality Gates
แต่ละ phase ของ implementation มี Quality Gate ที่ต้องผ่านก่อนไปต่อ Claude ช่วย verify ได้ทุก gate
| Phase | งานที่ทำ | Quality Gate | Claude ช่วยได้ |
|---|---|---|---|
| Data Layer | Schema, migrations, indexes | Migration รันได้, rollback ได้ | เขียน migration + test |
| Business Logic | Core functions, validation | Unit tests ผ่าน 100% | เขียน tests + implement |
| API Layer | Routes, middleware, auth | Integration tests ผ่าน | เขียน tests + routes |
| UI Layer | Components, forms, states | E2E tests ผ่าน key flows | เขียน Playwright tests |
| Performance | Query optimization, caching | Load test ผ่าน target | วิเคราะห์ bottleneck |
| Security | Auth check, input validation | Security checklist ผ่าน | รัน OWASP checklist |
ขั้นตอนที่ 3 — Testing Strategy แบบสมบูรณ์
Testing คือส่วนที่ Vibe Coder มักข้ามมากที่สุด แต่เป็นส่วนที่ทำให้ production code แตกต่างจาก prototype
| Unit Testing ด้วย Vitest + Claude |
|---|
# ให้ Claude เขียน comprehensive unit tests
> เขียน unit tests สำหรับ @src/lib/pricing.ts
ครอบคลุม ALL cases:
1. Happy paths (input ถูกต้อง → output ที่คาด)
2. Edge cases:
- จำนวนที่ boundary: 0, negative, MAX_SAFE_INTEGER
- null/undefined inputs
- empty arrays/objects
3. Error cases:
- invalid input type
- business rule violations
4. Precision cases:
- floating point (0.1 + 0.2 ≠ 0.3)
- currency rounding
ใช้ Vitest, organize เป็น describe blocks
ชื่อ test ต้อง readable: "should return X when Y"
# ตัวอย่าง output ที่ดี
describe("calculateDiscount", () => {
describe("percentage discount", () => {
it("should apply 10% discount to 1000", () => {
expect(calculateDiscount(1000, { type: "percent", value: 10 }))
.toBe(900);
});
it("should handle 0% discount", () => {...});
it("should cap at 100% discount", () => {...});
});
describe("fixed discount", () => {...});
describe("edge cases", () => {...});
});
| Integration Testing ด้วย Supertest |
|---|
> เขียน integration tests สำหรับ POST /api/orders
ใช้ Supertest + test database
Test cases:
1. ✅ Success: create order ถูกต้อง → 201 + order object
2. ❌ Unauthorized: ไม่มี token → 401
3. ❌ Validation: ข้อมูลไม่ครบ → 400 + error details
4. ❌ Business Rule: สินค้าหมด stock → 422 + user message
5. ⚡ Idempotency: ส่ง request เดิม 2 ครั้ง → สร้าง order เดียว
6. 🔒 Authorization: user ดู order ของคนอื่น → 403
Setup:
- เริ่ม test database ที่สะอาดก่อนแต่ละ test
- ล้าง database หลังแต่ละ test
- mock Stripe API (ไม่ call real API ใน test)
# ตัวอย่าง
describe("POST /api/orders", () => {
beforeEach(async () => {
await db.cleanDatabase();
await db.seed.basicProducts();
});
it("should create order and return 201", async () => {
const res = await request(app)
.post("/api/orders")
.set("Authorization", `Bearer ${testToken}`)
.send({ items: [{ productId: "p1", quantity: 2 }] });
expect(res.status).toBe(201);
expect(res.body.id).toBeDefined();
expect(res.body.total).toBe(200);
});
});
| E2E Testing ด้วย Playwright |
|---|
> เขียน E2E tests สำหรับ checkout flow
ใช้ Playwright + staging environment
Critical flows ที่ต้อง test:
1. Happy path: เลือกสินค้า → checkout → payment → confirmation
2. Failed payment: กรอก card ผิด → error message → retry
3. Out of stock: สินค้าหมดระหว่าง checkout → error + redirect
4. Session timeout: ค้างนาน → session หมด → redirect to login
# ตัวอย่าง Playwright test
test("complete checkout flow", async ({ page }) => {
await page.goto("/products");
// เลือกสินค้า
await page.click("[data-testid=product-1]");
await page.click("[data-testid=add-to-cart]");
// ไป checkout
await page.click("[data-testid=cart-icon]");
await page.click("[data-testid=checkout-btn]");
// กรอก payment
await page.fill("[data-testid=card-number]", "4242424242424242");
await page.fill("[data-testid=card-expiry]", "12/25");
await page.fill("[data-testid=card-cvc]", "123");
await page.click("[data-testid=pay-btn]");
// verify confirmation
await expect(page.locator("[data-testid=order-confirmed]"))
.toBeVisible({ timeout: 10000 });
});
ขั้นตอนที่ 4 — Security Checklist
Security เป็นสิ่งที่ Vibe Coder มักลืมมากที่สุด ใช้ checklist นี้ทุกครั้งก่อน deploy:
# ให้ Claude รัน Security Audit
> รัน Security Audit สำหรับ code ที่เพิ่งเขียน
ตรวจสอบ OWASP Top 10 ที่เกี่ยวข้อง:
□ Injection:
- SQL: ใช้ parameterized queries ทั้งหมด?
- NoSQL: sanitize MongoDB operators?
- Command: exec() หรือ shell command ใช้ input โดยตรงไหม?
□ Authentication:
- ทุก route ที่ sensitive มี auth check?
- JWT validation ถูกต้อง (verify signature, expiry)?
- Rate limiting บน login endpoint?
□ Authorization (IDOR):
- User ดูแก้ข้อมูลตัวเองเท่านั้น?
- ตรวจสอบ ownership ก่อน return data?
□ Sensitive Data:
- Password hashed ด้วย bcrypt (cost >= 10)?
- ไม่ return sensitive fields (password, token)?
- Log ไม่มี PII หรือ credentials?
□ Input Validation:
- Validate และ sanitize ทุก input?
- File upload: check type, size, scan content?
- URL parameters: validate format?
□ Error Handling:
- Error messages ไม่ leak internal info?
- Stack traces ไม่ถูกส่งไป client?
สำหรับแต่ละรายการ: ✅ ผ่าน / ❌ พบปัญหา (อธิบาย)
ขั้นตอนที่ 5 — Performance Testing และ Benchmarking
ก่อน deploy ต้องรู้ว่าโค้ดรองรับ load ได้มากแค่ไหน และ bottleneck อยู่ที่ไหน
| Load Testing ด้วย k6 |
|---|
# ให้ Claude เขียน k6 load test script
> เขียน k6 load test สำหรับ API ที่สำคัญ
Target: /api/products/search
Expected: p95 latency < 500ms ที่ 100 concurrent users
// k6 script ที่ Claude จะสร้าง
import http from "k6/http";
import { check, sleep } from "k6";
export const options = {
stages: [
{ duration: "30s", target: 10 }, // warm up
{ duration: "1m", target: 100 }, // ramp to target
{ duration: "3m", target: 100 }, // steady state
{ duration: "30s", target: 0 }, // ramp down
],
thresholds: {
http_req_duration: ["p(95)<500"], // 95% ต้อง < 500ms
http_req_failed: ["rate<0.01"], // error rate < 1%
},
};
export default function() {
const params = {
headers: { Authorization: `Bearer ${__ENV.TEST_TOKEN}` },
};
const searchTerms = ["shirt", "pants", "shoes", "hat"];
const term = searchTerms[Math.floor(Math.random() * searchTerms.length)];
const res = http.get(`${__ENV.BASE_URL}/api/products/search?q=${term}`, params);
check(res, {
"status 200": (r) => r.status === 200,
"has products": (r) => JSON.parse(r.body).products.length >= 0,
"response time OK": (r) => r.timings.duration < 500,
});
sleep(1);
}
# รัน: k6 run -e BASE_URL=https://staging.myapp.com \
# -e TEST_TOKEN=xxx script.js
| Database Query Performance ด้วย EXPLAIN ANALYZE |
|---|
> วิเคราะห์ performance ของ query นี้:
[แปะ Prisma query]
สร้าง raw SQL EXPLAIN ANALYZE version
เพื่อดู execution plan:
1. Seq Scan หรือ Index Scan?
2. estimated vs actual rows ต่างกันมากไหม?
3. สิ้นเปลือง memory ที่ step ไหน?
จากนั้นเสนอ:
- Index ที่ควรเพิ่ม
- Query rewrite ถ้าจำเป็น
- Caching strategy ถ้าเหมาะสม
# ตัวอย่าง EXPLAIN ANALYZE output ที่ Claude วิเคราะห์
EXPLAIN ANALYZE
SELECT u.*, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE u.created_at > NOW() - INTERVAL "30 days"
GROUP BY u.id;
# Claude จะเห็น Seq Scan บน users และเสนอ:
# CREATE INDEX idx_users_created_at ON users(created_at DESC);
ขั้นตอนที่ 6 — Observability: Logging, Metrics, Tracing
โค้ดที่ดีต้องสังเกตได้ เมื่อเกิดปัญหาใน production ต้องหาสาเหตุได้ภายในนาที ไม่ใช่ชั่วโมง
| Structured Logging |
|---|
> เพิ่ม structured logging ใน @src/lib/logger.ts
Requirements:
- ใช้ pino (เร็วกว่า winston 10x)
- Log format: JSON (เพื่อ parse ได้)
- Log levels: error, warn, info, debug
- ทุก log ต้องมี: timestamp, level, service,
requestId, userId (ถ้ามี), message, data
- ห้าม log: passwords, tokens, credit card
- Production: log ระดับ info ขึ้นไปเท่านั้น
# ตัวอย่าง logger ที่ดี
logger.info({
event: "payment.processed",
requestId: ctx.requestId,
userId: user.id,
orderId: order.id,
amount: order.total,
duration: timer.elapsed(),
// ไม่มี card number!
}, "Payment processed successfully");
> เพิ่ม request ID middleware ที่ inject requestId
ใน every request context เพื่อ trace across services
| Health Check Endpoint |
|---|
> สร้าง /api/health endpoint สำหรับ monitoring
ต้องตรวจสอบ:
□ Database: ping ได้? query ง่ายๆ รันได้?
□ Redis (ถ้าใช้): ping ได้?
□ External APIs: Stripe, SendGrid reachable?
□ Disk space: เหลือ > 20%?
□ Memory: heap usage < 80%?
Response format:
{
"status": "healthy" | "degraded" | "unhealthy",
"timestamp": "2025-01-15T10:00:00Z",
"version": "1.4.2",
"checks": {
"database": { "status": "ok", "latency": 5 },
"redis": { "status": "ok", "latency": 1 },
"stripe": { "status": "ok", "latency": 120 }
}
}
- 200: healthy หรือ degraded (บาง service ช้าแต่ยังทำงานได้)
- 503: unhealthy (core service ล้มเหลว)
- Timeout ของแต่ละ check: 2 วินาที
ขั้นตอนที่ 7 — Deployment Strategy
การ deploy ที่ดีต้องมีแผน rollback ชัดเจน และ minimize downtime
| Zero-downtime Deployment Checklist |
|---|
> สร้าง deployment checklist สำหรับ [feature]
โดย Claude ช่วยระบุ:
## Pre-deployment
□ Tests ทั้งหมดผ่าน (unit, integration, e2e)
□ Database migration reviewed (backward compatible?)
□ Environment variables ใหม่ set ใน production?
□ Feature flags configured?
□ Monitoring alerts set up?
## Migration Strategy
(สำคัญ: migration ต้อง backward compatible อย่างน้อย 1 version)
Step 1: Deploy migration (เพิ่ม column ใหม่, nullable)
Step 2: Deploy app (ใช้ column ใหม่)
Step 3: Backfill data เก่า (background job)
Step 4: Make column NOT NULL (next release)
## Deployment Steps
1. Deploy to staging → smoke test
2. Deploy to production (canary: 5% traffic)
3. Monitor 15 นาที (error rate, latency)
4. Expand to 50% → monitor
5. Full rollout → monitor 1 ชั่วโมง
## Rollback Plan
Trigger: error rate > 1% หรือ p95 > 2s
Time to rollback: < 5 นาที
Command: [ระบุคำสั่ง rollback จริง]
QA Testing — การทดสอบก่อน Production
QA (Quality Assurance) คือกระบวนการตรวจสอบว่า feature ทำงานถูกต้องในทุกสถานการณ์ Claude ช่วยออกแบบและรัน QA ได้อย่างเป็นระบบ
QA Test Plan ด้วย Claude
> สร้าง QA Test Plan สำหรับ [Feature Name]
ข้อมูล feature:
[อธิบาย feature ที่สร้าง]
สร้าง test plan ที่ครอบคลุม:
## Functional Testing
- Happy path scenarios (ทุก user journey)
- Negative scenarios (input ผิด, permission ไม่พอ)
- Boundary testing (min/max values)
## Browser/Device Compatibility
- Chrome, Firefox, Safari (latest)
- Mobile: iOS Safari, Android Chrome
- Responsive: 320px, 768px, 1280px, 1920px
## Accessibility Testing
- Keyboard navigation ครบ?
- Screen reader compatible?
- Color contrast WCAG 2.1 AA?
- Focus indicators visible?
## Data Integrity
- ข้อมูลถูก save ถูกต้อง?
- ข้อมูลแสดงถูกต้องหลัง refresh?
- Concurrent users: race condition?
## Format: Test Case Table
| ID | Scenario | Steps | Expected | Priority |
Automated QA Checklist สำหรับ Vibe Coding
ใช้ checklist นี้ทุกครั้งก่อน merge PR:
# .claude/commands/qa-check.md
# QA Pre-merge Checklist
# รัน automated checks ทั้งหมดและรายงานผล
1. รัน: npm run typecheck
✓ ต้องไม่มี TypeScript errors
2. รัน: npm run lint
✓ ต้องไม่มี ESLint errors (warnings OK)
3. รัน: npm test
✓ ต้องผ่าน 100% (0 failures)
✓ coverage ต้อง >= 70% สำหรับ new code
4. รัน: npm run build
✓ ต้อง build สำเร็จ ไม่มี errors
5. รัน Security check:
npm audit --audit-level=high
✓ ต้องไม่มี high/critical vulnerabilities
6. ตรวจสอบ git diff --stat
✓ ไม่มี .env ถูก commit
✓ ไม่มี console.log ที่ไม่จำเป็น
✓ ไม่มี TODO ที่ should not be in prod
7. ทดสอบ manual บน localhost:
- Happy path ที่สำคัญที่สุด
- Error case ที่น่าจะเกิดบ่อย
รายงานผลเป็น: ✅ ผ่าน / ❌ ล้มเหลว (พร้อมรายละเอียด)
Visual Regression Testing
ตรวจสอบว่า UI ไม่เปลี่ยนโดยไม่ตั้งใจ หลังจาก refactor หรือ upgrade dependency:
# ติดตั้ง Playwright screenshot testing
npm install @playwright/test
> เขียน visual regression tests สำหรับหน้าสำคัญ:
- Homepage
- Product listing
- Checkout flow (3 steps)
- User profile
// visual.test.ts
import { test, expect } from "@playwright/test";
test("homepage visual", async ({ page }) => {
await page.goto("/");
await page.waitForLoadState("networkidle");
await expect(page).toHaveScreenshot("homepage.png", {
maxDiffPixels: 100, // อนุญาต pixel ต่างได้ 100 pixels
});
});
# ครั้งแรก: รันเพื่อสร้าง baseline
npx playwright test visual --update-snapshots
# ครั้งต่อไป: เปรียบเทียบกับ baseline
npx playwright test visual
# ถ้า UI เปลี่ยน → test fail → ต้อง approve หรือ fix
A/B Testing และ Feature Flags
A/B Testing คือการทดสอบ feature สองแบบพร้อมกันกับผู้ใช้จริง เพื่อวัดว่าแบบไหนดีกว่า Feature Flags ช่วย control ว่าใครเห็น feature ไหน
Feature Flags — Control การ Release
Feature Flags ช่วยให้ deploy code ก่อน แล้วค่อย enable สำหรับผู้ใช้ทีหลัง ลด risk ของการ deploy
# ให้ Claude implement Feature Flag system
> สร้าง Feature Flag system สำหรับโปรเจค
Requirements:
- Store flags ใน database (เปลี่ยนได้ไม่ต้อง redeploy)
- Target: ทุก user / user ที่ระบุ / percentage
- Admin UI: เปิด/ปิด flag ได้ทันที
- Cache: 1 นาที (ไม่ hit DB ทุก request)
Schema:
model FeatureFlag {
id String @id @default(cuid())
key String @unique // "new-checkout"
enabled Boolean @default(false)
rolloutPct Int @default(0) // 0-100%
userIds String[] // specific users
description String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Usage ใน code:
if (await featureFlag.isEnabled("new-checkout", user.id)) {
// show new checkout
} else {
// show old checkout
}
A/B Test Framework
A/B Test วัดว่า variant ไหนให้ conversion rate ที่ดีกว่า
> สร้าง A/B Test framework ที่:
1. Assign user ไปยัง variant อย่าง consistent
(user เดิม → variant เดิมเสมอ)
2. Track events ที่สำคัญ (conversion goal)
3. Calculate statistical significance
4. Dashboard แสดงผลลัพธ์
ตัวอย่าง A/B Test: CTA Button
Variant A (control): "เพิ่มลงตะกร้า" (สีน้ำเงิน)
Variant B (treatment): "ซื้อเลย" (สีแดง)
Goal: Click rate
Duration: 2 สัปดาห์ หรือ n=1000/variant
// ab-test.ts
export async function getVariant(
testId: string,
userId: string
): Promise<"control" | "treatment"> {
// deterministic hash เพื่อให้ consistent
const hash = crypto
.createHash("sha256")
.update(`${testId}:${userId}`)
.digest("hex");
const num = parseInt(hash.substring(0, 8), 16);
return num % 2 === 0 ? "control" : "treatment";
}
export async function trackEvent(
testId: string,
userId: string,
event: "impression" | "conversion"
) {
const variant = await getVariant(testId, userId);
await db.abTestEvent.create({
data: { testId, userId, variant, event }
});
}
วิเคราะห์ผล A/B Test ด้วย Claude
# หลังเก็บ data ได้ 2 สัปดาห์
> วิเคราะห์ผล A/B Test "cta-button-color"
Data:
Control: 1,024 impressions, 87 conversions (8.5%)
Treatment: 1,031 impressions, 112 conversions (10.9%)
ช่วย:
1. คำนวณ statistical significance (p-value)
ใช้ Chi-square test หรือ Fisher exact test
2. คำนวณ confidence interval ของความต่าง
3. บอกว่าผลมี significance ไหม (p < 0.05)?
4. ถ้า significant: recommend ทำอะไร?
5. ถ้าไม่ significant: ต้องการ sample size เท่าไหร่?
Benchmark และ Profiling
Benchmark คือการวัดประสิทธิภาพอย่างเป็นระบบ Profiling คือการหาว่าโค้ดส่วนไหนช้า เป็นทักษะสำคัญที่ทำให้ production code ทำงานได้เร็วและประหยัด resource
Micro-benchmark ด้วย Vitest
> เขียน benchmark เพื่อเปรียบเทียบ algorithm A กับ B:
Algorithm A: ใช้ Array.filter().map()
Algorithm B: ใช้ single Array.reduce()
Test scenarios:
- Array ขนาด 100, 1,000, 10,000, 100,000 elements
- รันแต่ละ scenario 1000 ครั้ง
- รายงาน: mean, median, p99, memory usage
// benchmark.test.ts
import { bench, describe } from "vitest";
describe("search algorithms", () => {
const data = Array.from({ length: 10000 },
(_, i) => ({ id: i, value: Math.random() })
);
bench("filter + map", () => {
data.filter(x => x.value > 0.5).map(x => x.id);
});
bench("reduce", () => {
data.reduce((acc, x) => {
if (x.value > 0.5) acc.push(x.id);
return acc;
}, [] as number[]);
});
});
# รัน: npx vitest bench
Node.js Profiling ด้วย —prof
> ช่วยตั้งค่า profiling สำหรับ Node.js app
เพื่อหา CPU bottleneck
# รัน app พร้อม profiler
node --prof dist/server.js
# ส่ง traffic (load test)
k6 run profile-test.js
# หยุด app แล้ว process profile
node --prof-process isolate-*.log > profile.txt
# ให้ Claude วิเคราะห์
> นี่คือ Node.js CPU profile:
[วาง top functions จาก profile.txt]
วิเคราะห์:
1. Function ไหนใช้ CPU มากที่สุด?
2. เป็น bottleneck จริงหรือ false positive?
3. วิธี optimize ที่เหมาะสม?
# Alternative: ใช้ clinic.js (ง่ายกว่า)
npm install -g clinic
clinic doctor -- node dist/server.js
# เปิด browser อัตโนมัติ แสดง flame graph
Database Query Profiling
> สร้าง database profiling setup:
1. เปิด query logging ใน Prisma:
const prisma = new PrismaClient({
log: [
{ level: "query", emit: "event" },
{ level: "error", emit: "stdout" },
],
});
prisma.$on("query", (e) => {
if (e.duration > 100) { // log queries > 100ms
logger.warn({
query: e.query,
params: e.params,
duration: e.duration,
}, "Slow query detected");
}
});
2. เพิ่ม pg_stat_statements ใน PostgreSQL
เพื่อ track query statistics:
CREATE EXTENSION pg_stat_statements;
-- ดู slow queries
SELECT query, calls, mean_exec_time, total_exec_time
FROM pg_stat_statements
WHERE mean_exec_time > 100
ORDER BY total_exec_time DESC
LIMIT 10;
Error Handling และ Recovery Strategy
Production system ต้องรับมือกับ failure ได้อย่างสง่างาม ไม่ใช่แค่ “อย่าให้เกิด error” แต่ต้องรู้ว่าจะทำอะไรเมื่อเกิดขึ้น
Error Taxonomy — จัดประเภท Error อย่างถูกต้อง
| ประเภท Error | ตัวอย่าง | วิธี Handle | Retry? |
|---|---|---|---|
| Transient | Network timeout, DB connection blip | Retry with backoff | ได้ |
| Validation | Email format ผิด, field ขาด | Return 400 พร้อม detail | ไม่ได้ |
| Business Rule | Coupon หมดอายุ, สินค้าหมด stock | Return 422 พร้อม user message | ไม่ได้ |
| Auth | Token หมดอายุ, ไม่มีสิทธิ์ | Return 401/403 | บางครั้ง |
| Dependency | Stripe down, SendGrid fail | Fallback หรือ queue | ได้ |
| System | OOM, disk full, bug ใน code | Alert ทีม, log ละเอียด | บางครั้ง |
Retry Pattern ด้วย Exponential Backoff
> สร้าง retry utility ที่ใช้ได้ทั่วโปรเจค
// retry.ts
export async function withRetry<T>(
fn: () => Promise<T>,
options: {
maxAttempts?: number; // default: 3
initialDelay?: number; // default: 1000ms
maxDelay?: number; // default: 30000ms
backoffFactor?: number; // default: 2 (exponential)
retryIf?: (error: Error) => boolean;
onRetry?: (attempt: number, error: Error) => void;
} = {}
): Promise<T> {
const {
maxAttempts = 3,
initialDelay = 1000,
maxDelay = 30000,
backoffFactor = 2,
retryIf = () => true,
onRetry = () => {},
} = options;
let lastError: Error;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
if (attempt === maxAttempts || !retryIf(lastError)) {
throw lastError;
}
const delay = Math.min(
initialDelay * Math.pow(backoffFactor, attempt - 1),
maxDelay
);
const jitter = delay * 0.1 * Math.random(); // ±10% jitter
onRetry(attempt, lastError);
await new Promise(resolve => setTimeout(resolve, delay + jitter));
}
}
throw lastError!;
}
// Usage
const result = await withRetry(
() => stripe.paymentIntents.create(params),
{
maxAttempts: 3,
retryIf: (err) => err.message.includes("network"),
onRetry: (attempt, err) => logger.warn({ attempt, err }, "Retrying payment"),
}
);
Circuit Breaker Pattern
ป้องกันไม่ให้ระบบถาม external service ที่กำลัง fail ซ้ำๆ ช่วยให้ fail fast และ recover ได้เร็ว
> implement Circuit Breaker สำหรับ external API calls
States:
- CLOSED: ทำงานปกติ
- OPEN: หยุดส่ง request (fail fast)
- HALF_OPEN: ลอง request 1 อัน ถ้าสำเร็จ → CLOSED
Config:
- failureThreshold: 5 failures → OPEN
- successThreshold: 2 successes → CLOSED (from HALF_OPEN)
- timeout: 30 วินาที ก่อน HALF_OPEN
// ใช้ opossum library
npm install opossum
import CircuitBreaker from "opossum";
const stripeBreaker = new CircuitBreaker(
(params) => stripe.paymentIntents.create(params),
{
timeout: 5000, // 5s timeout per request
errorThresholdPercentage: 50, // 50% errors → OPEN
resetTimeout: 30000, // 30s ก่อน HALF_OPEN
volumeThreshold: 10, // ต้องมี 10+ requests ก่อน open
}
);
stripeBreaker.on("open", () => logger.error("Circuit OPEN: Stripe"));
stripeBreaker.on("halfOpen", () => logger.warn("Circuit HALF_OPEN: Testing Stripe"));
stripeBreaker.on("close", () => logger.info("Circuit CLOSED: Stripe recovered"));
// Usage (transparent to caller)
const intent = await stripeBreaker.fire(params);
Graceful Shutdown
เมื่อ server ต้อง restart ต้องทำให้ request ที่กำลังประมวลผลเสร็จก่อน ไม่ทิ้งงานกลางคัน
> implement Graceful Shutdown สำหรับ Express server
Requirements:
- รับ SIGTERM signal
- หยุดรับ request ใหม่
- รอ request ที่กำลังทำอยู่ให้เสร็จ (timeout: 30s)
- ปิด database connection
- Exit ด้วย code 0
// shutdown.ts
let isShuttingDown = false;
export function gracefulShutdown(server: Server) {
process.on("SIGTERM", async () => {
logger.info("SIGTERM received, starting graceful shutdown");
isShuttingDown = true;
server.close(async () => {
try {
await prisma.$disconnect();
await redis.quit();
logger.info("Graceful shutdown complete");
process.exit(0);
} catch (err) {
logger.error(err, "Error during shutdown");
process.exit(1);
}
});
// Force shutdown หลัง 30s
setTimeout(() => {
logger.error("Forced shutdown after timeout");
process.exit(1);
}, 30000);
});
}
// middleware: reject new requests during shutdown
app.use((req, res, next) => {
if (isShuttingDown) {
res.set("Connection", "close");
return res.status(503).json({ error: "Server shutting down" });
}
next();
});
Production Incident Management
เมื่อเกิดปัญหาใน production วิธีที่ตอบสนองมีผลอย่างมากต่อ downtime ที่เกิดขึ้น Claude ช่วยทั้งในการ debug และ communicate ได้
Incident Response Playbook
| 1 | Detect — รู้ว่ามีปัญหา Alert จาก monitoring ที่ตั้งไว้ หรือ user report มาก Claude Code ช่วย query log ได้ทันที |
|---|
# ตรวจสอบสถานะเร็ว
> ช่วย query log ของ 15 นาทีที่ผ่านมา:
รัน: grep "ERROR" /var/log/app/app.log | tail -50
หรือ query Cloudwatch:
aws logs filter-log-events --log-group-name /app/production
--start-time $(date -d "15 minutes ago" +%s000)
--filter-pattern "ERROR"
สรุปให้ว่า:
1. Error ประเภทไหนเกิดมากที่สุด?
2. เริ่มตั้งแต่เวลาไหน?
3. endpoint ไหนได้รับผลกระทบ?
| 2 | Triage — ประเมิน severity บอก Claude ข้อมูลที่มี Claude ช่วย assess impact และ priority |
|---|
> Incident Assessment:
ข้อมูลที่มี:
- Error rate: [X%] (ปกติ < 0.1%)
- Affected users: [N] คน
- Affected features: [list]
- Duration: [X minutes]
ประเมิน:
1. Severity: P1 (ระบบล่ม) / P2 (feature สำคัญพัง) / P3 (ผลกระทบน้อย)
2. ผู้ที่ต้องแจ้ง: team lead, CTO, users?
3. Timeline สำหรับ fix: ชั่วโมง / วัน
4. Quick mitigation ที่ทำได้ทันที?
| 3 | Mitigate — ลดผลกระทบก่อน ทำ workaround เพื่อลด downtime ก่อน แล้วค่อยหา root cause |
|---|
# วิธี mitigate ที่ทำได้เร็ว
> เราพบว่า payment feature พัง
ยังไม่รู้สาเหตุ
เสนอ mitigation options ที่ทำได้ใน 5 นาที:
- Feature flag: ปิด payment ชั่วคราว
- Maintenance mode: redirect ไปหน้าแจ้ง
- Rollback: ย้อนกลับ version ก่อนหน้า
- Scale up: เพิ่ม server ถ้า load issue
ระบุ command จริงสำหรับแต่ละ option
| 4 | Root Cause Analysis เมื่อ mitigate แล้ว ค่อยหา root cause อย่างเป็นระบบ |
|---|
| 5 | Post-mortem หลังแก้แล้ว ทำ post-mortem เพื่อป้องกันไม่ให้เกิดซ้ำ |
|---|
> เขียน Post-mortem document สำหรับ incident นี้:
ข้อมูล:
- เริ่ม: [เวลา]
- แก้ได้: [เวลา]
- Duration: [X minutes]
- Impact: [N users, N transactions affected]
Template:
## Timeline
[เวลา] อะไรเกิดขึ้น
## Root Cause
อธิบาย root cause จริงๆ (ไม่ใช่ symptom)
## Contributing Factors
สิ่งที่ทำให้ปัญหาแย่ลงหรือแก้ช้า
## What Went Well
สิ่งที่ทำได้ดีในการ respond
## Action Items
| Action | Owner | Due Date | Priority |
|--------|-------|----------|----------|
| เพิ่ม test ที่ catch bug นี้ | @john | 2025-01-20 | P1 |
| เพิ่ม alert สำหรับ error pattern นี้ | @jane | 2025-01-18 | P1 |
## Prevention
จะป้องกันไม่ให้เกิดซ้ำด้วยวิธีไหน?
สรุป: Production-Ready Checklist
รวม checklist ทั้งหมดไว้ใช้ก่อน deploy production จริง:
# .claude/commands/prod-ready.md
# Production Readiness Checklist
## Code Quality
□ TypeScript strict mode: ไม่มี any
□ Tests: unit + integration + e2e ผ่าน
□ Test coverage: >= 70% สำหรับ new code
□ Linting: ไม่มี errors
□ Build: สำเร็จ ไม่มี warnings สำคัญ
## Security
□ Authentication: ทุก protected route มี auth check
□ Authorization: ตรวจสอบ ownership ก่อน return data
□ Input validation: ทุก input ผ่าน validation
□ SQL: ไม่มี raw string concatenation
□ Secrets: ไม่มี hardcoded credentials
□ Dependencies: npm audit ไม่มี high/critical
## Performance
□ Database: indexes ครบสำหรับ queries ที่ใช้บ่อย
□ N+1: ไม่มี N+1 query ใน critical paths
□ Load test: ผ่าน target performance
□ Caching: ใช้ cache สำหรับ expensive operations
## Observability
□ Logging: structured logs ครบ
□ Error tracking: Sentry configured
□ Health check: /api/health ทำงาน
□ Alerts: monitoring alerts set up
## Reliability
□ Error handling: ทุก async มี try/catch
□ Retry logic: transient errors มี retry
□ Graceful shutdown: รองรับ SIGTERM
□ Circuit breaker: external APIs มี protection
## Deployment
□ Migration: backward compatible
□ Rollback: plan ชัดเจน ทดสอบแล้ว
□ Feature flags: configured
□ Runbook: อัปเดตแล้ว
□ ทุกข้อ: ✅ พร้อม deploy | ❌ ต้องแก้ก่อน
คำศัพท์ประจำบท
| 5 Whys | เทคนิคหา Root Cause โดยถาม “ทำไม” ซ้ำๆ จนถึงต้นเหตุจริง |
|---|---|
| Root Cause | สาเหตุที่แท้จริงของปัญหา ไม่ใช่ symptom ที่เห็น |
| Contract-First | กำหนด Interface/API contract ก่อน implement เพื่อลด ambiguity |
| Circuit Breaker | Pattern ที่หยุดส่ง request ไป service ที่กำลัง fail เพื่อ fail fast |
| Exponential Backoff | วิธี retry โดยรอนานขึ้นทุกครั้ง เพื่อไม่กด service ที่กำลัง recover |
| Graceful Shutdown | การ shutdown ที่รอ request ปัจจุบันเสร็จก่อน ไม่ทิ้งงานกลางคัน |
| Feature Flag | Switch ที่เปิด/ปิด feature ได้โดยไม่ต้อง redeploy |
| A/B Test | การทดสอบ 2 variants พร้อมกันกับ users จริง เพื่อวัดว่าแบบไหนดีกว่า |
| Statistical Significance | ระดับความมั่นใจทางสถิติว่าผลต่างไม่ได้เกิดจากความบังเอิญ |
| p-value | ค่าที่บอกว่าผลที่เห็นเกิดจากความบังเอิญน้อยแค่ไหน (< 0.05 = significant) |
| Benchmark | การวัดประสิทธิภาพอย่างเป็นระบบเพื่อเปรียบเทียบ |
| Profiling | การหาว่าโค้ดส่วนไหนใช้ CPU/Memory มากที่สุด |
| Flame Graph | กราฟแสดง call stack ที่ใช้ CPU จนหา bottleneck ได้ |
| EXPLAIN ANALYZE | คำสั่ง SQL ที่แสดง execution plan จริงของ query |
| Load Test | การทดสอบระบบภายใต้ load จำนวนมาก เพื่อหา breaking point |
| Canary Deployment | การ deploy ไปยัง % เล็กๆ ของ users ก่อน แล้วค่อยขยาย |
| Post-mortem | การวิเคราะห์หลัง incident เพื่อหาสาเหตุและป้องกันซ้ำ |
| MTTR | Mean Time To Recovery — เวลาเฉลี่ยในการแก้ incident |
| Error Budget | ปริมาณ error/downtime ที่ยอมรับได้ใน SLA (เช่น 0.1%/เดือน) |
บทที่ 5 | ทำให้ Claude Code จบงานได้ระดับ Production กลุ่มเป้าหมาย: นักพัฒนาทุกระดับที่ต้องการนำ code ขึ้น production อย่างมั่นใจ
จาก Vibe Coding ถึง Production — ช่องว่างที่ต้องข้าม
บทที่ 4 สอนให้สร้างของได้เร็ว แต่ “เร็ว” ไม่ใช่ “พร้อม” เสมอไป Production code ต้องผ่านกระบวนการที่ครบถ้วนก่อนให้ผู้ใช้จริงสัมผัส บทนี้จะพา walk through ตั้งแต่โค้ดที่เพิ่ง Vibe จนถึงระบบที่ deploy จริงและวัดผลได้
The Production Gap — สิ่งที่ Vibe Coding ยังขาด
| Vibe Coding ได้ | Production ต้องการเพิ่ม |
|---|---|
| Function ทำงานได้ | Handle ทุก edge case ที่เป็นไปได้ |
| Happy path ผ่าน | Error path ที่ graceful และ informative |
| โค้ดอ่านได้ | โค้ดที่ maintainable โดยคนอื่น 6 เดือนต่อมา |
| รันได้ใน local | รันได้ทุก environment อย่าง consistent |
| ทดสอบด้วยมือ | Automated tests ที่รันทุก commit |
| Deploy ครั้งแรกได้ | Deploy ซ้ำได้อย่าง reliable ไม่ว่ากี่ครั้ง |
| ไม่รู้ว่าช้าหรือเร็ว | Benchmark และ Performance budget ชัดเจน |
| ไม่รู้ว่า User ชอบไหม | A/B test วัดผลด้วย data จริง |
Production Pipeline ทั้งหมด
นี่คือ pipeline ที่ทุก feature ต้องผ่านก่อนถึงมือ user จริง Claude Code ช่วยได้ในทุกขั้น:
| ขั้นที่ | Phase | งานหลัก | Claude ช่วยอะไร |
|---|---|---|---|
| 1 | Code Quality | Lint, Format, Type check | Auto-fix + review |
| 2 | Unit Testing | Test ทุก function ทุก edge case | เขียน + รัน test |
| 3 | Integration Testing | Test ที่ component ทำงานร่วมกัน | สร้าง test scenario |
| 4 | Security Audit | หาช่องโหว่ก่อน deploy | OWASP checklist |
| 5 | Performance | Benchmark + optimize | หา bottleneck |
| 6 | QA Testing | Manual + automated E2E | สร้าง test plan |
| 7 | Staging Deploy | ทดสอบใน environment จริง | Deployment script |
| 8 | Monitoring Setup | Log, metric, alert | เขียน dashboard |
| 9 | Production Deploy | Release strategy | Blue/green, canary |
| 10 | A/B Testing | วัดผล feature ใหม่ | สร้าง experiment |
Phase 1: Code Quality — มาตรฐานก่อนทุกอย่าง
โค้ดที่ไม่ผ่าน Quality gate ไม่ควรไปต่อ ตั้งค่าให้ block อัตโนมัติ ป้องกันโค้ดแย่เข้า repository
ตั้งค่า Quality Gate อัตโนมัติ
ให้ Claude ช่วยตั้งค่า Linting, Formatting และ Type checking แบบ zero-config:
> ตั้งค่า code quality toolchain สำหรับ Next.js + TypeScript:
1. ESLint: ใช้ config next/typescript + strict rules
2. Prettier: single quote, no semi, 100 char line length
3. TypeScript: strict mode, no implicit any
4. Husky pre-commit hook: รัน lint + typecheck ก่อน commit
5. lint-staged: รัน prettier เฉพาะไฟล์ที่เปลี่ยน (เร็วกว่า)
ติดตั้งและ config ทุกอย่างในครั้งเดียว
ให้ error ที่ชัดเจนเมื่อ commit ไม่ผ่าน
# package.json scripts ที่ควรมี
{
"scripts": {
"lint": "next lint --fix",
"lint:check": "next lint",
"typecheck": "tsc --noEmit",
"format": "prettier --write .",
"format:check": "prettier --check .",
"quality": "npm run typecheck && npm run lint:check && npm run format:check"
},
"lint-staged": {
"*.{ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{json,md,css}": ["prettier --write"]
}
}
# .husky/pre-commit
#!/bin/sh
npx lint-staged
npm run typecheck
Claude Code Review ก่อน PR
ให้ Claude รีวิวโค้ดทุกครั้งก่อนสร้าง Pull Request โดยใช้ checklist ที่ครอบคลุม:
# .claude/commands/pr-review.md
# Pre-PR Code Review
รีวิวโค้ดทั้งหมดที่เปลี่ยนแปลงจาก main:
## 1. ความถูกต้องของ Logic
- Business rules ถูกต้องตาม @docs/domain/business-rules.md?
- Edge cases ครบ? (empty array, null, undefined, negative numbers)
- Boundary conditions? (0, max int, empty string)
## 2. Error Handling
- ทุก async operation มี try/catch?
- Error messages สื่อความหมาย?
- Error log ไปที่ Sentry ด้วย enough context?
- User เห็น friendly error ไม่ใช่ stack trace?
## 3. Security
- Input validation ครบทุก endpoint?
- Authorization check ก่อนทุก data access?
- Sensitive data ไม่อยู่ใน log?
- SQL/NoSQL injection ป้องกันได้?
## 4. Performance
- N+1 queries?
- Missing database indexes?
- Unnecessary re-renders ใน React?
- Heavy computation ใน render path?
## 5. TypeScript
- ไม่มี any ที่ไม่จำเป็น?
- Types ครอบคลุม null/undefined?
- Generic types ใช้ถูกต้อง?
## Output Format
### ✅ APPROVED / ⚠️ APPROVED WITH NOTES / ❌ CHANGES REQUIRED
### Critical Issues (ต้องแก้ก่อน merge):
### Minor Issues (แก้ได้ทีหลัง):
### Suggestions (ไม่บังคับ):
Phase 2: Testing Strategy — ครบทุกชั้น
Testing Pyramid คือหลักการที่ดี: Unit tests เยอะ Integration tests ปานกลาง E2E tests น้อยแต่ครอบคลุม critical path การมี Claude ช่วยเขียน tests ทำให้ coverage สูงขึ้นได้โดยใช้เวลาน้อยลง
Testing Pyramid สำหรับ Vibe Coder
| ชั้น | จำนวน | ความเร็ว | ค่าใช้จ่าย | ใช้สำหรับ |
|---|---|---|---|---|
| Unit Tests | มาก (70%) | เร็ว ms | ถูก | ทุก function, ทุก utility |
| Integration Tests | กลาง (20%) | กลาง sec | กลาง | API endpoints, DB queries |
| E2E Tests | น้อย (10%) | ช้า นาที | แพง | Critical user journeys เท่านั้น |
Unit Testing — ให้ Claude เขียน Test ครอบคลุม
Unit test ที่ดีต้องครอบคลุม Happy path, Error path, Edge cases และ Boundary conditions:
> เขียน comprehensive unit tests สำหรับ
@src/lib/pricing.ts ทุก function
ใช้ Vitest + describe/it pattern
ครอบคลุม:
1. Happy path: input ปกติ output ถูกต้อง
2. Edge cases: 0, negative, null, undefined, empty
3. Boundary: ค่าต่ำสุด/สูงสุดที่ valid
4. Error cases: input ผิด format ต้อง throw
5. Business rules: ตาม @docs/domain/business-rules.md
Format: describe("functionName") > it("should...")
ไม่ใช้ magic numbers อธิบาย test case ด้วย
ตัวอย่าง Unit Test ที่ดี
// pricing.test.ts
import { describe, it, expect } from "vitest"
import { calculateDiscount, applyMemberDiscount } from "./pricing"
describe("calculateDiscount", () => {
describe("Coupon discount", () => {
it("should apply percentage coupon correctly", () => {
expect(calculateDiscount(1000, { type: "percent", value: 10 })).toBe(900)
})
it("should apply fixed amount coupon correctly", () => {
expect(calculateDiscount(1000, { type: "fixed", value: 150 })).toBe(850)
})
it("should not go below minimum price", () => {
// coupon 100% discount แต่มี minimum price 100
const result = calculateDiscount(500, { type: "percent", value: 100 }, { minPrice: 100 })
expect(result).toBe(100)
})
it("should throw when coupon value is negative", () => {
expect(() => calculateDiscount(1000, { type: "percent", value: -10 }))
.toThrow("Coupon value must be positive")
})
it("should throw when original price is zero", () => {
expect(() => calculateDiscount(0, { type: "percent", value: 10 }))
.toThrow("Original price must be greater than 0")
})
})
describe("Member discount stacking", () => {
it("should apply member discount AFTER coupon", () => {
// 1000 → coupon 10% → 900 → member 5% → 855
const afterCoupon = calculateDiscount(1000, { type: "percent", value: 10 })
expect(applyMemberDiscount(afterCoupon, "gold")).toBe(855)
})
})
})
Integration Testing — ทดสอบ API Endpoints
Integration test ตรวจสอบว่า component หลายตัวทำงานร่วมกันถูกต้อง รวมถึง database จริง:
> สร้าง integration tests สำหรับ POST /api/orders
Setup:
- ใช้ Vitest + supertest
- Database: test database แยกจาก dev
- Seed: สร้าง user และ products ก่อน test
- Cleanup: ลบข้อมูลหลัง test ทุกอัน
Test cases:
1. สร้าง order สำเร็จ → 201 + order ID
2. สร้าง order โดยไม่มี auth → 401
3. สินค้า out of stock → 400 + error message
4. สินค้าไม่มีอยู่จริง → 404
5. Payment amount ไม่ตรง → 400
6. Concurrent orders (race condition test)
Verify ด้วย:
- Response status code
- Response body structure
- Database state หลัง request
- Side effects (email sent?, inventory updated?)
// orders.integration.test.ts
import { describe, it, expect, beforeEach, afterEach } from "vitest"
import request from "supertest"
import { app } from "../app"
import { db } from "../lib/db"
import { createTestUser, createTestProduct, clearTestData } from "./helpers"
describe("POST /api/orders", () => {
let authToken: string
let testProduct: { id: string; price: number; stock: number }
beforeEach(async () => {
const user = await createTestUser({ email: "test@test.com" })
authToken = user.token
testProduct = await createTestProduct({ price: 1000, stock: 10 })
})
afterEach(async () => {
await clearTestData()
})
it("should create order successfully", async () => {
const res = await request(app)
.post("/api/orders")
.set("Authorization", `Bearer ${authToken}`)
.send({ productId: testProduct.id, quantity: 2 })
expect(res.status).toBe(201)
expect(res.body.order.total).toBe(2000)
// verify inventory updated
const product = await db.product.findUnique({ where: { id: testProduct.id } })
expect(product?.stock).toBe(8) // 10 - 2
})
it("should return 400 when out of stock", async () => {
const res = await request(app)
.post("/api/orders")
.set("Authorization", `Bearer ${authToken}`)
.send({ productId: testProduct.id, quantity: 100 }) // เกิน stock
expect(res.status).toBe(400)
expect(res.body.error.code).toBe("E_OUT_OF_STOCK")
})
})
E2E Testing — ทดสอบ Critical User Journey
E2E test ใช้ Playwright จำลอง user จริง เน้นเฉพาะ journey ที่ถ้าพังแล้วกระทบรายได้มากที่สุด:
> สร้าง E2E tests ด้วย Playwright สำหรับ
Critical user journeys 3 อัน:
Journey 1: Purchase Flow (สำคัญที่สุด)
- เปิดหน้าแรก → เลือกสินค้า → Add to cart
- → Checkout → กรอก shipping → เลือก payment
- → ยืนยัน order → เห็น confirmation page
- → ได้รับ email confirmation
Journey 2: User Registration + First Purchase
- กรอก email, password, verify email
- → ซื้อสินค้าครั้งแรก → เห็น welcome discount
Journey 3: Search + Filter + Purchase
- ค้นหาสินค้า → filter by category, price
- → เลือกสินค้า → ซื้อ
Setup:
- ใช้ test user account แยกต่างหาก
- รัน against staging environment
- Screenshot เมื่อ fail
- Retry 2 ครั้งอัตโนมัติ
// tests/e2e/purchase-flow.spec.ts
import { test, expect } from "@playwright/test"
test.describe("Purchase Flow", () => {
test.beforeEach(async ({ page }) => {
// Login ด้วย test account
await page.goto("/login")
await page.fill("[name=email]", process.env.TEST_USER_EMAIL!)
await page.fill("[name=password]", process.env.TEST_USER_PASSWORD!)
await page.click("[type=submit]")
await page.waitForURL("/dashboard")
})
test("complete purchase flow", async ({ page }) => {
// เลือกสินค้า
await page.goto("/products")
await page.click("[data-testid=product-card]:first-child")
await page.click("[data-testid=add-to-cart]")
// Checkout
await page.goto("/cart")
await page.click("[data-testid=checkout-btn]")
// กรอก shipping
await page.fill("[name=address]", "123 Test Street")
await page.fill("[name=city]", "Bangkok")
await page.selectOption("[name=province]", "BKK")
await page.click("[data-testid=next-btn]")
// เลือก payment (test card)
await page.fill("[name=card-number]", "4242424242424242")
await page.fill("[name=expiry]", "12/28")
await page.fill("[name=cvc]", "123")
await page.click("[data-testid=pay-btn]")
// verify confirmation
await expect(page.getByText("Order Confirmed")).toBeVisible()
await expect(page.getByTestId("order-id")).toContainText("ORD-")
})
})
// playwright.config.ts
export default {
retries: 2,
screenshot: "only-on-failure",
video: "retain-on-failure",
use: { baseURL: process.env.STAGING_URL },
}
Test Coverage Report — วัดความครบถ้วน
# ดู coverage report
npx vitest run --coverage
# ให้ Claude วิเคราะห์ coverage gaps
> นี่คือ coverage report:
File: src/lib/pricing.ts — 67% coverage
Uncovered lines: 45-67, 89-102
ดู @src/lib/pricing.ts บรรทัด 45-67 และ 89-102
แล้วสร้าง test cases ที่ยังขาดอยู่
โดยเฉพาะ error handling paths
# ตั้ง minimum coverage threshold
// vitest.config.ts
coverage: {
thresholds: {
lines: 80,
functions: 80,
branches: 70,
}
}
Phase 3: Security Audit — ป้องกันก่อนถูกโจมตี
Security audit เป็นขั้นตอนที่ Vibe Coder มักข้ามเพราะคิดว่า Claude น่าจะเขียนโค้ดปลอดภัย แต่ความจริงคือ Claude อาจพลาด security issue ได้เหมือนกัน
Security Audit Checklist ด้วย Claude
# .claude/commands/security-audit.md
# Security Audit
ทำ security audit แบบ comprehensive สำหรับ PR นี้:
## OWASP Top 10 Checklist
### 1. Injection
- SQL Injection: ใช้ parameterized queries หรือ ORM?
- NoSQL Injection: validate input ก่อน query?
- Command Injection: ไม่รัน user input ใน shell?
### 2. Broken Authentication
- Session timeout ตั้งค่าไว้?
- Password ที่เก็บ hash ด้วย bcrypt/argon2?
- Brute force protection?
- JWT: expire time สมเหตุสมผล, signature verify ถูกต้อง?
### 3. Sensitive Data Exposure
- HTTPS ทุก endpoint?
- Sensitive data ใน log?
- Password/token ใน error message?
- API response ส่ง field ที่ไม่ควรส่งไหม?
### 4. Access Control
- ทุก endpoint มี auth check?
- User A เข้าถึงข้อมูล User B ได้ไหม? (IDOR)
- Admin-only routes มี role check?
### 5. XSS
- User input ที่ render เป็น HTML ถูก sanitize?
- dangerouslySetInnerHTML ใช้กับ user input?
- Content-Security-Policy header ตั้งไว้?
### 6. CSRF
- Form submit มี CSRF token?
- State-changing requests ใช้ POST ไม่ใช่ GET?
## รายงานผล
### 🔴 Critical (ต้องแก้ทันที):
### 🟠 High (ต้องแก้ก่อน production):
### 🟡 Medium (แก้ใน sprint นี้):
### 🟢 Low (แก้ได้ทีหลัง):
ตัวอย่างการหาและแก้ Security Issues
IDOR (Insecure Direct Object Reference)
| ❌ มีช่องโหว่ IDOR // GET /api/orders/:id const order = await db.order.findUnique({ where: { id: params.id } }) // ไม่ check ว่า order เป็นของ user นี้! // user A ดู order ของ user B ได้ | ✅ ป้องกัน IDOR const order = await db.order.findFirst({ where: { id: params.id, userId: session.user.id // check ownership } }) if (!order) { throw new NotFoundError() // ไม่บอกว่ามีอยู่แต่ไม่มีสิทธิ์ } |
|---|
Mass Assignment
| ❌ มีช่องโหว่ // User ส่ง body อะไรก็ได้ await db.user.update({ where: { id: userId }, data: req.body // อันตราย! }) // user อาจส่ง { role: “admin” } | ✅ Whitelist fields const { name, email, bio } = req.body // รับเฉพาะ field ที่อนุญาต await db.user.update({ where: { id: userId }, data: { name, email, bio } }) // role ไม่สามารถเปลี่ยนได้ |
|---|
Environment Variables Security
> ตรวจสอบ security ของ environment variables:
1. มี .env.example ที่ไม่มี secret จริง?
2. .env อยู่ใน .gitignore?
3. ไม่มีการ console.log env vars?
4. Validation ว่า required env ครบเมื่อ startup?
สร้าง src/lib/env.ts ที่:
- validate env vars ทุกตัวตอน app start
- throw error ถ้า required var ขาด
- type-safe env vars ด้วย zod
// src/lib/env.ts
import { z } from "zod"
const envSchema = z.object({
DATABASE_URL: z.string().url(),
NEXTAUTH_SECRET: z.string().min(32),
STRIPE_SECRET_KEY: z.string().startsWith("sk_"),
NEXT_PUBLIC_APP_URL: z.string().url(),
})
export const env = envSchema.parse(process.env)
// throw at startup ถ้า env ไม่ถูกต้อง
Phase 4: Performance Benchmark — วัดก่อนปรับ
ไม่ควร optimize อะไรก็ตามโดยไม่มีตัวเลข “ช้า” และ “เร็ว” ต้องนิยามให้ชัดก่อน แล้วค่อย benchmark เปรียบเทียบ
Performance Budget — กำหนดเป้าหมาย
Performance Budget คือตัวเลขที่ตกลงกันว่า “ดีพอ” ก่อน optimize ต้องรู้ว่า target คืออะไร:
| Metric | Poor | Needs Work | Good | Excellent |
|---|---|---|---|---|
| Page Load (LCP) | > 4s | 2.5-4s | < 2.5s | < 1s |
| API Response (P95) | > 1000ms | 500-1000ms | < 500ms | < 100ms |
| Time to Interactive | > 5s | 3.8-5s | < 3.8s | < 1s |
| First Byte (TTFB) | > 1800ms | 900-1800ms | < 900ms | < 200ms |
| Core Web Vitals (CLS) | > 0.25 | 0.1-0.25 | < 0.1 | < 0.05 |
# เพิ่มใน CLAUDE.md
## Performance Budget
- API response P95: < 500ms (เป้าหมาย < 200ms)
- Page load LCP: < 2.5s บน 4G mobile
- Database query: < 100ms ทุก query
- Memory usage: < 512MB per instance
## ถ้าทำให้ exceed budget ต้องขออนุมัติก่อน
Database Performance Benchmark
> สร้าง performance benchmark สำหรับ
database queries ที่ใช้บ่อยที่สุด:
1. รัน EXPLAIN ANALYZE สำหรับทุก query ที่สำคัญ
2. วัด execution time กับ dataset ขนาด:
- 1,000 rows (dev ปัจจุบัน)
- 100,000 rows (3 เดือนข้างหน้า)
- 1,000,000 rows (1 ปีข้างหน้า)
3. ระบุ query ที่จะ slow ที่ scale ใหญ่
4. เสนอ indexes ที่ต้องเพิ่ม
# สร้าง benchmark script
// benchmark/db-queries.ts
import { db } from "../src/lib/db"
import { performance } from "perf_hooks"
async function benchmark(name: string, fn: () => Promise<unknown>) {
const runs = 100
const times: number[] = []
for (let i = 0; i < runs; i++) {
const start = performance.now()
await fn()
times.push(performance.now() - start)
}
times.sort((a, b) => a - b)
console.log(`${name}:`)
console.log(` P50: ${times[50].toFixed(2)}ms`)
console.log(` P95: ${times[95].toFixed(2)}ms`)
console.log(` P99: ${times[99].toFixed(2)}ms`)
}
await benchmark("getProductList", () =>
db.product.findMany({ take: 20, include: { category: true } })
)
await benchmark("searchProducts", () =>
db.product.findMany({ where: { name: { contains: "test" } }, take: 20 })
)
API Load Testing ด้วย k6
Load testing จำลอง user จำนวนมากใช้งานพร้อมกัน ช่วยหา bottleneck ก่อน traffic จริงมา:
> สร้าง load test script ด้วย k6 สำหรับ:
Scenario 1: Normal load
- 50 concurrent users, 5 นาที
- ทำ: browse product, search, view detail
Scenario 2: Peak load
- Ramp up จาก 0 → 200 users ใน 2 นาที
- คงที่ 200 users 5 นาที
- Ramp down 2 นาที
Scenario 3: Stress test
- หา breaking point ว่า server ล่มที่กี่ users
Success criteria:
- Error rate < 1%
- P95 response < 500ms
- ไม่มี memory leak (memory ไม่เพิ่มขึ้นเรื่อยๆ)
// load-test.js (k6 script)
import http from "k6/http"
import { check, sleep } from "k6"
import { Rate } from "k6/metrics"
const errorRate = new Rate("errors")
export const options = {
stages: [
{ duration: "2m", target: 50 }, // Ramp up
{ duration: "5m", target: 50 }, // Stay at 50
{ duration: "2m", target: 200 }, // Ramp to peak
{ duration: "5m", target: 200 }, // Stay at peak
{ duration: "2m", target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ["p(95)<500"], // P95 < 500ms
errors: ["rate<0.01"], // Error < 1%
},
}
const BASE_URL = "https://staging.myapp.com"
export default function() {
// Browse products
const res = http.get(`${BASE_URL}/api/products?page=1`)
errorRate.add(res.status !== 200)
check(res, { "products loaded": (r) => r.status === 200 })
sleep(1)
// Search
const search = http.get(`${BASE_URL}/api/products?q=phone`)
errorRate.add(search.status !== 200)
sleep(0.5)
}
# รัน: k6 run load-test.js
# ดู result: k6 run --out json=result.json load-test.js
Frontend Performance — Core Web Vitals
> วิเคราะห์และปรับปรุง Core Web Vitals:
1. รัน Lighthouse CI บน staging:
npx lhci autorun
2. วัด metrics ปัจจุบัน:
- LCP (Largest Contentful Paint)
- FID / INP (Interaction to Next Paint)
- CLS (Cumulative Layout Shift)
3. ให้ Claude วิเคราะห์ report:
> นี่คือ Lighthouse report:
[paste JSON output]
ระบุ top 5 improvements ที่ impact สูงที่สุด
โดยเรียงตาม effort vs impact
4. ปรับปรุงที่พบบ่อย:
- Image optimization (next/image)
- Font loading (display: swap)
- Unused JavaScript (tree shaking)
- Server-side rendering ที่เหมาะสม
# lighthouserc.js
module.exports = {
ci: {
collect: {
url: ["https://staging.myapp.com", "/products", "/checkout"],
numberOfRuns: 3,
},
assert: {
assertions: {
"categories:performance": ["error", { minScore: 0.8 }],
"categories:accessibility": ["error", { minScore: 0.9 }],
"first-contentful-paint": ["error", { maxNumericValue: 2000 }],
"largest-contentful-paint": ["error", { maxNumericValue: 2500 }],
"cumulative-layout-shift": ["error", { maxNumericValue: 0.1 }],
},
},
},
}
Phase 5: QA Testing — ก่อนถึงมือ User
QA (Quality Assurance) คือขั้นตอนที่ทดสอบ feature ในมุมมองของ user จริง ไม่ใช่แค่ technical correctness Claude ช่วยสร้าง test plan ที่ครอบคลุมได้
สร้าง QA Test Plan ด้วย Claude
> สร้าง QA Test Plan สำหรับ Feature: User Profile Edit
Feature requirements:
- User แก้ไข name, email, bio, avatar ได้
- Email ต้อง verify ใหม่เมื่อเปลี่ยน
- Avatar resize อัตโนมัติ max 500x500px
- Preview ก่อน save
สร้าง test plan ที่ครอบคลุม:
1. Functional tests: ทุก feature ทำงานได้
2. Negative tests: input ผิดแล้วเกิดอะไร
3. Boundary tests: ค่าขีดจำกัด
4. UX tests: user experience flow
5. Accessibility tests: keyboard, screen reader
6. Cross-browser: Chrome, Firefox, Safari
7. Mobile responsive: iPhone, Android
Format: Test ID | Test Case | Steps | Expected | Priority
ตัวอย่าง QA Test Plan
| Test ID | Test Case | Steps | Expected | Priority |
|---|---|---|---|---|
| QA-001 | แก้ชื่อสำเร็จ | 1.กรอกชื่อใหม่ 2.Save | ชื่อเปลี่ยนทันที | High |
| QA-002 | ชื่อว่างเปล่า | 1.ลบชื่อออก 2.Save | Error: Name required | High |
| QA-003 | ชื่อยาวเกิน | 1.กรอก 200 ตัว 2.Save | Error: Max 100 chars | Medium |
| QA-004 | Email ใหม่ | 1.เปลี่ยน email 2.Save | Email verify ถูกส่ง | High |
| QA-005 | Avatar ขนาดใหญ่ | 1.Upload 5MB PNG | Auto resize < 500KB | Medium |
| QA-006 | Avatar format ผิด | 1.Upload .pdf | Error: Invalid format | Medium |
| QA-007 | Preview ก่อน save | 1.เปลี่ยนรูป 2.ดู preview | Preview ถูกต้อง | Low |
Regression Testing — ป้องกัน Feature เก่าพัง
เมื่อ deploy feature ใหม่ ต้องมั่นใจว่า feature เก่ายังทำงานได้ Claude ช่วยสร้าง regression test suite ได้:
> สร้าง regression test suite สำหรับ release นี้
ตรวจสอบว่า feature เก่ายังทำงานได้:
Critical features ที่ต้อง test:
- User login/logout
- Product listing และ search
- Add to cart
- Checkout flow
- Order history
- Payment
สร้าง Playwright script ที่รันทุก feature
ใน 15 นาที (ต้องเร็วพอสำหรับ CI/CD)
บน staging environment
# ตั้งให้รันอัตโนมัติก่อน production deploy
# ถ้า fail → block deployment
Accessibility Testing — ทุกคนใช้ได้
> ตรวจสอบ Accessibility ของ @src/components/
ตาม WCAG 2.1 Level AA:
1. Semantic HTML: ใช้ heading, button, nav ถูกต้อง?
2. Keyboard navigation: Tab ผ่านทุก interactive element?
3. Focus indicator: เห็นชัดเจนว่า focus อยู่ที่ไหน?
4. Alt text: ทุกรูปมี meaningful alt?
5. Color contrast: ผ่าน 4.5:1 ratio?
6. Error messages: screen reader อ่านได้?
7. Form labels: ทุก input มี label?
สร้าง axe-core test สำหรับ pages สำคัญ
// accessibility.test.ts
import { test, expect } from "@playwright/test"
import AxeBuilder from "@axe-core/playwright"
test("checkout page passes accessibility", async ({ page }) => {
await page.goto("/checkout")
const results = await new AxeBuilder({ page }).analyze()
expect(results.violations).toEqual([])
})
Phase 6: Deployment Strategy — ปล่อยอย่างปลอดภัย
Deploy แบบมั่นใจต้องมี strategy ที่ลด risk และ rollback ได้เร็วเมื่อมีปัญหา
Environment Strategy
| Environment | ใช้สำหรับ | Database | Deploy เมื่อ |
|---|---|---|---|
| Local Dev | Development ส่วนตัว | Local SQLite/PG | ตลอดเวลา |
| Staging | QA + Integration test | Staging DB (copy prod) | ทุก PR merge |
| Preview | Review per PR | ใช้ร่วมกับ Staging | ทุก PR สร้าง |
| Production | User จริง | Production DB | Manual approve |
Deployment Checklist ก่อน Production
# .claude/commands/deploy-check.md
# Pre-Production Deployment Checklist
ตรวจสอบทุกข้อก่อน approve production deploy:
## Code Quality
□ All tests pass (unit + integration + e2e)
□ No TypeScript errors
□ No ESLint errors
□ Code review approved
□ Security audit passed
## Database
□ Migration tested บน staging แล้ว
□ Migration reversible (มี rollback script)
□ Data backup ล่าสุดมีไหม
□ Index strategy reviewed
## Configuration
□ Environment variables ครบใน production
□ API keys ถูกต้อง (ไม่ใช่ test keys)
□ Rate limits ตั้งค่าแล้ว
□ CORS ตั้งค่า whitelist ถูก
## Monitoring
□ Error tracking (Sentry) connected
□ Performance monitoring connected
□ Alert rules ตั้งค่าแล้ว
□ Dashboard พร้อม monitor
## Rollback Plan
□ มีวิธี rollback code ใน < 5 นาที
□ มีวิธี rollback database ถ้า migration เสีย
□ On-call ใครดู production ช่วง deploy
รัน: git diff main...HEAD --stat แล้วสรุปให้
Blue-Green Deployment
Blue-Green คือการมี 2 environment เหมือนกัน สลับ traffic เมื่อ deploy ทำให้ rollback ทันทีได้โดยไม่มี downtime:
> อธิบาย Blue-Green deployment strategy
และสร้าง deployment script สำหรับ:
Infrastructure: Vercel (หรือ AWS ECS)
Flow:
1. Deploy version ใหม่ไปที่ Green environment
2. Run smoke tests บน Green
3. ถ้าผ่าน → switch traffic จาก Blue → Green
4. ถ้าไม่ผ่าน → stay on Blue, debug Green
5. หลัง stable → Green กลายเป็น Blue สำหรับ deploy ถัดไป
# deploy.sh
#!/bin/bash
set -e
echo "🚀 Starting deployment..."
# Deploy to staging
vercel --prod --scope=team --yes
# Run smoke tests
echo "🔍 Running smoke tests..."
npx playwright test tests/smoke/ --project=chromium
if [ $? -eq 0 ]; then
echo "✅ Deployment successful!"
# Notify Slack
curl -X POST $SLACK_WEBHOOK -d '{"text":"✅ Deploy success!"}'
else
echo "❌ Smoke tests failed! Rolling back..."
vercel rollback
curl -X POST $SLACK_WEBHOOK -d '{"text":"❌ Deploy failed, rolling back"}'
exit 1
fi
Canary Release — ปล่อยทีละน้อย
Canary release คือการปล่อย feature ให้ user บางส่วนก่อน ถ้าไม่มีปัญหาค่อยขยาย เหมาะสำหรับ feature ใหญ่ที่ risk สูง:
// Feature flags สำหรับ Canary release
// src/lib/feature-flags.ts
import { getServerSession } from "next-auth"
export async function isFeatureEnabled(flag: string): Promise<boolean> {
// ดึงจาก LaunchDarkly, Unleash, หรือ database
const flags = await getFeatureFlags()
return flags[flag]?.enabled ?? false
}
// ใช้งานใน component
const showNewCheckout = await isFeatureEnabled("new_checkout_v2")
return showNewCheckout ? <NewCheckout /> : <OldCheckout />
# Canary rollout plan
# Day 1: 5% ของ users
# Day 3: 25% ถ้าไม่มีปัญหา
# Day 7: 50%
# Day 10: 100%
Phase 7: Monitoring — รู้ปัญหาก่อน User รายงาน
Monitoring ที่ดีทำให้รู้ว่าระบบมีปัญหาก่อน user จะมา complain สร้างได้ใน 1-2 ชั่วโมงด้วย Claude
Error Tracking ด้วย Sentry
> ตั้งค่า Sentry สำหรับ Next.js app นี้:
1. สร้าง sentry.client.config.ts และ sentry.server.config.ts
2. Capture ทุก uncaught error
3. Enrich errors ด้วย: user ID, session info,
request details, environment
4. Ignore errors ที่ไม่สำคัญ: network errors จาก bots
5. Set up alerts: email เมื่อ error rate > 1%
6. Create release tracking เพื่อดูว่า deploy ไหนทำให้เกิด error
// sentry.server.config.ts
import * as Sentry from "@sentry/nextjs"
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: process.env.NODE_ENV === "production" ? 0.1 : 1.0,
beforeSend(event) {
// ไม่ส่ง error จาก bot
if (event.request?.headers?.["user-agent"]?.includes("bot")) {
return null
}
return event
}
})
Application Metrics
// src/lib/metrics.ts — custom metrics
import { register, Counter, Histogram } from "prom-client"
export const httpRequestDuration = new Histogram({
name: "http_request_duration_seconds",
help: "Duration of HTTP requests in seconds",
labelNames: ["method", "route", "status"],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5],
})
export const orderCreated = new Counter({
name: "orders_created_total",
help: "Total number of orders created",
labelNames: ["payment_method"],
})
export const paymentFailed = new Counter({
name: "payment_failures_total",
help: "Total number of payment failures",
labelNames: ["reason"],
})
// ใช้งาน
orderCreated.inc({ payment_method: "credit_card" })
paymentFailed.inc({ reason: "insufficient_funds" })
// Dashboard ดูที่ /metrics
Alert Rules — แจ้งเตือนอัตโนมัติ
> สร้าง alert rules สำหรับ production:
Critical (PagerDuty + SMS):
- Error rate > 5% ใน 5 นาที
- API P95 > 2000ms ใน 10 นาที
- Payment failure rate > 10%
- Server CPU > 90% นาน > 5 นาที
Warning (Slack):
- Error rate > 1%
- API P95 > 500ms
- Database connection pool > 80%
- Memory > 80%
Info (Log only):
- New user registration
- Order created
- Deploy completed
# สร้าง Slack alert function
async function sendAlert(level: string, message: string) {
await fetch(process.env.SLACK_WEBHOOK!, {
method: "POST",
body: JSON.stringify({
text: `${level === "critical" ? "🚨" : "⚠️"} ${message}`,
})
})
}
Phase 8: A/B Testing — วัดผลด้วย Data จริง
A/B Testing คือการทดสอบ 2 version พร้อมกันเพื่อวัดว่า version ไหนดีกว่าจาก user behavior จริง ไม่ใช่ความเห็นของทีม
เมื่อไหร่ควรทำ A/B Test?
| ควรทำ A/B Test | ไม่ต้องทำ A/B Test |
|---|---|
| UI/UX ที่คาดว่าจะกระทบ conversion | Bug fixes (ชัดเจนว่าต้องแก้) |
| Pricing หรือ promotion strategy | Security improvements |
| Onboarding flow ใหม่ | Feature ที่ทุกคน request |
| CTA copy หรือ button design | Technical refactoring |
| Checkout flow ปรับปรุง | Compliance requirements |
| Search algorithm เปลี่ยน | ฐาน user น้อยเกินไป (< 1000/วัน) |
A/B Test Setup ด้วย Claude
> สร้าง A/B test framework สำหรับ checkout button:
Hypothesis:
"เปลี่ยน CTA จาก Checkout เป็น Buy Now จะเพิ่ม conversion"
Primary metric: Conversion rate (cart → order)
Secondary metrics: Revenue per user, Time to checkout
Guard rails: Cart abandonment ต้องไม่เพิ่มขึ้น
Sample size: คำนวณด้วย statistical significance
Duration: 2 สัปดาห์ (ให้ครบ weekly pattern)
สร้าง:
1. Experiment config
2. Assignment logic (user ไหนเห็น variant ไหน)
3. Event tracking
4. Analysis query
// src/lib/experiments.ts
import { db } from "./db"
import { hashUserId } from "./utils"
interface Experiment {
name: string
variants: string[]
weights: number[] // ต้องรวมกันได้ 100
startDate: Date
endDate: Date
}
export async function getVariant(
userId: string,
experiment: Experiment
): Promise<string> {
// Deterministic assignment: user เดิม = variant เดิมทุกครั้ง
const hash = hashUserId(userId + experiment.name)
const bucket = hash % 100
let cumulative = 0
for (let i = 0; i < experiment.variants.length; i++) {
cumulative += experiment.weights[i]
if (bucket < cumulative) {
// Log assignment
await logExperimentAssignment(userId, experiment.name, experiment.variants[i])
return experiment.variants[i]
}
}
return experiment.variants[0]
}
// ใช้งาน
const checkoutExperiment: Experiment = {
name: "checkout_cta_q1_2025",
variants: ["control", "buy_now"],
weights: [50, 50], // 50/50 split
startDate: new Date("2025-01-15"),
endDate: new Date("2025-01-29"),
}
const variant = await getVariant(session.user.id, checkoutExperiment)
const buttonText = variant === "buy_now" ? "Buy Now" : "Checkout"
Event Tracking สำหรับ A/B Test
// src/lib/analytics.ts
export async function trackEvent(
userId: string,
event: string,
properties: Record<string, unknown>
) {
await db.analyticsEvent.create({
data: {
userId,
event,
properties: JSON.stringify(properties),
timestamp: new Date(),
}
})
}
// Track สำคัญใน checkout flow
await trackEvent(userId, "checkout_started", {
experiment: "checkout_cta_q1_2025",
variant: variant,
cartValue: cart.total,
})
await trackEvent(userId, "order_completed", {
experiment: "checkout_cta_q1_2025",
variant: variant,
orderId: order.id,
revenue: order.total,
})
วิเคราะห์ผล A/B Test
> วิเคราะห์ผล A/B test นี้:
Experiment: checkout_cta_q1_2025
Duration: 2025-01-15 ถึง 2025-01-29
Control (Checkout):
- Impressions: 5,234
- Conversions: 523 (10.0% conversion rate)
- Revenue: ฿2,615,000
Variant (Buy Now):
- Impressions: 5,198
- Conversions: 571 (10.98% conversion rate)
- Revenue: ฿2,882,000
บอกผม:
1. Statistical significance (p-value)?
2. Confidence interval?
3. Effect size?
4. ควร ship variant หรือไม่ พร้อมเหตุผล
5. คำแนะนำ next experiment
# SQL Query วิเคราะห์
SELECT
properties->>variant AS variant,
COUNT(DISTINCT CASE WHEN event = "checkout_started" THEN userId END) as impressions,
COUNT(DISTINCT CASE WHEN event = "order_completed" THEN userId END) as conversions,
ROUND(100.0 * COUNT(DISTINCT CASE WHEN event = "order_completed" THEN userId END)
/ COUNT(DISTINCT CASE WHEN event = "checkout_started" THEN userId END), 2) AS conversion_rate,
SUM(CASE WHEN event = "order_completed" THEN (properties->>revenue)::numeric END) AS revenue
FROM analytics_events
WHERE properties->>experiment = "checkout_cta_q1_2025"
AND timestamp BETWEEN "2025-01-15" AND "2025-01-29"
GROUP BY variant
เสริมจากบทที่ 4: Loop Debugging ขั้น Advanced
บทที่ 4 ให้ Protocol พื้นฐาน ส่วนนี้จะลงลึกเทคนิคที่ใช้ได้กับ production bugs ที่ซับซ้อนกว่า
Root Cause Analysis (RCA) Framework
เมื่อ production มีปัญหาใหญ่ ใช้ RCA หา root cause จริงๆ ไม่ใช่แค่แก้ symptom:
# .claude/commands/rca.md
# Root Cause Analysis
ทำ RCA สำหรับ incident นี้:
## Timeline
สร้าง timeline ว่าอะไรเกิดขึ้นตามลำดับ:
- [เวลา] เกิดอะไร
- [เวลา] ใครพบ
- [เวลา] ทำอะไรแก้
- [เวลา] กลับมาปกติ
## 5 Whys Analysis
ถามว่า "ทำไม" ซ้ำ 5 ครั้งจนถึง root cause:
ทำไม? → ทำไม? → ทำไม? → ทำไม? → ทำไม?
## Root Causes
- Proximate cause: สิ่งที่ทำให้เกิด incident โดยตรง
- Root cause: ต้นตอที่แท้จริง
- Contributing factors: ปัจจัยที่ทำให้รุนแรงขึ้น
## Impact
- Users affected: [จำนวน]
- Duration: [เวลา]
- Revenue impact: [ประมาณ]
## Action Items
| Action | Owner | Priority | Due Date |
|--------|-------|----------|----------|
| [แก้ root cause] | [ชื่อ] | P0 | [วันที่] |
| [ป้องกันซ้ำ] | [ชื่อ] | P1 | [วันที่] |
## ป้องกัน recurrence
อะไรที่ต้องเปลี่ยนใน process, code, หรือ monitoring?
Production Debugging Protocol
เมื่อ production มีปัญหาและต้องแก้เร็ว Protocol นี้ช่วยให้คิดอย่างเป็นระบบแม้ตอนกดดัน:
| 1 | Assess — ประเมินก่อน Critical หรือไม่? User กี่คนได้รับผล? Revenue impact? ต้อง rollback ทันทีไหม? |
|---|
> Production incident: [อธิบายปัญหา]
มี error log นี้:
[paste logs]
ช่วยประเมิน:
1. Severity: P0/P1/P2/P3?
2. Blast radius: กระทบ user กี่ %?
3. ควร rollback ทันทีหรือแก้ไปข้างหน้า?
4. ถ้าแก้: ใช้เวลานานแค่ไหน?
ตอบภายใน 2 นาทีโดยไม่ต้องสมบูรณ์ 100%
ต้องการ decision ก่อน
| 2 | Contain — หยุดความเสียหาย ทำให้ระบบ stable ก่อน แม้จะยังไม่ได้แก้ root cause |
|---|
# วิธี contain ที่พบบ่อย
# 1. Rollback deployment
vercel rollback # หรือ git revert + deploy
# 2. Feature flag ปิด feature ที่มีปัญหา
await setFeatureFlag("new_payment", false)
# 3. Rate limiting เพื่อลด load
# 4. Redirect traffic ออกจาก broken endpoint
# 5. Scale up servers ถ้า load issue
| 3 | Diagnose — หา root cause วิเคราะห์อย่างเป็นระบบ ไม่ใช่เดาสุ่ม |
|---|
> วิเคราะห์ production incident:
Logs ที่เก็บได้:
[paste logs ทั้งหมด]
Timeline:
- 14:30 น. Deploy version 2.4.1
- 14:45 น. Error rate เริ่มสูง
- 15:00 น. User complaints
Code ที่เปลี่ยนใน v2.4.1:
[paste git diff หรือ PR description]
ช่วย:
1. วิเคราะห์ logs หา pattern
2. เชื่อม logs กับ code ที่เปลี่ยน
3. Identify root cause ที่น่าจะเป็นไปได้มากที่สุด
4. เสนอ diagnostic steps เพิ่มเติมถ้ายังไม่แน่ใจ
| 4 | Fix — แก้และ verify แก้ root cause พร้อมทดสอบก่อน deploy |
|---|
| 5 | Document — บันทึกทุกอย่าง สร้าง post-mortem ป้องกันซ้ำ |
|---|
การจัดการ Dependencies ที่ทำให้เกิด Loop
บ่อยครั้ง loop เกิดจาก dependency ที่มี bug หรือ breaking change ไม่ใช่โค้ดของเราเอง:
> Error นี้เกิดหลัง update packages:
TypeError: Cannot read property "x" of undefined
ใน node_modules/some-library/index.js
npm list | grep some-library
some-library@3.0.0 ← update จาก 2.x
ช่วย:
1. วิเคราะห์ว่า breaking change อยู่ที่ไหน
2. ดู changelog ของ library ว่าต้องทำอะไรบ้าง
3. เสนอ migration path
4. ถ้า migration ซับซ้อน เสนอ downgrade
พร้อม lock version เพื่อ stability
# Lock version ป้องกัน auto-update
# package.json
"some-library": "2.8.1" # ใช้ exact version
# ไม่ใช่ "^2.8.1" (อนุญาต minor update)
# ไม่ใช่ "~2.8.1" (อนุญาต patch update)
Debugging ด้วย Time Travel (Git Bisect Automation)
เมื่อไม่รู้ว่า commit ไหนทำให้เกิดบัก ใช้ Git Bisect อัตโนมัติ:
# สร้าง bisect script อัตโนมัติ
# bisect-test.sh
#!/bin/bash
# Script นี้จะถูกรันโดย git bisect
# exit 0 = good commit
# exit 1 = bad commit
npm run build 2>/dev/null # ต้อง build ได้
if [ $? -ne 0 ]; then exit 1; fi
# รัน specific test ที่เกี่ยวกับ bug
npx vitest run tests/pricing.test.ts 2>/dev/null
exit $?
# รัน bisect
git bisect start
git bisect bad HEAD
git bisect good v2.3.0 # version ที่รู้ว่าดี
git bisect run ./bisect-test.sh
# Git จะหา commit ที่ทำให้เกิดบักอัตโนมัติ
# จากนั้นให้ Claude วิเคราะห์
> ได้ bad commit นี้:
[paste commit diff]
อธิบายว่า commit นี้ทำให้เกิด bug อย่างไร
และวิธีแก้ที่ถูกต้อง
Memory Leak Detection
Memory leak ทำให้ server ช้าลงเรื่อยๆ จนต้อง restart เป็น production bug ที่พบบ่อยแต่หายาก:
> สงสัยว่ามี memory leak ใน @src/server/
Memory เพิ่มจาก 200MB เป็น 800MB ใน 24 ชั่วโมง
แล้ว restart ลดลงมา 200MB ใหม่
ช่วยสร้าง:
1. Memory monitoring endpoint: GET /api/debug/memory
แสดง heap used, heap total, RSS
2. Leak detection script ที่รัน
ทุก 1 นาทีแล้ว log ถ้า memory เพิ่ม > 10MB/นาที
3. Suspect code analysis:
ดู @src/server/ หา patterns ที่มักทำให้ memory leak:
- EventEmitter ที่ไม่ removeListener
- setTimeout/setInterval ที่ไม่ clearTimeout
- Closure ที่ hold reference
- Cache ที่ไม่มี size limit
- Stream ที่ไม่ปิด
// memory-monitor.ts
setInterval(() => {
const mem = process.memoryUsage()
console.log({
heapUsed: Math.round(mem.heapUsed / 1024 / 1024) + "MB",
heapTotal: Math.round(mem.heapTotal / 1024 / 1024) + "MB",
rss: Math.round(mem.rss / 1024 / 1024) + "MB",
timestamp: new Date().toISOString()
})
}, 60000)
ตัวอย่าง: Feature ตั้งแต่ Vibe Coding ถึง Production จริง
ตัวอย่างนี้ walk through feature “ระบบ Coupon” ตั้งแต่เริ่มจนถึง production และ A/B test ให้เห็น full pipeline จริงๆ
| Day 1: Vibe Coding |
|---|
> สร้าง Coupon system:
- Table: coupons (code, type, value, max_uses, expires_at)
- Validate coupon: check code, expiry, usage limit
- Apply coupon: ลดราคาตาม type (percent/fixed)
- Track usage: บันทึกว่า user ไหนใช้
Tech: Next.js + Prisma + PostgreSQL
ทำ step by step รอ approve
| Day 2: Testing |
|---|
# หลัง implement เสร็จ
> เขียน comprehensive tests:
- Unit: validateCoupon, calculateDiscount
- Integration: POST /api/coupons/apply
- Edge cases: หมดอายุ, หมด uses, coupon ซ้ำ
> เขียน E2E test:
- User กรอก coupon code → เห็นราคาลด → checkout
# รัน tests
npm test
# ถ้า fail ไม่แก้เอง ส่ง error ให้ Claude
> test ที่ fail คือ:
[paste error]
แก้ให้ผ่านโดยไม่แก้ test logic
| Day 3: Security + Performance |
|---|
# Security audit
> /project:security-audit
# Performance benchmark
> benchmark validateCoupon function:
- 100 calls consecutive
- 50 concurrent calls
- Database query analysis
# พบว่า lookup query ช้า 200ms
> เพิ่ม index บน coupons.code
และ coupon_usages.coupon_id + user_id
# benchmark อีกครั้งหลัง index
# ได้ 8ms (เร็วขึ้น 25x)
| Day 4: QA + Staging Deploy |
|---|
# สร้าง QA test plan
> สร้าง test plan สำหรับ coupon feature
ครอบคลุม functional, negative, boundary, UX
# Deploy to staging
git push origin feature/coupon-system
# Vercel สร้าง preview URL อัตโนมัติ
# รัน regression tests บน staging
npx playwright test tests/e2e/ --project=chromium \
--config=playwright.staging.config.ts
# Deploy check
> /project:deploy-check
| Day 5: Production + Monitoring |
|---|
# Canary deploy: 10% ของ users ก่อน
# ตั้ง feature flag: coupon_system = 10%
# Monitor 24 ชั่วโมง
# - Error rate ปกติ
# - Coupon apply success rate > 99%
# - ไม่มี double-apply bugs
# ถ้าผ่าน → 100% users
# ตั้ง feature flag: coupon_system = 100%
| Day 14: A/B Test ผล |
|---|
# หลังจาก coupon system stable 1 สัปดาห์
# A/B test: "First-time coupon" popup
> วิเคราะห์ผล A/B test:
Control: ไม่มี popup
- Conversion rate: 8.2%
- Average order value: ฿850
Variant: Popup แจก 10% coupon สำหรับ new user
- Conversion rate: 11.5% (+40%!)
- Average order value: ฿780 (-8%)
- Net revenue per user: +29%
p-value: 0.001 (statistically significant)
Confidence: 99.9%
Decision: Ship variant ทั้ง 100%
Next test: ทดลอง 15% vs 10% coupon
คำศัพท์ประจำบท
| Quality Gate | เกณฑ์คุณภาพที่โค้ดต้องผ่านก่อนจะไปขั้นตอนถัดไปได้ |
|---|---|
| Testing Pyramid | หลักการแบ่งสัดส่วน tests: Unit (70%) Integration (20%) E2E (10%) |
| Integration Test | ทดสอบที่ component หลายตัวทำงานร่วมกัน รวมถึง database จริง |
| E2E Test | End-to-End ทดสอบทั้งระบบจาก browser จริง เหมือน user จริง |
| Core Web Vitals | ชุด metrics ที่ Google ใช้วัด UX: LCP, INP, CLS |
| Load Testing | ทดสอบระบบด้วย traffic จำนวนมากเพื่อหา bottleneck |
| k6 | เครื่องมือ Load testing แบบ code-first ยอดนิยม |
| OWASP Top 10 | รายการช่องโหว่ความปลอดภัยเว็บที่พบบ่อยที่สุด 10 อันดับ |
| IDOR | Insecure Direct Object Reference ช่องโหว่ที่ user เข้าถึงข้อมูลคนอื่น |
| Blue-Green Deploy | Deploy strategy ที่มี 2 environment สลับกัน rollback ได้ทันที |
| Canary Release | ปล่อย feature ให้ user บางส่วนก่อน ค่อยขยายถ้าไม่มีปัญหา |
| Feature Flag | Switch เปิด/ปิด feature โดยไม่ต้อง deploy code ใหม่ |
| A/B Testing | ทดสอบ 2 version พร้อมกันเพื่อวัดว่า version ไหนดีกว่าจาก data จริง |
| Statistical Significance | ความมั่นใจทางสถิติว่าผลที่เห็นไม่ได้เกิดจากโชค |
| P95/P99 | Percentile 95/99 เวลา response ที่ช้า 5%/1% ของ requests |
| Root Cause Analysis (RCA) | กระบวนการหา “ทำไม” จริงๆ ไม่ใช่แค่แก้ symptom |
| Post-mortem | เอกสารวิเคราะห์ incident หลังผ่านไป เพื่อป้องกันซ้ำ |
| Regression Test | ทดสอบว่า feature เก่ายังทำงานได้หลัง deploy ใหม่ |
| Memory Leak | การที่ program ใช้ memory มากขึ้นเรื่อยๆ โดยไม่คืน |