YOLO Software Architecture
design แบบ “ทำไปเรื่อย” ที่ไร้แผน — ทางลัดสู่ Big Ball of Mud
คืออะไร
หัวข้อที่มีชื่อว่า “คืออะไร”ในโลกการพัฒนาซอฟต์แวร์ มี pattern สถาปัตยกรรมมากมายที่ช่วยจัดระเบียบ codebase เช่น Clean Architecture และ Vertical Slice Architecture แต่ไม่ใช่ทุกระบบที่เดินตาม pattern เหล่านี้ บางระบบพัฒนาแบบวุ่นวายไร้โครงสร้าง — นั่นคือ YOLO Architecture ซึ่งเล่นคำจาก “You Only Live Once” เพื่อสะท้อนธรรมชาติที่สะเพร่าและทำไปเรื่อย ๆ (ad-hoc) ของระบบเช่นนี้
YOLO Architecture คือสไตล์การพัฒนาที่มีการวางแผนน้อยที่สุด ขาดการจัดระเบียบ และไร้หลักการสถาปัตยกรรมชัดเจน เป็นสภาวะที่ code ถูกเขียนโดยแทบไม่คำนึงถึงความสามารถในการดูแล การ scale หรืออนาคต มักเริ่มใน project เล็กหรือสตาร์ตอัปที่ให้ความสำคัญกับความเร็วและผลลัพธ์เฉพาะหน้ามากกว่าคุณภาพ code ระยะยาว มันไม่ใช่ “pattern” ที่ใครออกแบบไว้ตั้งใจ แต่เป็นสิ่งที่เกิดขึ้นเองเมื่อไม่มีใครลงมือฝึกวินัยด้านสถาปัตยกรรมอย่างต่อเนื่อง
สิ่งที่ควรแยกให้ออกคือ YOLO มีสองรูปแบบที่ต่างกันในเจตนา ตาม Technical Debt Quadrant ของ Martin Fowler: แบบแรกคือ reckless deliberate — ทีมรู้ตัวและเลือกลัดจงใจ เช่น เดโมในแฮคคาธอนหรือ spike ทดลองไอเดีย ซึ่งพอรับได้ถ้าจำกัดขอบเขต แบบที่สองคือ accidental — ไม่มีใครตัดสินใจเลือก YOLO เลย มันคืบคลานเข้ามาเองจากแรงกดดันเรื่อง deadline ต้นทุน ประสบการณ์ทีมที่จำกัด requirement ที่เปลี่ยนตลอด และธรรมชาติของซอฟต์แวร์ที่โครงสร้างภายในมองไม่เห็นจากภายนอก (ตามที่ Brian Foote และ Joseph Yoder อธิบายไว้ในเปเปอร์ Big Ball of Mud ต้นฉบับ) แบบหลังนี้อันตรายกว่ามาก เพราะไม่มีจุดใดที่ทีมรู้ตัวว่ากำลังก่อหนี้ทางเทคนิคอยู่
ลักษณะของ YOLO Architecture
หัวข้อที่มีชื่อว่า “ลักษณะของ YOLO Architecture”- ขาด modularity: code มักเป็น monolith แยก concern น้อย function และ class ถูกใส่ตรงไหนก็ได้ที่สะดวก จนเกิด tightly coupled
- เอกสารแย่: แทบไม่มีการทำเอกสาร พึ่ง tribal knowledge ทำให้คนใหม่เข้าใจระบบยาก
- ตัดสินใจแบบ ad-hoc: ตัดสินใจสถาปัตยกรรมเฉพาะหน้าโดยไม่คิดถึงผลระยะยาว ทางลัดคือเรื่องปกติ
- technical debt สูง: ทางลัดสะสมจนจัดการยากขึ้นเรื่อย ๆ
- พัฒนาแบบตั้งรับ: เปลี่ยนตามความจำเป็นเฉพาะหน้า ไม่มี roadmap ยิ่งระบบใหญ่ การแก้เล็ก ๆ ยิ่งลามเป็นปัญหาต่อเนื่อง productivity ดิ่งลง
ทางสู่ Big Ball of Mud: YOLO Architecture มักเป็นจุดตั้งต้นของ pattern Big Ball of Mud — ระบบที่โครงสร้างมั่วซั่ว code โตอย่างควบคุมไม่ได้จนดูแลแทบไม่ไหว จากช่วงแรกที่ดูจัดการได้ สู่การโตเร็ว ปัญหา scale และสุดท้ายถึงจุดที่ refactor แทบเป็นไปไม่ได้โดยไม่เสี่ยงมหาศาล Wikipedia อธิบาย Big Ball of Mud ว่าเป็นระบบที่ “haphazardly structured, sprawling, sloppy” — ไร้สถาปัตยกรรมที่พอสังเกตเห็นได้เลย
ทำอย่างไร
หัวข้อที่มีชื่อว่า “ทำอย่างไร”กลไกที่พาไปสู่ YOLO
หัวข้อที่มีชื่อว่า “กลไกที่พาไปสู่ YOLO”ไม่มีใครเริ่ม project โดยตั้งใจให้เป็น YOLO Architecture แต่มันเกิดขึ้นทีละขั้นผ่านแรงกดดันที่สะสม ตามที่ Foote และ Yoder ระบุไว้ในเปเปอร์ต้นฉบับ: แรงกดดันเรื่องเวลา (deadline ใกล้เข้ามา สถาปัตยกรรมจึงถูกลดความสำคัญ) ต้นทุนของการออกแบบที่ดีซึ่งไม่เห็นผลตอบแทนทันที ประสบการณ์ทีมที่จำกัดทั้งด้านเทคนิคและ domain requirement ที่เปลี่ยนไปจนแม้สถาปัตยกรรมที่ดีในตอนแรกก็ค่อย ๆ กร่อน และการที่โครงสร้างซอฟต์แวร์มองไม่เห็นจากภายนอกทำให้ผู้บริหารมองข้ามได้ง่าย
flowchart TD
A[เริ่ม project เร็ว ไม่วางแผนสถาปัตยกรรม] --> B[เขียน code แบบ ad hoc ตามแรงกดดัน deadline]
B --> C[ข้ามเอกสารและข้าม code review เพื่อความเร็ว]
C --> D[ทางลัดสะสมกลายเป็น technical debt]
D --> E[module พันกันแบบ tightly coupled]
E --> F[กลายเป็น Big Ball of Mud]
F --> G[Refactor ทำได้ยากและเสี่ยงสูงมาก]
จะป้องกันไม่ให้เกิด YOLO ได้อย่างไร
หัวข้อที่มีชื่อว่า “จะป้องกันไม่ให้เกิด YOLO ได้อย่างไร”เพื่อกันไม่ให้ project กลายเป็น Big Ball of Mud ควรวางรากฐานสถาปัตยกรรมที่มั่นคงตั้งแต่เนิ่น ๆ (แม้ไม่จำเป็นต้องวันแรก):
- ลงทุนกับการวางแผนและออกแบบ โดยคำนึงถึง scalability ตั้งแต่ต้น แต่ไม่ต้องถึงขั้น Big Design Up Front ที่วางแผนละเอียดจนไม่ได้เริ่มลงมือ
- จัดโครงสร้าง code เป็น module/ชั้นที่ชัดเจน ตามหลัก SOLID และ Separation of Concerns และส่งมอบ feature แบบ Vertical Slices เพื่อให้แต่ละ feature แยกจากกันได้จริง
- ทำเอกสาร รวมถึง decision record สั้น ๆ ว่าทำไมถึงเลือกทางนี้ เพื่อไม่ให้ความรู้ติดอยู่กับตัวคนเพียงคนเดียว
- จัดกระบวนการ code review ที่เข้มงวด พร้อมเครื่องมืออัตโนมัติ (static analysis, linting, CI) เพื่อจับปัญหาก่อนมันฝังลึก
- ยึดวิสัยทัศน์สถาปัตยกรรมร่วม ให้ทั้งทีมเข้าใจตรงกันว่าระบบควรมีหน้าตาแบบไหน แทนที่แต่ละคนจะตัดสินใจเองแยกกัน
- refactor อย่างต่อเนื่อง เพื่อจัดการ technical debt ก่อนมันสะสมจนแก้ไม่ไหว
ตัวอย่าง
หัวข้อที่มีชื่อว่า “ตัวอย่าง”ตัวอย่างต่อไปนี้จำลองสิ่งที่มักเกิดขึ้นหลังทีมพัฒนาแบบ YOLO มาหลายสปรินต์ — ทุกอย่างถูกยัดรวมไว้ใน class เดียวเพราะ “ตอนนั้นแก้แบบนี้เร็วกว่า”
// ผลลัพธ์ทั่วไปของ YOLO Architecture: ทุก concern กองรวมกันในเมทอดเดียวpublic class OrderController : ControllerBase{ private readonly SqlConnection db = new SqlConnection("Server=prod;...");
[HttpPost("checkout")] public IActionResult Checkout(OrderDto dto) { // การเข้าถึงข้อมูล เขียนตรงใน controller ไม่มี repository ไม่มี test db.Open(); var cmd = new SqlCommand("INSERT INTO Orders (Email, Total) VALUES (@e, @t)", db); cmd.Parameters.AddWithValue("@e", dto.Email); cmd.Parameters.AddWithValue("@t", dto.Total); cmd.ExecuteNonQuery();
// logic ธุรกิจถูกแปะเพิ่มทีหลังแบบรีบ ๆ ไม่มีที่ไปเขียนใหม่ if (dto.Total > 1000) dto.Total *= 0.9m;
// TODO เพิ่มไว้ตอน production พังครั้งที่ 3 ยังไม่มีเวลากลับมาแก้ // ยังไม่ validate สต็อกสินค้าก่อน checkout
new SmtpClient().Send("orders@shop.com", dto.Email, "ยืนยันคำสั่งซื้อ", "ขอบคุณที่สั่งซื้อ"); return Ok(); }}สถานการณ์ทั่วไป: ทีมเริ่มจาก code เดโมสำหรับพรีเซนต์นักลงทุน แล้วต้องรีบขึ้น production ก่อนถึงเวลา จึงข้าม code review และไม่มี test คุ้มกันไว้ พอ feature ใหม่ต้องเพิ่มเข้ามา ทุกคนก็แปะ logic เพิ่มลงใน class เดิมเพราะ “แก้ตรงนี้ง่ายสุด” หกเดือนผ่านไป ทีม incident postmortem พบว่าไม่มีขอบเขตความรับผิดชอบชัดเจนเลยสักจุดเดียว
แนวทางแก้ไขคือแยก concern ออกจากกันตามหลัก SRP และย้าย logic ไปอยู่ในชั้นที่เหมาะสม เช่น
// แยกความรับผิดชอบ: controller บาง เรียกใช้ service ที่มี logic ธุรกิจpublic class OrderController : ControllerBase{ private readonly IOrderService orderService;
public OrderController(IOrderService orderService) => this.orderService = orderService;
[HttpPost("checkout")] public async Task<IActionResult> Checkout(OrderDto dto) { var result = await orderService.PlaceOrderAsync(dto); return result.IsSuccess ? Ok(result.Value) : BadRequest(result.Error); }}ประโยชน์และข้อควรระวัง
หัวข้อที่มีชื่อว่า “ประโยชน์และข้อควรระวัง”เมื่อไหร่ที่ยอมรับได้: ความเร็วของ YOLO มีที่ทางของมันจริง ๆ ในบางบริบท เช่น hackathon การทำ spike เพื่อพิสูจน์ไอเดียแบบ throwaway หรือ prototype ที่จะถูกทิ้งแน่นอนหลังเดโม ในสถานการณ์เหล่านี้การไม่วางสถาปัตยกรรมล่วงหน้าสอดคล้องกับหลัก YAGNI — ไม่สร้างสิ่งที่ยังไม่จำเป็นต้องใช้ ต้นทุนตั้งต้นต่ำ ทำให้ทดสอบไอเดียได้เร็วโดยไม่ต้องผ่านขั้นตอน Big Design Up Front ที่ถ่วงเวลา
ข้อควรระวัง: ปัญหาคือ prototype แทบทุกตัวมีแนวโน้ม “รอด” ไปขึ้น production จริงโดยไม่มีใครหยุดกลับมา redesign และเมื่อนั้น YOLO Architecture จะเริ่มเก็บดอกเบี้ยทางเทคนิค — Big Ball of Mud ที่ refactor เสี่ยงสูงและแพง onboarding คนใหม่ทำได้ช้าเพราะไม่มีเอกสารหรือขอบเขตชัดเจน การเปลี่ยนแปลงเล็ก ๆ กลับส่งผลกระทบลามไปทั่วระบบเพราะทุกอย่าง tightly coupled และเมื่อไม่มี test คุ้มกัน bug ก็หลุดเข้า production ง่ายขึ้นเรื่อย ๆ ยิ่งระบบมีอายุนานเท่าไร ต้นทุนของการอยู่แบบ YOLO ต่อไปก็ยิ่งสูงกว่าต้นทุนของการหยุดมาจัดโครงสร้างใหม่
ทางที่สมดุลคือยอมรับว่าความชุลมุนในช่วงเริ่มต้นเป็นเรื่องธรรมชาติ (ตามที่ Foote และ Yoder เองก็ยอมรับ) แต่ต้อง “รู้ตัว” ว่ากำลังก่อหนี้อยู่ และมีวินัยพากลับมาสู่โครงสร้างที่ยั่งยืนก่อนที่มันจะสายเกินแก้
ที่เกี่ยวข้อง
หัวข้อที่มีชื่อว่า “ที่เกี่ยวข้อง”- SOLID
- Separation of Concerns
- Vertical Slices
- Common Architectural Vision
- Architectural Agility
- Big Ball of Mud