บทที่ 17 · Part 5 — Decisions & Delivery
Trade-offs & Decisions
Frame สำหรับเทียบทางเลือกในห้องประชุม, CAP/PACELC ตามความจริง, cost เป็น design axis, build-vs-buy และ ADR
ใครก็ลิสต์ทางเลือกได้ งานของ architect คือ เลือก, อธิบายการเลือกด้วยภาษาที่ธุรกิจเข้าใจ, และ ทิ้งบันทึกไว้ให้ทีมในอนาคตประเมินใหม่ได้ บทนี้คือวินัยของการเลือก
จบบทนี้คุณจะ
- ใช้ frame 5 ขั้นเทียบทางเลือกบนกระดานได้ใน 20 นาที
- พูดเรื่อง CAP/PACELC ได้ถูกต้อง โดยไม่ตกหลุมที่คนส่วนใหญ่ตก
- สร้าง cost model หยาบๆ ได้ใน 1 ชั่วโมง และรู้ว่า cost per transaction เปลี่ยนบทสนทนาอย่างไร
- แยก one-way door ออกจาก two-way door และจัดสรรเวลารีวิวให้ถูก
- เขียน ADR 1 หน้าที่ใช้ได้จริง
- จับ anti-pattern 10 ข้อได้จากประโยคที่คนพูด
[!TIP] ไม่มี best practice มีแต่ trade-off ในบริบท "Microservices เป็น best practice" ไม่ใช่คำกล่าวเกี่ยวกับสถาปัตยกรรม แต่เป็นคำกล่าวเกี่ยวกับงานที่แล้วของใครคนหนึ่ง
ทุกคุณสมบัติที่คุณได้มา คุณจ่ายด้วยอีกอย่าง — consistency จ่ายด้วย availability, ความยืดหยุ่นจ่ายด้วยความเรียบง่าย, performance จ่ายด้วยเงิน, การ decouple จ่ายด้วยความสามารถในการตามรอย, redundancy จ่ายด้วย consistency
เวลาใครพูดคำว่า "best practice" คำถามที่มีประโยชน์คือ "best สำหรับ quality attribute ตัวไหน และทีมที่ทำแบบนั้นจ่ายอะไรไป"
Frame 5 ขั้นที่ใช้ได้ในห้องประชุม
- ตั้งการตัดสินใจเป็นคำถามที่แคบ — ไม่ใช่ "ควรใช้ฐานข้อมูลอะไร" แต่เป็น "เก็บ transfer history ที่ไหน ให้ pattern #2 (list 20 รายการล่าสุดต่อ account, 100/s, p99 150 ms) และ pattern #7 (ประวัติ 7 ปี, 50/วัน) ถูก serve ทั้งคู่"
- ลิสต์เกณฑ์ที่สำคัญกับเคสนี้ และให้น้ำหนัก — ก่อนดูทางเลือก การให้น้ำหนักหลังจากมีตัวโปรดแล้วคือการหาเหตุผลมาสนับสนุน ไม่ใช่การตัดสินใจ
- ลิสต์ 3 ทางเลือก และหนึ่งในนั้นต้องเป็น "สิ่งที่ง่ายที่สุด" (ไม่ทำอะไร หรือขยายของที่มีอยู่) — ถ้าทางเลือกที่ง่ายไม่อยู่ในตาราง การเทียบนั้นไม่ซื่อสัตย์
- ให้คะแนน แล้วเขียนเหตุผล 1 บรรทัดต่อช่อง — เหตุผลคือผลลัพธ์จริง ตัวเลขเป็นแค่วิธีทำให้เห็นรูปร่าง
- เขียนไว้ว่าอะไรจะทำให้เปลี่ยนการตัดสินใจ — "ถ้า write volume เกิน 5,000/s ให้ทบทวน" ข้อนี้เปลี่ยนการตัดสินใจให้เป็นสิ่งที่ monitor ได้ และทำให้การทบทวน เป็นเรื่องปกติ ไม่ใช่การยอมรับความล้มเหลว
ตัวอย่างเดินจริง: เก็บ transfer history ที่ไหน
| เกณฑ์ (น้ำหนัก) | A · Postgres เดิม partition ตามเดือน | B · Postgres hot + archive บน object storage | C · Cassandra สำหรับประวัติทั้งหมด |
|---|---|---|---|
| Serve pattern #2 (w3) | 3 index บน (account_id, created_at) | 3 เหมือนกันสำหรับ hot data | 3 partition key ตรงตามธรรมชาติ |
| Serve pattern #7 (w1) | 2 ได้ แต่ทำให้ OLTP box บวม | 3 นี่คือสิ่งที่ cold storage มีไว้ทำ | 3 ได้สบาย |
| อยู่ใน transaction เดียวกับ ledger (w3) | 3 transaction เดียวกัน ฟรี | 3 hot path ไม่เปลี่ยน | 0 ต้องมี saga หรือ dual write |
| ต้นทุน operational (w2) | 3 ไม่มีอะไรใหม่ต้องรัน | 2 archive job 1 ตัว + เส้นทาง query | 1 cluster ใหม่, ทักษะใหม่, on-call ใหม่ |
| ค่า infra ที่ 5 ปี (w2) | 1 8 TB บน premium storage | 3 ถูกกว่าราว 100 เท่าสำหรับ cold | 2 ถูกกว่าต่อ byte แต่ node มากกว่า |
| Headroom ถึง 20× (w1) | 1 จะต้อง shard | 2 เหมือนกัน แต่ช้ากว่า | 3 ออกแบบมาเพื่อสิ่งนี้ |
| รวมถ่วงน้ำหนัก | 28 | 33 | 22 |
วิธีนำเสนอผลลัพธ์นั้น
"เราเลือก B: เก็บ 90 วันใน Postgres และ archive ข้อมูลที่เก่ากว่านั้นลง object storage เป็น Parquet พร้อมเส้นทาง query แบบ on-demand มัน serve access pattern ได้ทั้ง 2 อัน, รักษาการเขียน ledger ไว้ใน transaction เดียว, และถูกกว่าราว 100 เท่าสำหรับภาระ retention 7 ปี
เรายอมรับว่า query ข้อมูลที่ archive แล้วใช้เวลาระดับวินาทีไม่ใช่มิลลิวินาที ซึ่งตรงกับ SLA ที่ระบุไว้สำหรับ pattern นั้น
เราจะทบทวนถ้าปริมาณ transfer เกิน 5,000/s ต่อเนื่อง หรือถ้าทีม ops ต้องเข้าถึงข้อมูลเก่าในระดับต่ำกว่าวินาที
Cassandra ได้คะแนนต่ำสุดเพราะมันจะทำลายการเขียน ledger แบบ atomic — นั่นคือต้นทุนด้านความถูกต้องที่เราไม่ยอมจ่ายเพื่อ scale ที่เรายังไม่มี"
สังเกตว่าย่อหน้านั้นทำ 4 อย่าง: บอกสิ่งที่เลือก, เหตุผล, ต้นทุนที่ยอมรับ, trigger ที่จะทบทวน, และ ทำไมตัวที่แพ้จึงแพ้ — 4 ประโยค นั่นคือ deliverable
CAP, PACELC และสิ่งที่เกิดขึ้นจริง
CAP: เมื่อ network เกิด partition (P) คุณต้องเลือกระหว่าง consistency (C) กับ availability (A) — นั่นคือทั้งทฤษฎีบท และมันถูกใช้ผิดบ่อยมาก
สิ่งที่ CAP **ไม่ได้** พูด
- ไม่ได้พูดว่า "เลือก 2 จาก 3" — partition ไม่ใช่ทางเลือก เพราะ network มันล้มเอง คุณกำลังเลือกระหว่าง C กับ A ตอนที่เกิด partition เท่านั้น
- C ใน CAP คือ linearizability ไม่ใช่ C ใน ACID — คนละแนวคิดที่ใช้ตัวอักษรเดียวกัน
- ไม่ได้พูดอะไรเลยเกี่ยวกับ latency ตอนทำงานปกติ — ซึ่งเป็นสถานะที่ระบบของคุณอยู่ 99.99% ของชีวิต นั่นคือช่องว่างที่ PACELC มาเติม
- มันเป็นเรื่องต่อ operation ไม่ใช่ต่อระบบ — ฐานข้อมูลตัวเดียวกัน serve การอ่านยอดเงินแบบ linearizable และการอ่าน feed แบบ eventual ได้พร้อมกัน การพูดว่า "ระบบเราเป็น AP" มักเป็นสัญญาณว่าไม่มีใครดูให้ละเอียด
PACELC เป็นสูตรที่มีประโยชน์กว่า: ถ้ามี Partition ให้เลือก A หรือ C; Else (ตอนทำงานปกติ) ให้เลือก Latency หรือ Consistency
| ระบบ | พฤติกรรมตอน partition | พฤติกรรมตอนทำงานปกติ |
|---|---|---|
| Relational DB แบบ single-primary | PC — write หยุดแทนที่จะแยกทางกัน | EC — synchronous commit รอ replica |
Cassandra ที่ QUORUM | PA/PC ปรับได้ด้วย consistency level | EL — แลก consistency กับ latency ได้ต่อ query |
| DynamoDB | PC สำหรับ strongly consistent read, PA สำหรับ eventual | EL — eventual read ถูกกว่าและเร็วกว่า |
| Google Spanner | PC — ปฏิเสธแทนที่จะแยกทางกัน | EC — จ่าย latency จริงเพื่อ global consistency (TrueTime) |
อ้างอิง: CAP Twelve Years Later — Eric Brewer อธิบายเองว่าทฤษฎีบทของเขาถูกอ่านผิดอย่างไร · PACELC ของ Daniel Abadi
ตาราง trade-off ที่คุณจะใช้บ่อยที่สุด
| ถ้าคุณต้องการ… | คุณจะจ่ายด้วย… | ทางออกที่ใช้กันบ่อย |
|---|---|---|
| Strong consistency | latency และ availability ตอน partition | ทำให้เฉพาะ decision path เป็น strong และให้ display path เป็น eventual |
| Read latency ต่ำ | ความเก่าของข้อมูล | ระบุ ขอบเขตความเก่าไว้ใน SLO บวก read-your-writes ให้ actor |
| Write throughput สูง | ความยืดหยุ่นของ query, join, บางทีรวมถึง ordering | เขียนลง append store ที่เร็ว แล้วสร้าง read model แบบ async |
| ความเป็นอิสระของ service | ความสามารถตามรอย และการเสีย database transaction | outbox + saga + correlation ID + ที่เดียวที่เห็น flow ทั้งหมด |
| ไม่ให้ข้อมูลหาย (RPO 0) | write latency (synchronous replication) | ยอมรับมัน — สำหรับเงินไม่มีทางออก และไม่ควรไปหา |
| ส่งของเร็ว | design debt ที่สะสม | เรียกชื่อหนี้นั้นออกมาพร้อม trigger ที่จะจ่ายคืน ไม่ใช่ "เดี๋ยวมาเก็บ" |
| ต้นทุนต่ำลง | headroom และวันหยุดของใครคนหนึ่ง | right-size ตาม พีคที่วัดได้ ไม่ใช่ตามความกลัว |
| ชิ้นส่วนน้อยลง | เพดานการ scale บางอย่าง | มักเป็นการแลกที่ถูกต้อง — เอาไปเลย แล้วทบทวนที่ trigger ที่บันทึกไว้ |
Cost เป็น design axis ระดับหนึ่ง
Architect ที่คุยเรื่องต้นทุนไม่ได้ คือ architect ที่ธุรกิจเลิกฟัง
เงินบน cloud ไปอยู่ที่ไหนจริงๆ
| หมวด | ความจริงที่มักเกิด |
|---|---|
| Compute | มักเป็นบรรทัดใหญ่สุด และมัก over-provision 3–10 เท่าเพราะไม่มีใครวัด · right-size ตาม p95 utilization ที่วัดได้, autoscaling ที่มีพื้นสมเหตุสมผล, committed-use discount สำหรับ baseline ที่นิ่ง |
| Managed database | แพงต่อ GB และต่อ IOPS · การลดปริมาณข้อมูล (archive) และลดจำนวน index มักประหยัดได้มากกว่าการลดขนาด instance |
| Network egress | บรรทัดที่ทำให้ทุกคนตกใจ — ข้อมูลที่ออกจาก cloud และมักรวมถึงข้อมูลที่ข้าม zone/region ถูกคิดเงิน · service mesh ที่คุยกันข้าม AZ เยอะอาจแพงกว่า compute ที่มันประสานงาน |
| Observability | log และ metric ingestion ถูกมิเตอร์ · debug log ที่ละเอียดในระดับ scale แพงกว่า application ได้จริง · sample, structure, ตั้ง retention ต่อ stream, ทิ้งสิ่งที่ไม่มีใคร query |
| Non-production ที่เปิดทิ้ง | dev/test/staging รัน 24/7 ให้ทีมที่ทำงาน 40 ชั่วโมงต่อสัปดาห์ — scheduled shutdown คือเงินฟรี |
| คน | ต้นทุนที่ใหญ่ที่สุดในแทบทุกระบบ · สถาปัตยกรรมที่ต้องมีผู้เชี่ยวชาญ 3 คนดูแลแพงกว่าอันที่ไม่ต้องมีเลย แม้ใบแจ้งหนี้จะน้อยกว่า — พูดข้อนี้ออกมาดังๆ ในห้องรีวิว มันคือข้อโต้แย้งที่ชนะความซับซ้อนที่ไม่จำเป็น |
Unit economics — ตัวเลขที่ทำให้คุณน่าเชื่อถือ
คำนวณ COST PER BUSINESS EVENT แล้วติดตามมันตามเวลา
ค่า infra ต่อเดือน / จำนวน transaction ต่อเดือน = ต้นทุนต่อ transaction
ตัวอย่าง: infra 300,000 บาท/เดือน, 15,000,000 transaction
= 0.02 บาท ต่อ transaction
ตอนนี้ทุกข้อเสนอเชิง design ประเมินได้ในหน่วยเดียวกัน:
- "cache ตัวนี้ลดขนาด DB และประหยัดราว 0.003 บาท/txn ที่ปริมาณปัจจุบัน"
- "fraud service ใหม่เพิ่มราว 0.008 บาท/txn คุ้มกับ fraud ที่ลดลงที่คาดไว้ไหม"
<-- คำถามที่ฝ่ายธุรกิจตอบได้
- "ต้นทุนต่อ transaction เพิ่ม 40% ไตรมาสนี้ ขณะที่ปริมาณเพิ่ม 10%"
<-- นั่นคือปัญหาเชิงสถาปัตยกรรม และตอนนี้มันมองเห็นได้
Metric ตัวเดียวนี้เปลี่ยนวิธีที่ผู้บริหารมองงานสถาปัตยกรรม
จาก cost centre ที่มาขอเงิน เป็น function ที่บริหารตัวเลขที่พวกเขาสนใจ — สร้าง dashboard นี้
Build vs buy vs partner
| พิจารณา | Build | Buy / SaaS | Partner (ธนาคารหรือผู้ให้บริการที่มีใบอนุญาต) |
|---|---|---|---|
| เหมาะเมื่อ | มันคือสิ่งที่ทำให้คุณต่าง หรือไม่มี product ไหนเข้ากับ constraint ของคุณ | เป็นปัญหา commodity ที่แก้แล้ว (auth, email, SMS, monitoring, feature flag) | ความสามารถนั้นต้องมีใบอนุญาต, สมาชิกภาพใน scheme, หรือสถานะทางกฎหมายที่คุณไม่มี |
| ต้นทุนจริง | build + operate + on-call + upgrade ตลอดไป — ประมาณเป็น 3 เท่าของที่คุณประเมินไว้ | ค่า license + integration + ต้นทุนของ outage ของเขา + ต้นทุนการย้ายในอนาคต | ค่าธรรมเนียม + integration + change window ของเขา + roadmap ของเขากลายเป็นของคุณ |
| ความเสี่ยงหลัก | opportunity cost — คุณไม่ได้สร้าง product | lock-in; data residency; SLA ของเขาไม่ใช่ SLO ของคุณ; เขาถูกซื้อกิจการ | concentration risk; อำนาจต่อรองจำกัด; คุณรับ availability ของเขามาเป็นของคุณ |
| การบรรเทา | timebox; ทำให้เล็ก; ซื่อสัตย์ว่าใครจะ maintain มันในปีที่ 3 | ห่อไว้หลัง interface ของคุณเอง (adapter) ให้เปลี่ยนได้; เป็นเจ้าของการ export ข้อมูลของตัวเอง | ออกแบบเผื่อ 2 provider ตั้งแต่วันแรก แม้จะเปิดตัวด้วยรายเดียว — abstraction เหนือ rail ราคาถูกวันนี้ และมีค่ามหาศาลวันหน้า |
Third-party dependency ในธุรกิจที่ถูกกำกับดูแลคือสถาปัตยกรรม **และ** governance
เมื่อคุณพึ่งผู้ให้บริการภายนอกสำหรับความสามารถที่ลูกค้าเห็น design ต้องตอบได้ว่า:
- availability ตามสัญญาของเขาเท่าไหร่ และมันมีผลต่อ SLO ของเราอย่างไร
- ระหว่าง maintenance window ของเขา เราจะเข้าคิว, degrade, หรือปฏิเสธ
- failover ไป provider รายที่ 2 คืออะไร และเคยทดสอบแล้วหรือยัง
- ข้อมูลอะไรออกจากสภาพแวดล้อมของเรา ภายใต้ข้อตกลงอะไร
- ใครได้รับแจ้งเมื่อเขามี incident และเรารู้ได้อย่างไร (status page ที่ต้องจำไปเปิดดูเองไม่ใช่กลไกตรวจจับ — subscribe แบบ programmatic)
การเลือกหรือเปลี่ยน provider ที่แตะเงินหรือข้อมูลลูกค้าไม่ใช่การตัดสินใจของฝ่ายวิศวกรรมเพียงลำพัง เตรียมการประเมินทางเทคนิคและทางเลือกไว้ ส่วนการผูกพันต้องมีเจ้าของที่รับผิดชอบ (procurement, risk, compliance, legal) — เขียนข้อนี้ไว้ใน design doc ไม่ใช่สมมติว่ามีคนอื่นจัดการแล้ว
One-way door และ two-way door
| Two-way door (ย้อนกลับได้) | One-way door (ย้อนยาก) | |
|---|---|---|
| ตัวอย่าง | การเลือก library, ขอบเขต service ภายใน, cache TTL, API ภายใน, instance type | public API contract, event schema ที่ consumer พึ่ง, partition key, รูปแบบ ID, การแทนค่าเงิน/จำนวน, สัญญา vendor, อะไรที่เก็บข้อมูลลูกค้า |
| กระบวนการ | ตัดสินเร็วในทีม ลองเลย วัดผล ผิดก็เปลี่ยน | ช้าลง เขียนเป็นเอกสาร ให้คนที่จะอยู่กับมันรีวิว ทำ prototype ถ้าทำได้ |
| Failure mode | analysis paralysis กับเรื่องที่ไม่สำคัญ | การตัดสินใจ 30 นาทีที่มีต้นทุน 2 ปี |
ทีมส่วนใหญ่ทำกลับกันเป๊ะ
เถียงเรื่อง framework เป็นสัปดาห์ (two-way) แต่ใช้ 15 นาทีกับ event schema ที่ consumer 11 รายจะพึ่งพาไป 5 ปี (one-way)
การแทรกแซงที่มีค่าที่สุดของ architect บ่อยครั้งคือแค่พูดออกมาดังๆ ว่า "อันนี้เป็น one-way door เรามาใช้เวลากับมันจริงๆ กันเถอะ"
ADR — 1 หน้า ถาวร
# ADR-014: ใช้ immutable double-entry ledger สำหรับยอดเงิน wallet
Status: Accepted (Proposed | Accepted | Superseded by ADR-nnn)
Date: 2026-08-07
Deciders: <ชื่อ/ตำแหน่ง> Consulted: <finance, risk>
## Context
ยอดเงิน wallet ปัจจุบันเป็นคอลัมน์ที่แก้ได้ ใน 6 เดือนที่ผ่านมามี 3 incident
ที่อธิบายยอดเงินไม่ได้ และไม่มีทางไล่ที่มาว่ามันมาถึงตัวเลขนั้นได้อย่างไร
Internal audit ขอให้แสดงที่มาของยอดใดก็ได้ ปริมาณ ~500k txn/วัน
คาดว่าจะเป็น 2M/วัน ภายใน 18 เดือน
## Decision
เก็บการเคลื่อนย้ายเงินทั้งหมดเป็น immutable double-entry ledger entry
ที่รวมกันได้ 0 ต่อ transaction · derive ยอดเงิน · มี snapshot table
พร้อม watermark สำหรับ read performance · บังคับ unique idempotency key
ต่อ transaction ที่ระดับฐานข้อมูล
## Consequences
+ ยอดเงินใดก็อธิบายและตรวจสอบได้ครบ
+ การแก้ไขกลายเป็น reversal entry ที่มองเห็นได้ (ตอบข้อกำหนด audit)
+ กำจัด write contention บนแถวเดียวของ account ที่ร้อน
+ zero-sum monitor แบบต่อเนื่องตรวจจับ bug ได้ทั้งคลาสตั้งแต่ต้น
- การอ่านยอดกลายเป็น snapshot + delta ไม่ใช่การอ่านคอลัมน์เดียว
- storage โตราว 5 เท่าต่อ transaction (วัดได้: ~2 KB เทียบ ~0.4 KB)
- ต้อง migrate ยอดเงินเดิม โดยมี opening-balance entry ต่อ account
ต้องมี finance sign-off กับตัวเลขตอน cutover
## Options considered
1. คงคอลัมน์ที่แก้ได้ไว้ แล้วเพิ่มตาราง audit log
ปฏิเสธ: audit log กับยอดเงินขัดกันได้ และยอดเงินยังเป็น authority
จึงไม่ได้แก้ปัญหา
2. ใช้ ledger database เฉพาะทาง
ปฏิเสธในรอบนี้: เพิ่ม operational dependency; ปริมาณของเรายังไม่ต้องการ
ทบทวนเมื่อเกิน ~5,000 txn/s
3. Immutable double-entry ใน Postgres ที่มีอยู่ <-- เลือกอันนี้
## Revisit if
- write rate ต่อเนื่องเกิน 5,000 entry/s หรือ
- p99 ของการอ่านยอดเกิน 50 ms หลัง tune snapshot แล้ว หรือ
- ข้อกำหนด retention เปลี่ยนจนทำให้ hot storage เกิน ~4 TB
ทำไม ADR คุ้มค่า 20 นาที
6 เดือนถัดมามีคนถามว่า "ทำไมมันซับซ้อนขนาดนี้" ถ้าไม่มี ADR คำตอบที่ซื่อสัตย์คือ "ไม่มีใครจำได้" และผลที่น่าจะเกิดคือการเขียนใหม่ที่ไปค้นพบ constraint ชุดเดิมด้วยความเจ็บปวดแบบเดิม
ถ้ามี ADR คุณได้คำตอบจริง รวมถึงทางเลือกที่ถูกปฏิเสธและเหตุผล ซึ่งเป็นส่วนที่ป้องกันการวนลูป
ADR ยังทำให้การ onboard เร็วขึ้นมาก และให้คำตอบที่ป้องกันได้เวลา auditor ถามว่าทำไมมี control นี้
เก็บไว้ใน repository ข้างโค้ด, ใส่หมายเลข, append-only — การ supersede ADR หมายถึงเขียนฉบับใหม่ที่อ้างถึงฉบับเดิม ไม่ใช่แก้ประวัติ (Michael Nygard · adr.github.io)
Anti-pattern และ design smell
| Smell | เสียงที่ได้ยิน | ปัญหาที่ซ่อนอยู่ | ทำอะไร |
|---|---|---|---|
| Resume-driven design | "เราควรใช้ Kafka กับ Kubernetes กับ service mesh" | เลือกเทคโนโลยีก่อนมี requirement | ถาม "requirement ข้อไหน" 3 ครั้ง ถ้าไม่มีคำตอบ มันก็ไม่ใช่ requirement |
| Distributed monolith | "เรามี 12 service แต่ทั้งหมด deploy พร้อมกันและใช้ฐานข้อมูลเดียวกัน" | จ่ายค่าความกระจายไปแล้วแต่ไม่ได้ประโยชน์อะไรกลับมา | รวมกลับ หรือแยกข้อมูลให้จริงจัง — อันนี้แย่ที่สุดของทั้ง 2โลก และพบบ่อยมาก |
| Premature scaling | "มาออกแบบเผื่อผู้ใช้ 1 ล้านคน" | ความซับซ้อนวันนี้เพื่อโหลดที่อาจไม่มาถึง · product จริงตายเพราะส่งของช้า | ออกแบบเผื่อ 10× ของวันนี้ เขียนเส้นทางถึง 100× ไว้ แล้วส่งของ |
| Shared mutable database | "ทีม reporting อ่านตารางเราตรงๆ" | schema ของคุณกลายเป็น public API ที่คุณแก้ไม่ได้ | ให้ view, replica, export หรือ event stream — interface ที่คุณ version ได้ |
| God service | "ทุกอย่างเรียก core service" | single point of failure, คอขวดของ deployment, ทีมเดียวบล็อกทุกคน | แยกตามความเป็นเจ้าของข้อมูล; ย้าย read path ไปมี model ของตัวเอง |
| Chatty boundaries | "มันเรียก 40 ครั้งเพื่อ render หน้าเดียว" | latency และความน่าจะเป็นที่จะล้มคูณกันต่อการเรียก | batch endpoint, aggregate ที่ edge (BFF), หรือ ทบทวนขอบเขต — 40 ครั้งหมายถึงรอยต่ออยู่ผิดที่ |
| Big-bang rewrite | "เราจะสร้าง v2 ขนานกันแล้วสลับปีหน้า" | 2 ระบบที่ต้องดูแล, เป้าที่เคลื่อนที่, และ cutover ที่ไม่มีการซ้อม · อัตราล้มเหลวสูงมากในประวัติศาสตร์ | Strangler fig: route ทีละส่วน, ให้ทั้ง 2รันอยู่, migrate ทีละนิด, พร้อม release เสมอ |
| Speculative abstraction | "เราทำให้ pluggable ไว้เผื่อเปลี่ยน provider" | abstraction ที่ออกแบบก่อนมี implementation ที่ 2 มักเป็น abstraction ที่ผิด | รอเคสจริงอันที่ 2 · ข้อยกเว้น: dependency ที่ถูกกำกับ (payment rail, KYC provider) ที่การมี provider ที่ 2 เป็น requirement ที่รู้อยู่แล้ว — ที่นั่นให้สร้างรอยต่อไว้ก่อนอย่างตั้งใจ |
| Retry everything | "เราใส่ retry แล้ว ตอนนี้ resilient แล้ว" | retry ที่ไม่มี idempotency, budget และ jitter ขยายความล้มเหลว | ดูบทที่ 12 — retry เป็นตัวคูณโหลดก่อนที่จะเป็นเครื่องมือ resilience |
| Alert on everything | "เรามี alert 400 ตัว" | alert fatigue — ตัวจริงถูก mute | alert ที่ อาการที่ผู้ใช้รู้สึก และที่ SLO burn rate · ที่เหลือเป็น dashboard |
อ้างอิง: Strangler Fig Application · Sacrificial Architecture — ฉบับหลังเป็นการมองใหม่ที่มีประโยชน์: การสร้างของที่คุณตั้งใจจะทิ้งเป็นเรื่องชอบธรรม ถ้าคุณพูดออกมาดังๆ และวางแผนไว้
Lab 17 — ตัดสิน ป้องกัน และบันทึก
ส่วน A (35 นาที) หยิบการตัดสินใจที่ยังเปิดอยู่จริงจากงานของคุณ ทำ frame ให้ครบ: คำถามที่แคบ, เกณฑ์ที่ถ่วงน้ำหนัก, 3 ทางเลือกรวมทางที่ง่ายที่สุด, ช่องคะแนนที่มีเหตุผล 1 บรรทัด, และ trigger ที่จะทบทวน
กฎ: คุณต้องใส่ทางเลือกที่คุณเองไม่ชอบ และให้คะแนนมันอย่างเป็นธรรม
ส่วน B (25 นาที) เขียน ADR ตาม template ข้างบน ข้อจำกัด: 1 หน้า ถ้าไม่พอ แปลว่าการตัดสินใจของคุณจริงๆ เป็นหลายการตัดสินใจ — แยกมัน
ส่วน C (30 นาที) สลับกับอีกกลุ่มแล้วเล่นบท devil's advocate หน้าที่ของ reviewer คือหา: เกณฑ์ที่ขาดไป, น้ำหนักที่ถูกเลือกเพื่อให้ตัวโปรดชนะ, ทางเลือกที่ถูกทำให้อ่อนแอเกินจริง, และ consequence ที่ตกหายไป
ทุกกลุ่มจะมีอย่างน้อย 2 ข้อ — นั่นคือประเด็น นี่คือสิ่งที่ review ที่ดีทำ และการทำให้กันคือวิธีที่ทักษะนี้ถ่ายทอด
สรุปบทนี้
ตั้งคำถามให้แคบ ถ่วงน้ำหนักเกณฑ์ก่อนดูทางเลือก และต้องมีทางที่ง่ายที่สุดในตาราง · เขียน trigger ที่จะทบทวนไว้ทุกครั้ง · CAP เป็นเรื่องต่อ operation ไม่ใช่ต่อระบบ · cost per transaction เปลี่ยนสถานะของงานสถาปัตยกรรมในสายตาผู้บริหาร · ใช้เวลารีวิวกับ one-way door ไม่ใช่ two-way door · และ ADR 1 หน้าที่บอกว่า ทางเลือกที่แพ้แพ้เพราะอะไร คือสิ่งที่ป้องกันการเขียนใหม่ในอีก 2 ปี