Murphy’s Law
อะไรที่ผิดพลาดได้ มันจะผิดพลาด
Murphy’s Law กล่าวว่า “อะไรที่ผิดพลาดได้ มันจะผิดพลาด” (Anything that can go wrong will go wrong) เป็นหนึ่งใน “กฎ” พื้นบ้านที่ถูกอ้างอิงมากที่สุดในวงการวิศวกรรม ไม่ใช่ทฤษฎีทางฟิสิกส์หรือสถิติ แต่เป็นคำเตือนเชิงปฏิบัติที่บอกว่า: ถ้าระบบมีช่องโหว่หรือทางที่จะพังได้ สักวันหนึ่งมันจะพังตามทางนั้นจริง ๆ โดยเฉพาะเมื่อระบบถูกใช้งานซ้ำ ๆ เป็นเวลานาน หรือมีผู้ใช้จำนวนมาก
ใจความสำคัญ
หัวข้อที่มีชื่อว่า “ใจความสำคัญ”เมื่อออกแบบและสร้างระบบ สิ่งสำคัญคือต้องคิดถึงทุกวิถีทางที่ระบบอาจล้มเหลว เพราะสักวันสิ่งเหล่านั้นจะเกิดขึ้นจริง Defensive programming คือการเขียน code ที่คาดการณ์ปัญหาและจัดการมันก่อนที่จะลุกลามกลายเป็นความเสียหายใหญ่ และหลักที่มักใช้คู่กันคือ fail fast — ยิ่งตรวจพบ input ที่ผิดเร็วเท่าไร ความเสียหายก็ยิ่งน้อย ใช้ทรัพยากรจัดการน้อยลง และผู้ใช้แก้ไขได้เร็วขึ้น
ที่มาของวลีนี้ย้อนไปถึงปี 1949 ที่ Muroc Army Air Field (ต่อมาคือ Edwards Air Force Base) ในโครงการทดสอบ MX981 ของกองทัพอากาศสหรัฐฯ ซึ่งศึกษาผลของแรง g ต่อร่างกายมนุษย์ด้วยการเลื่อนแท่นจรวด (rocket sled) นำโดยพันเอก John Stapp วิศวกรการบิน Edward A. Murphy Jr. เป็นผู้เสนอให้ใช้ strain gauge วัดแรงบนสายรัดของ Stapp แต่เกจถูกต่อสายผิดจนอ่านค่าได้ศูนย์ทุกตัว Murphy จึงบ่นถึงผู้ช่วยของเขาในทำนองว่า “ถ้ามีทางทำผิดได้ เขาจะทำผิดทางนั้น” คำพูดนี้ถูกเพื่อนร่วมงานนำไปสรุปย่อเป็น “If it can happen, it will happen” และตั้งชื่อล้อเลียนตาม Murphy ภายหลัง Stapp เองกลับนำวลีนี้ไปเผยแพร่ในเชิงบวก — เป็นหลักคิดเรื่องการวางแผนป้องกันความหายนะล่วงหน้า ไม่ใช่คำพูดในแง่ร้ายอย่างที่หลายคนเข้าใจ
น่าสังเกตว่าถ้อยคำดั้งเดิมของ Murphy เองมีความจำเพาะเจาะจงกว่าที่คนทั่วไปจำกัน คือประมาณว่า “ถ้ามีสองวิธีขึ้นไปในการทำสิ่งหนึ่ง และวิธีหนึ่งจะนำไปสู่หายนะ จะมีคนเลือกทำวิธีนั้น” นักวิทยาศาสตร์บางคน เช่น Richard Dawkins และ David Hand ก็ตั้งข้อสังเกตว่าปรากฏการณ์นี้อาจอธิบายได้ด้วย confirmation bias และ selection bias — เหตุการณ์ที่ไม่พึงประสงค์เกิดขึ้นตามความน่าจะเป็นปกติ แต่คนจะจดจำเฉพาะครั้งที่มันสร้างปัญหาเท่านั้น
ความหมายและนัยยะ
หัวข้อที่มีชื่อว่า “ความหมายและนัยยะ”ในบริบทซอฟต์แวร์ Murphy’s Law ไม่ได้บอกว่าจักรวาลมีเจตนาร้ายต่อโปรแกรมเมอร์ แต่สื่อถึงข้อเท็จจริงเชิงสถิติง่าย ๆ ว่า: ระบบที่มีขนาดใหญ่พอ มีผู้ใช้มากพอ หรือทำงานนานพอ จะเจอทุก edge case ที่เป็นไปได้ทางคณิตศาสตร์ในที่สุด ไม่ว่าจะเป็น null reference, race condition, network timeout, disk เต็ม, clock skew, หรือ input ประหลาดที่ผู้ใช้พิมพ์เข้ามา นัยยะเชิงปฏิบัติของกฎนี้คือ:
- อย่าคิดว่า “คงไม่มีใครทำแบบนั้นหรอก” — ถ้า input นั้นเป็นไปได้ทางเทคนิค ในที่สุดจะมีคนหรือระบบอื่นส่งมันเข้ามาจริง
- ความล้มเหลวมักเกิดในเวลาที่แย่ที่สุด — ระหว่าง demo สำคัญ ตอนพีคโหลดสูงสุด หรือกลางดึกวันหยุดยาวตอนไม่มีใครเฝ้าระบบ เพราะนั่นคือช่วงที่ระบบถูกใช้งานในรูปแบบที่ไม่เคยถูกทดสอบมาก่อน หรือถูกใช้งานหนักผิดปกติ
- การป้องกันล่วงหน้าถูกกว่าการแก้ไขทีหลัง — costs ของการเขียน validation, error handling และ test edge case ตั้งแต่ต้น ต่ำกว่าการ debug production incident ที่เกิดขึ้นแล้วมาก
กฎนี้จึงเป็นเหตุผลเชิงปรัชญาที่หนุนหลังแนวปฏิบัติหลายอย่างในวิศวกรรมซอฟต์แวร์ ตั้งแต่ defensive programming, การเขียนเทส (โดยเฉพาะเทส edge case และ negative case), การทำ code review, ไปจนถึงการมี rollback plan, monitoring/alerting และ on-call rotation ในระดับ DevOps — ทั้งหมดนี้คือการยอมรับว่า “มันจะพังแน่ ๆ สักวัน” แล้ววางระบบให้รับมือได้ ไม่ใช่หวังว่ามันจะไม่พัง
graph TD
A[Possible failure path exists] --> B{Path guarded}
B -->|No| C[Eventually triggered in production]
B -->|Yes| D[Validation or fail fast catches it]
C --> E[Incident and firefighting]
D --> F[Contained and logged safely]
ตัวอย่างในโลกจริง
หัวข้อที่มีชื่อว่า “ตัวอย่างในโลกจริง”- Windows 98 Blue Screen of Death ต่อหน้าสาธารณะ — วันที่ 20 เมษายน 1998 ที่งาน COMDEX ชิคาโก ผู้ช่วยของ Bill Gates ชื่อ Chris Capossela สาธิต feature Plug and Play โดยเสียบสแกนเนอร์เข้ากับเครื่องที่รัน Windows 98 บนเวที ผลคือเครื่องเด้ง Blue Screen ต่อหน้าผู้ชมนับพัน Gates พูดติดตลกว่า “That must be why we are not shipping Windows 98 yet” เหตุการณ์นี้เป็นตัวอย่างคลาสสิกที่ว่าความล้มเหลวมักโผล่มาในช่วงเวลาที่สำคัญและถูกจับตามองที่สุด (demo ต่อหน้าสื่อ) เพราะอุปกรณ์ที่ไม่เคยผ่านการทดสอบ (scanner รุ่นที่ทีม Windows ไม่เคยตรวจสอบ) ถูกใช้งานในเงื่อนไขที่ไม่คุ้นเคย หลังเหตุการณ์นี้ Microsoft ถึงกับสร้างห้องทดสอบ demo โดยเฉพาะ เพื่อ stress-test อุปกรณ์ก่อนขึ้นเวทีทุกครั้ง
- Null reference / unhandled exception ใน production — ทีมพัฒนาจำนวนมากเจอ
NullReferenceExceptionหรือundefined is not a functionจาก field ที่ “ไม่น่าจะว่างเปล่า” ตามสมมติฐานตอนออกแบบ แต่เมื่อระบบมีผู้ใช้เป็นล้าน ในที่สุดก็มี record ที่ field นั้นว่างจริง ๆ เพราะ data migration เก่า หรือ integration จากระบบภายนอกที่ส่งข้อมูลไม่ครบ - Deploy วันศุกร์ — วัฒนธรรม “no deploy on Friday” ในหลายทีมวิศวกรรมมาจากประสบการณ์ตรงว่า ถ้ามี bug แฝงอยู่ใน code ที่เพิ่ง deploy มันจะโผล่ตอนบ่ายวันศุกร์ ทำให้ทีมต้องแก้ไขฉุกเฉินในวันหยุดสุดสัปดาห์ที่คนพร้อม on-call น้อยที่สุด — สอดคล้องกับข้อสังเกตที่ว่าความล้มเหลวมักเกิดในเวลาที่แย่ที่สุด
- Load testing ที่มองข้าม edge case — ระบบผ่านการทดสอบ load ปกติมาตลอด แต่ล่มเมื่อเจอ traffic spike แบบเฉพาะเจาะจง (เช่น flash sale, ผลบอลออกพร้อมกันหลายล้านคน) เพราะเงื่อนไขนั้นไม่เคยถูกจำลองไว้ในการทดสอบ
บทเรียนที่นำไปใช้
หัวข้อที่มีชื่อว่า “บทเรียนที่นำไปใช้”- เขียน code แบบ defensive ตั้งแต่ต้น — ตรวจสอบ input, จัดการ null/empty case, ห่อ external call ด้วย try/catch หรือ timeout เสมอ อย่ารอให้ production บอกว่ามันพัง
- Fail fast แทนที่จะปล่อยให้ error ลุกลามเงียบ ๆ — ยิ่งตรวจพบปัญหาใกล้จุดกำเนิดเท่าไร stack trace ก็ยิ่งชัดเจน และผลกระทบต่อ state ของระบบก็ยิ่งน้อย
- เขียนเทสให้ครอบคลุม edge case ไม่ใช่แค่ happy path — ทดสอบ input ที่ผิดปกติ, ค่าที่เป็นขอบเขต (boundary), และสถานการณ์ concurrency ที่ไม่น่าจะเกิดแต่เป็นไปได้ทางเทคนิค
- วางแผนสำหรับตอนที่มันพังจริง ๆ ไม่ใช่แค่พยายามไม่ให้มันพัง — มี rollback plan, feature flag, monitoring/alerting, และ runbook สำหรับ on-call เพราะ Murphy’s Law บอกไว้แล้วว่าการป้องกัน 100% เป็นไปไม่ได้
- อย่า demo หรือ deploy feature ที่ยังไม่ผ่านการทดสอบในสภาพแวดล้อมจริงต่อหน้าความเสี่ยงสูง — บทเรียนจาก COMDEX 1998 คือให้ stress-test สถานการณ์ demo ในสภาพเดียวกับที่จะใช้จริงก่อนเสมอ
- มองกฎนี้เป็นเครื่องมือคิดเชิงความน่าจะเป็น ไม่ใช่โชคชะตา — ใช้มันเพื่อกระตุ้นให้ทีมถามคำถามว่า “ถ้าสิ่งนี้ผิดพลาด จะเกิดอะไรขึ้น และเราจัดการอย่างไร” มากกว่าจะยอมแพ้ด้วยความรู้สึกสิ้นหวัง