Skip to content

16.1 หลักการและกลยุทธ์

บทนี้อธิบายหลักการที่ใช้ตัดสินใจในทุก Wave การเปลี่ยนแปลงแต่ละขั้นต้องสอดคล้องกับหลักการเหล่านี้ ถ้าขัดแย้ง ให้หยุดและตัดสินใจใหม่

หลักการที่ 1 — Strangler pattern ไม่ใช่ big-bang

นิยาม

Strangler pattern (ชื่อเต็ม Strangler Fig Application) เป็นวิธี migration ที่ค่อย ๆ แทนที่ระบบเดิมทีละส่วน แทนที่จะเขียนใหม่ทั้งหมดแล้วสลับในครั้งเดียว

เปรียบเทียบ

วิธีระยะเวลาความเสี่ยงผลกระทบผู้ใช้ความเป็นไปได้ที่จะล้มเหลว
Big-bang rewrite12-18 เดือนสูงมากสูง (downtime ตอน cutover)สูง (โครงการหลายแห่งล้มเหลว)
Strangler pattern18-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 ใดทำก่อน

  1. Self-contained — ไม่พึ่ง context อื่นมาก
  2. ผลกระทบสูง — แก้ debt Critical หรือ High ได้หลายตัว
  3. ขอบเขตชัด — สามารถระบุ input/output ได้ชัดเจน
  4. ทดสอบได้ — มีวิธี verify ว่า service ใหม่ให้ผลเหมือนเดิม

หลักการที่ 2 — Bounded context เป็นขอบเขตการแยก

นิยาม

Bounded context มาจาก Domain-Driven Design (DDD) คือขอบเขตที่ model ของ domain มีความหมายความหมายเดียวกัน ใน VTRC ตัวอย่างเช่น:

  • identity — user, role, session, device key
  • payroll — slip, tax, salary
  • leave — leave request, approval, balance
  • welfare — hospital claim, approval chain

หลีกเลี่ยงการแยกตามชนิดของเทคนิค

แยกที่ผิดแยกที่ถูก
pdf-service, excel-servicedocument-renderer (ซึ่ง render ทั้ง PDF และ Excel)
user-service, auth-serviceidentity (รวม user, role, session, device key)
read-api, write-apibounded context หนึ่งรับผิดชอบทั้ง read และ write ของ domain นั้น

หลีกเลี่ยงการแยกที่เล็กเกินไป

ถ้า context แยกแล้วมี entity เพียง 1-2 ตัว ไม่คุ้มค่าการ maintain service แยก — ควรรวมกับ context ใกล้เคียง

หลักการที่ 3 — Reversibility

นิยาม

ทุกขั้นตอนของ modernization ต้องสามารถย้อนกลับได้ ถ้าเกิดปัญหาใน production ทีมต้องสามารถ rollback ได้ภายใน 30 นาที

วิธีทำให้ reversible

  1. Feature flag — ทุกการเปลี่ยนแปลงที่กระทบ request flow ต้องผ่าน flag
  2. Dual-write — ถ้า migrate schema, เขียนข้อมูลทั้ง schema เก่าและใหม่ในช่วง transition
  3. Backward-compatible API — API เก่าต้องยังทำงานได้จนกว่า client ทุกตัวย้าย
  4. 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
Databasemigration schema ทีเดียวexpand-then-contract
Frontendrewrite ทั้งหน้าค่อย ๆ ย้าย component
Dependencyupgrade ทุกตัวในเวลาเดียวกันทีละตัว, ทดสอบแต่ละตัว

หลักการที่ 5 — Optimize สำหรับ decision velocity

นิยาม

Modernization ที่ดีต้องทำให้ทีมตัดสินใจเร็วขึ้น ไม่ช้าลง

วิธีทำ

  1. ลด coupling — ถ้า context แยกกัน, ทีมแต่ละกลุ่มตัดสินใจได้โดยไม่ต้องประสานทุกอย่าง
  2. Clear interface — ถ้า interface ชัดเจน, ทีมเปลี่ยน implementation ภายในได้โดยไม่กระทบ context อื่น
  3. 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 เดินไปในทิศทางที่ถูกต้อง:

  1. Strangler pattern ไม่ใช่ big-bang
  2. Bounded context เป็นขอบเขต
  3. Reversibility
  4. No-big-bang ในทุกระดับ
  5. Decision velocity
  6. Observability ก่อน
  7. วัดผล

บทถัดไปจะเริ่มลงรายละเอียดของ Wave 0 — stop-the-bleed