Gall’s Law
ระบบซับซ้อนที่ใช้งานได้ ล้วนวิวัฒน์มาจากระบบง่าย ๆ ที่ใช้งานได้
ใจความสำคัญ
หัวข้อที่มีชื่อว่า “ใจความสำคัญ”Gall’s Law เป็นข้อสังเกตเกี่ยวกับธรรมชาติและวิวัฒนาการของระบบซับซ้อน มาจากหนังสือ Systemantics: How Systems Work and Especially How They Fail (1975) ของ John Gall กุมารแพทย์และนักเขียนชาวอเมริกัน ซึ่งภายหลังตีพิมพ์ซ้ำในชื่อ The Systems Bible
“A complex system that works is invariably found to have evolved from a simple system that worked. A complex system designed from scratch never works and cannot be patched up to make it work. You have to start over, beginning with a working simple system.”
— John Gall, Systemantics
แปลใจความ: “ระบบซับซ้อนที่ทำงานได้ ล้วนพบว่าวิวัฒน์มาจากระบบง่าย ๆ ที่ทำงานได้เสมอ ระบบซับซ้อนที่ออกแบบจากศูนย์ไม่มีทางทำงานได้ และแก้ให้ใช้ได้ก็ไม่ได้ คุณต้องเริ่มใหม่ด้วยระบบง่าย ๆ ที่ทำงานได้เสียก่อน”
Gall ไม่ได้เขียนเรื่องนี้จากมุมมองวิศวกรรมซอฟต์แวร์โดยตรง แต่จากการสังเกตระบบราชการ องค์กร และระบบวิศวกรรมโดยทั่วไปว่าทำไมจึงมักล้มเหลว ทว่านักออกแบบระบบซอฟต์แวร์ — โดยเฉพาะ Grady Booch ที่หยิบยกกฎนี้มาอ้างอิงตั้งแต่ปี 1991 — ทำให้ Gall’s Law กลายเป็นหลักคิดหลักในวงการวิศวกรรมซอฟต์แวร์และสถาปัตยกรรมระบบสมัยใหม่
หนังสือของ Gall ตีพิมพ์ครั้งแรกในปี 1975 ในชื่อ General Systemantics ก่อนจะขยายและตีพิมพ์ซ้ำในปี 1986 ในชื่อ Systemantics: The Underground Text of Systems Lore และอีกครั้งในปี 2002 ในชื่อ The Systems Bible งานชิ้นนี้ถูกแปลเป็นภาษาสเปน เยอรมัน ฮีบรู และญี่ปุ่น และมีอิทธิพลต่อนักคิดเชิงระบบคนสำคัญอย่าง Mario Bunge, Paul Watzlawick และ Russell Ackoff แม้เนื้อหาส่วนใหญ่ในหนังสือจะเขียนด้วยน้ำเสียงเสียดสีองค์กรและระบบราชการ แต่ข้อสังเกตแกนกลางเรื่องวิวัฒนาการของความซับซ้อนกลับสอดคล้องอย่างลึกซึ้งกับแนวทาง agile และ extreme programming ที่เกิดขึ้นภายหลัง
ความหมายและนัยยะ
หัวข้อที่มีชื่อว่า “ความหมายและนัยยะ”กฎนี้ตั้งอยู่บนข้อสังเกตเชิงประจักษ์ (empirical observation) ไม่ใช่ทฤษฎีที่พิสูจน์ได้ทางคณิตศาสตร์ แต่สะท้อนรูปแบบที่พบซ้ำ ๆ ในระบบที่ประสบความสำเร็จ:
- ระบบซับซ้อนต้องผ่านการทดสอบกับความจริงทีละขั้น ระบบที่ออกแบบจากศูนย์โดยไม่เคยรันจริงเลย ไม่มีทางคาดเดาได้ครบทุกกรณีขอบ (edge case) ทุกปฏิสัมพันธ์ระหว่างส่วนประกอบ หรือทุกความต้องการที่แท้จริงของผู้ใช้
- โครงสร้างที่ใช้งานได้ในระบบง่าย เป็นฐานที่ระบบซับซ้อนสร้างต่อได้ เมื่อระบบง่ายทำงานได้จริง มันพิสูจน์แล้วว่าส่วนประกอบพื้นฐานถูกต้อง จากนั้นค่อยเพิ่มความซับซ้อนทีละชั้นโดยยังคงมีระบบที่ใช้งานได้เสมอในทุกขั้นตอน
- นี่คือข้อโต้แย้งต่อ Big Design Up Front (BDUF) — การพยายามออกแบบทุกส่วนของระบบใหญ่ให้เสร็จสมบูรณ์ก่อนเริ่มลงมือสร้าง มักจบด้วยระบบที่ซับซ้อนเกินจะ debug หรือแก้ไขได้ เพราะไม่เคยผ่านการพิสูจน์ทีละส่วนกับโลกจริงเลย
- ข้อสังเกตนี้อธิบายทั้งความสำเร็จและความล้มเหลว ตัวอย่างคลาสสิกที่มักถูกอ้างถึงคือ World Wide Web ที่เริ่มจากระบบไฮเปอร์เท็กซ์เรียบง่ายแล้วค่อยขยายจนซับซ้อนอย่างทุกวันนี้ เทียบกับ CORBA ซึ่งเริ่มต้นด้วยข้อกำหนด (specification) ที่ซับซ้อนตั้งแต่ต้นและประสบปัญหาการยอมรับใช้งานอย่างกว้างขวาง
สิ่งสำคัญคือ Gall’s Law ไม่ได้บอกว่า “อย่าสร้างระบบซับซ้อน” แต่บอกว่า เส้นทางไปสู่ระบบซับซ้อนที่ทำงานได้ต้องผ่านระบบง่ายที่ทำงานได้ก่อนเสมอ — ไม่มีทางลัดข้ามขั้นตอนนี้ไปได้
อีกมุมหนึ่งที่ควรทำความเข้าใจคือ ทำไมการออกแบบระบบซับซ้อนจากศูนย์ (design from scratch) จึงล้มเหลวซ้ำ ๆ ในทางปฏิบัติ เหตุผลไม่ใช่เพราะนักออกแบบไม่เก่งพอ แต่เพราะ:
- ความซับซ้อนของระบบเติบโตแบบ combinatorial จำนวนปฏิสัมพันธ์ที่เป็นไปได้ระหว่างส่วนประกอบเพิ่มขึ้นเร็วกว่าจำนวนส่วนประกอบมาก การคาดการณ์ทุกกรณีล่วงหน้าจึงเป็นไปไม่ได้ในทางปฏิบัติ แม้จะมีสเปคที่ละเอียดเพียงใด
- ข้อกำหนด (requirement) ที่แท้จริงเผยตัวออกมาก็ต่อเมื่อมีคนใช้งานระบบจริง ไม่ว่าจะระดมสมองหรือทำ requirement-gathering ละเอียดแค่ไหน ก็ไม่มีทางรู้ทุกอย่างก่อนที่ผู้ใช้จริงจะได้สัมผัสระบบ
- ระบบง่ายที่ทำงานได้ทำหน้าที่เป็น “หลักฐานเชิงประจักษ์” ทีละชั้น ทุกครั้งที่เพิ่มความซับซ้อนแล้วยังคงทำงานได้ นั่นคือการพิสูจน์สมมติฐานของชั้นก่อนหน้า สะสมความมั่นใจไปทีละขั้น ต่างจากการออกแบบทั้งระบบทีเดียวที่ไม่มีจุดตรวจสอบระหว่างทางเลย
ตัวอย่างในโลกจริง
หัวข้อที่มีชื่อว่า “ตัวอย่างในโลกจริง”Monolith First ในสถาปัตยกรรม microservices — Martin Fowler เสนอแนวคิด “Monolith First” ไว้ในบทความชื่อเดียวกัน โดยชี้ว่าเรื่องราวความสำเร็จของ microservices เกือบทั้งหมดเริ่มจาก monolith ที่ใช้งานได้จริงแล้วโตจนต้องแตกออกเป็นบริการย่อย ในขณะที่ระบบซึ่งถูกออกแบบเป็น microservices ตั้งแต่วันแรกมักจบลงด้วยปัญหาใหญ่ เหตุผลหลักสองข้อคือ (1) ทีมงานยุคเริ่มต้นต้องการวงจร feedback ที่เร็ว การจัดการความซับซ้อนเชิง operation ของ microservices จะถ่วงความเร็วนั้น และ (2) การกำหนดขอบเขตบริการ (service boundary) ที่ถูกต้องเป็นเรื่องยากแม้แต่สำหรับสถาปนิกผู้เชี่ยวชาญ — การมี monolith ที่ทำงานได้ก่อนช่วยให้ทีมค้นพบขอบเขตที่แท้จริงจากการใช้งานจริง ก่อนจะผูกมัดกับการแยกบริการ
World Wide Web เทียบกับ CORBA — เว็บเริ่มต้นจากโพรโทคอล HTTP และ HTML ที่เรียบง่ายมากในปี 1991 แล้วค่อยเติบโตทีละองค์ประกอบ (CSS, JavaScript, REST APIs, ฯลฯ) จนกลายเป็นโครงสร้างพื้นฐานที่ซับซ้อนที่สุดระบบหนึ่งของโลก ในทางตรงข้าม CORBA (Common Object Request Broker Architecture) ถูกออกแบบด้วยข้อกำหนดที่ซับซ้อนตั้งแต่เริ่มต้น และแม้จะได้รับการสนับสนุนจากอุตสาหกรรมอย่างมาก ก็ไม่เคยได้รับความนิยมในวงกว้างเท่าเว็บ
MVP (Minimum Viable Product) ในการพัฒนาผลิตภัณฑ์ — การเริ่มต้นด้วยผลิตภัณฑ์ขั้นต่ำที่ใช้งานได้จริง แล้วปล่อยให้ผู้ใช้จริงเป็นตัวขับเคลื่อนการเพิ่ม feature เป็นการประยุกต์ใช้ Gall’s Law โดยตรงในบริบทของ product development — ตัวอย่างที่มักถูกยกมาคือ Facebook ที่เริ่มจากระบบโปร file นักศึกษาฮาร์วาร์ดเรียบง่าย ก่อนขยายเป็น platform ระดับโลก
การเติบโตของ domain model ใน code — ลองเทียบสองแนวทางในการเริ่มสร้างระบบ order processing แนวทางแรกพยายามออกแบบโครงสร้างที่ “รองรับทุกอนาคต” ตั้งแต่วันแรก (BDUF): มี interface, abstract factory, strategy pattern สำหรับ discount, tax, shipping ที่ยังไม่มีลูกค้าจริงมาทดสอบเลยสักราย ผลคือ code ซับซ้อนแต่ไม่มีส่วนไหนผ่านการพิสูจน์กับความต้องการจริง เมื่อความต้องการจริงมาถึงกลับไม่ตรงกับโครงสร้างที่เดาไว้ และต้องรื้อทิ้งเกือบทั้งหมด
// เริ่มจากระบบง่ายที่ทำงานได้จริงก่อน — ยังไม่มี abstraction ที่ยังไม่ถูกพิสูจน์public class Order{ public decimal Total { get; private set; }
public void AddItem(decimal price) { Total += price; }}
// เมื่อมีความต้องการจริงเรื่องส่วนลด ค่อยเพิ่มความซับซ้อนทีละชั้น// โดยยังคงมีระบบที่ทำงานได้ในทุกขั้นตอน แทนที่จะออกแบบ IDiscountStrategy// ล่วงหน้าตั้งแต่วันแรกโดยยังไม่รู้ว่ากฎส่วนลดจริงเป็นอย่างไรpublic class Order{ private readonly List<decimal> _itemPrices = new();
public void AddItem(decimal price) => _itemPrices.Add(price);
public decimal Total => _itemPrices.Sum() * (1 - DiscountRateFor(_itemPrices.Count));
// กฎส่วนลดถูกเพิ่มเข้ามาหลังจากเห็นความต้องการจริงจากผู้ใช้ ไม่ใช่การเดาล่วงหน้า private static decimal DiscountRateFor(int itemCount) => itemCount >= 10 ? 0.1m : 0m;}บทเรียนที่นำไปใช้
หัวข้อที่มีชื่อว่า “บทเรียนที่นำไปใช้”- อย่าพยายามสร้างระบบใหญ่ที่เชื่อมโยงกันทั้งหมดตั้งแต่วันแรก (หลีกเลี่ยง Big Design Up Front) แต่จงเริ่มจากสิ่งที่เรียบง่ายและใช้งานได้จริง แล้วค่อย ๆ เติบโตอย่างมีการทดสอบกับความจริงในทุกขั้น
- ส่งมอบเป็น Vertical Slices แต่ละ slice ควรเป็นระบบเล็ก ๆ ที่ “ทำงานได้” ครบวงจรตั้งแต่ต้นจนจบ (end-to-end) แม้จะบางและมี feature น้อย ดีกว่าการสร้างทุก layer ให้หนาแต่ไม่มี slice ไหนใช้งานได้จริงเลย
- ยึด KISS และ YAGNI เป็นแนวปฏิบัติคู่กัน — ความซับซ้อนที่คุณ คาดว่า จะต้องใช้ในอนาคต มักผิดจากสิ่งที่ระบบจริงต้องการ การเพิ่มความซับซ้อนควรมาจากความต้องการที่พิสูจน์แล้วเท่านั้น ไม่ใช่จากการเดา
- ในการออกแบบสถาปัตยกรรม ให้พิจารณา monolith-first ก่อนกระโดดไป microservices เว้นแต่ทีมมีประสบการณ์ตรงกับ domain นั้นมาก่อนแล้วจริง ๆ
- ยอมรับว่าการ refactor (refactoring) เป็นส่วนหนึ่งของเส้นทางที่หลีกเลี่ยงไม่ได้ เพราะระบบง่ายที่ทำงานได้จะต้องถูกปรับโครงสร้างซ้ำแล้วซ้ำเล่าเพื่อรองรับความซับซ้อนที่เพิ่มขึ้น — นี่ไม่ใช่ความล้มเหลวของการออกแบบเริ่มต้น แต่เป็นกลไกวิวัฒนาการตามธรรมชาติของระบบที่ใช้งานได้
- ระวังไม่ตีความกฎนี้ผิดเป็นข้ออ้างละเลยการออกแบบเลย Gall’s Law ไม่ได้ต่อต้านการวางแผนสถาปัตยกรรม แต่เตือนว่าการวางแผนต้องพิสูจน์ผ่านการทำงานจริงเป็นระยะ ๆ ไม่ใช่ปล่อยให้แผนใหญ่ลอยอยู่บนกระดาษจนกว่าจะ “เสร็จสมบูรณ์” ก่อนเริ่มสร้าง
flowchart LR
Start[ระบบง่ายที่ทำงานได้] --> Grow[เพิ่มความซับซ้อนทีละส่วน]
Grow --> Verify[พิสูจน์ด้วยการใช้งานจริง]
Verify --> Grow
Verify --> Complex[ระบบซับซ้อนที่ทำงานได้]
Scratch[ออกแบบระบบซับซ้อนจากศูนย์] --> Fail[ไม่ทำงาน แก้ไม่ได้]
ที่เกี่ยวข้อง
หัวข้อที่มีชื่อว่า “ที่เกี่ยวข้อง”แหล่งอ้างอิง
หัวข้อที่มีชื่อว่า “แหล่งอ้างอิง”- ที่มา · deviq.com/laws/galls-law
- Systemantics: How Systems Work and Especially How They Fail (ภายหลังตีพิมพ์ซ้ำในชื่อ The Systems Bible) — John Gall, 1975
- John Gall (author) — Wikipedia
- MonolithFirst — Martin Fowler
- Gall’s Law | Laws of Software Engineering