16.1 หลักการและกลยุทธ์
บทนี้อธิบายหลักการที่ใช้ตัดสินใจในทุก Wave การเปลี่ยนแปลงแต่ละขั้นต้องสอดคล้องกับหลักการเหล่านี้ ถ้าขัดแย้ง ให้หยุดและตัดสินใจใหม่
หลักการที่ 1 — Strangler pattern ไม่ใช่ big-bang
นิยาม
Strangler pattern (ชื่อเต็ม Strangler Fig Application) เป็นวิธี migration ที่ค่อย ๆ แทนที่ระบบเดิมทีละส่วน แทนที่จะเขียนใหม่ทั้งหมดแล้วสลับในครั้งเดียว
เปรียบเทียบ
| วิธี | ระยะเวลา | ความเสี่ยง | ผลกระทบผู้ใช้ | ความเป็นไปได้ที่จะล้มเหลว |
|---|---|---|---|---|
| Big-bang rewrite | 12-18 เดือน | สูงมาก | สูง (downtime ตอน cutover) | สูง (โครงการหลายแห่งล้มเหลว) |
| Strangler pattern | 18-24 เดือน | ต่ำ-ปานกลาง | ต่ำ (ค่อย ๆ เปลี่ยน) | ต่ำ (fail ทีละส่วน ไม่ใช่ทั้งหมด) |
วิธีใช้ใน VTRC
┌─────────────────────────┐
│ vtrc-api (monolith) │
│ │
request ────────►│ /api/graphql │
│ │
└─────────────────────────┘
หลัง Wave 2 เริ่ม (ตัวอย่าง — document-renderer):
┌─────────────────────────┐
│ vtrc-api (เล็กลง) │
│ │
request ────────►│ /api/graphql │
│ │ │
│ └─► feature flag │
│ │ │
└───────────┼─────────────┘
│
┌───────────▼─────────────┐
│ document-renderer (Go) │
│ /render/pdf │
└─────────────────────────┘router ใน vtrc-api ตรวจ feature flag — ถ้าเปิด ส่ง request ไป service ใหม่ ถ้าปิด ใช้ code เดิม ค่อย ๆ เพิ่มสัดส่วน traffic ไป service ใหม่
เกณฑ์ตัดสินใจว่า context ใดทำก่อน
- Self-contained — ไม่พึ่ง context อื่นมาก
- ผลกระทบสูง — แก้ debt Critical หรือ High ได้หลายตัว
- ขอบเขตชัด — สามารถระบุ input/output ได้ชัดเจน
- ทดสอบได้ — มีวิธี verify ว่า service ใหม่ให้ผลเหมือนเดิม
หลักการที่ 2 — Bounded context เป็นขอบเขตการแยก
นิยาม
Bounded context มาจาก Domain-Driven Design (DDD) คือขอบเขตที่ model ของ domain มีความหมายความหมายเดียวกัน ใน VTRC ตัวอย่างเช่น:
identity— user, role, session, device keypayroll— slip, tax, salaryleave— leave request, approval, balancewelfare— hospital claim, approval chain
หลีกเลี่ยงการแยกตามชนิดของเทคนิค
| แยกที่ผิด | แยกที่ถูก |
|---|---|
pdf-service, excel-service | document-renderer (ซึ่ง render ทั้ง PDF และ Excel) |
user-service, auth-service | identity (รวม user, role, session, device key) |
read-api, write-api | bounded context หนึ่งรับผิดชอบทั้ง read และ write ของ domain นั้น |
หลีกเลี่ยงการแยกที่เล็กเกินไป
ถ้า context แยกแล้วมี entity เพียง 1-2 ตัว ไม่คุ้มค่าการ maintain service แยก — ควรรวมกับ context ใกล้เคียง
หลักการที่ 3 — Reversibility
นิยาม
ทุกขั้นตอนของ modernization ต้องสามารถย้อนกลับได้ ถ้าเกิดปัญหาใน production ทีมต้องสามารถ rollback ได้ภายใน 30 นาที
วิธีทำให้ reversible
- Feature flag — ทุกการเปลี่ยนแปลงที่กระทบ request flow ต้องผ่าน flag
- Dual-write — ถ้า migrate schema, เขียนข้อมูลทั้ง schema เก่าและใหม่ในช่วง transition
- Backward-compatible API — API เก่าต้องยังทำงานได้จนกว่า client ทุกตัวย้าย
- Database migration แบบ expand-then-contract:
- Phase 1 (expand): เพิ่ม schema ใหม่โดยไม่ลบเก่า
- Phase 2 (migrate): ค่อย ๆ ย้ายข้อมูลและ code
- Phase 3 (contract): ลบ schema เก่าเมื่อไม่ใช้
เกณฑ์ "reversible enough"
ก่อน deploy การเปลี่ยนแปลงใด ถามว่า:
- ถ้า deploy แล้วระบบพัง จะ rollback อย่างไร?
- rollback ใช้เวลานานเท่าไหร่?
- ข้อมูลที่ถูกเขียนระหว่างที่ใช้ version ใหม่จะเสียหรือไม่?
ถ้าตอบไม่ได้ ห้าม deploy
หลักการที่ 4 — No-big-bang ในทุกระดับ
หลักการนี้ใช้ไม่เฉพาะที่ระดับระบบ แต่ทุกระดับ:
| ระดับ | หลีกเลี่ยง | ทำแทน |
|---|---|---|
| ระบบ | rewrite ทั้ง monolith | แยกทีละ context |
| Service | แยกทุก endpoint ในครั้งเดียว | ค่อย ๆ ย้ายทีละ endpoint |
| Database | migration schema ทีเดียว | expand-then-contract |
| Frontend | rewrite ทั้งหน้า | ค่อย ๆ ย้าย component |
| Dependency | upgrade ทุกตัวในเวลาเดียวกัน | ทีละตัว, ทดสอบแต่ละตัว |
หลักการที่ 5 — Optimize สำหรับ decision velocity
นิยาม
Modernization ที่ดีต้องทำให้ทีมตัดสินใจเร็วขึ้น ไม่ช้าลง
วิธีทำ
- ลด coupling — ถ้า context แยกกัน, ทีมแต่ละกลุ่มตัดสินใจได้โดยไม่ต้องประสานทุกอย่าง
- Clear interface — ถ้า interface ชัดเจน, ทีมเปลี่ยน implementation ภายในได้โดยไม่กระทบ context อื่น
- Test as safety net — ถ้ามี test ครอบคลุม, ทีมกล้าแก้โดยไม่กลัวพัง
สิ่งที่ทำให้ decision velocity ต่ำ
- monolith ที่ทุกอย่างพึ่งกัน
- ไม่มี test
- กระบวนการ approval ที่ยาว
- ไม่มีเอกสาร architecture
หลักการที่ 6 — Observability ก่อน complexity
นิยาม
ก่อนที่จะเพิ่ม feature หรือเปลี่ยน architecture ระบบต้อง "มองเห็น" ได้ก่อน
ลำดับความสำคัญ
1. Observability ก่อน
├── logging
├── metrics
├── tracing
└── alerting
2. แล้วค่อย
├── refactor
├── migration
└── new featureถ้า migrate โดยที่ระบบไม่ observable, เมื่อเกิดปัญหาจะ debug ไม่ได้
หลักการที่ 7 — Modernization ต้องวัดผล
KPI ที่ต้อง track ทุก sprint
| KPI | ทำไมสำคัญ |
|---|---|
| Critical/High debt count | วัดความคืบหน้าของ modernization |
| Deploy frequency | วัดความเร็วของทีม |
| MTTR (Mean Time To Recover) | วัดความสามารถในการดูแล |
| Change failure rate | วัดคุณภาพการ deploy |
| Uptime | วัดความพร้อมใช้งาน |
| Lead time (จาก commit → deploy) | วัดความคล่องตัว |
หลีกเลี่ยง vanity metric
| Vanity metric (หลีกเลี่ยง) | Actionable metric (ใช้แทน) |
|---|---|
| "เราทำ migration ไปแล้ว 50%" | "service ใหม่รับ 30% ของ production traffic แล้ว" |
| "เราลด debt ได้ 20 รายการ" | "Critical debt เหลือ 0 รายการ" |
| "เรา deploy 100 ครั้ง/เดือน" | "change failure rate < 5%" |
สรุป
หลักการทั้ง 7 ข้อนี้เป็น "raails" ที่ทำให้ modernization เดินไปในทิศทางที่ถูกต้อง:
- Strangler pattern ไม่ใช่ big-bang
- Bounded context เป็นขอบเขต
- Reversibility
- No-big-bang ในทุกระดับ
- Decision velocity
- Observability ก่อน
- วัดผล
บทถัดไปจะเริ่มลงรายละเอียดของ Wave 0 — stop-the-bleed