07 · วิศวกรรมซอฟต์แวร์
ชั้นนี้คือจุดที่โปรแกรมเลิกเป็นข้อความที่รันได้ และกลายเป็นวัตถุที่ต้องรอดจากการถูกแก้
ซอฟต์แวร์ไม่มีชิ้นส่วนที่เคลื่อนไหวสักชิ้น แล้วทำไมมันถึงผุ (rot)?
วันที่ 1 สิงหาคม 2012 Knight Capital บริษัทเทรดหุ้น ปล่อยโค้ดใหม่ลงเซิร์ฟเวอร์แปดเครื่องที่ส่งคำสั่งซื้อขายไปตลาดหุ้นนิวยอร์ก โค้ดไปถึงเจ็ดเครื่อง เครื่องที่แปดยังมีรูทีนชื่อ Power Peg ค้างอยู่ บริษัทเลิกใช้มันตั้งแต่ปี 2003 และไม่เคยลบทิ้ง โค้ดใหม่หยิบ feature flag ตัวเก่ามาใช้ซ้ำ และบนเครื่องนั้นเครื่องเดียว มันปลุกรูทีนที่ตายไปแล้วให้ทำงานอีกครั้ง ในเวลาราว 45 นาที Knight ยิงคำสั่งซื้อขายที่ไม่ได้ตั้งใจนับล้านรายการเข้าตลาด ขาดทุน 460 ล้านดอลลาร์ มากกว่ามูลค่าทั้งบริษัท คำสั่งของ Securities and Exchange Commission บันทึกรายละเอียดไว้อีกจุดหนึ่ง โค้ดที่หลับใหลตัวนี้ถูกแตะครั้งสุดท้ายในปี 2005 ตอนที่ฟังก์ชันที่มันพึ่งพาถูกย้ายไปอยู่ที่อื่น และไม่มีใครเทสต์ซ้ำว่าสองส่วนนั้นยังเข้ากันได้ (SEC Release 34-70694, 2013) Knight จ่ายค่าปรับ 12 ล้านดอลลาร์ และอยู่ไม่ถึงสิ้นปีในฐานะบริษัทอิสระ
ไม่มีบรรทัดไหนของ Power Peg เปลี่ยนไปเลยตลอดเก้าปี ความยากทั้งหมดของชั้นนี้อยู่ตรงนั้น ซอฟต์แวร์ไม่มีตลับลูกปืนให้สึก ไม่มีโลหะให้ล้า ไม่มีชิ้นส่วนหลุดพิกัดความคลาดเคลื่อน โปรแกรมที่ทิ้งไว้เฉยๆ จะคำนวณพรุ่งนี้เหมือนที่คำนวณวันนี้เป๊ะ สิ่งที่ขยับคือทุกอย่างรอบตัวมัน — ฮาร์ดแวร์ข้างล่าง ไลบรารีข้างๆ กฎระเบียบข้างบน และคนที่เคยเข้าใจมัน Meir Lehman ตั้งชื่อเรื่องนี้ไว้ในทศวรรษ 1970 เป็นกฎข้อแรกในกฎวิวัฒนาการซอฟต์แวร์ของเขา โปรแกรมที่ฝังอยู่ในโลกจริงต้องถูกปรับตัวต่อเนื่อง ไม่อย่างนั้นมันจะใช้การได้แย่ลงทีละน้อย กฎข้อสองคือหนามที่ซ่อนอยู่ท้ายเรื่อง เมื่อโปรแกรมแบบนี้วิวัฒนาการไป ความซับซ้อนของมันเพิ่มขึ้นเสมอ เว้นแต่จะมีคนตั้งใจลงแรงกดมันเอาไว้ (Lehman, Proc. IEEE 68(9), 1980) ผุ ไม่ใช่การเสื่อมของตัววัตถุ แต่คือพื้นใต้เท้ามันเคลื่อน
ศาสตร์นี้จึงไม่ใช่การศึกษาเรื่องการเขียนโปรแกรม วิศวกรของ Google สรุปความต่างไว้ในประโยคเดียว "วิศวกรรมซอฟต์แวร์คือการเขียนโปรแกรมที่ถูกอินทิเกรตไปตามเวลา" (software engineering is programming integrated over time) (Winters, Manshreck & Wright, 2020) การเขียนโปรแกรมผลิตสิ่งที่ทำงานได้ วิศวกรรมผลิตสิ่งที่ทำให้กลับมาทำงานได้อีก โดยคนอื่น หลังพื้นใต้เท้ามันเคลื่อนไปแล้ว สคริปต์ที่รันครั้งเดียวแล้วทิ้งไม่ต้องการอะไรจากชั้นนี้เลย ระบบจ่ายเงินเดือนที่ยังรันอยู่ในปี 2040 ต้องการทุกอย่าง
สิ่งแรกที่ชั้นนี้ต้องยอมรับคือความยากบางส่วนกำจัดไม่ได้ Fred Brooks ผู้เคยคุมโครงการ OS/360 ของ IBM — โครงการเขียนโปรแกรมที่ใหญ่ที่สุดในยุคนั้น และเป็นการดิ้นรนที่โด่งดัง — แบ่งงานออกเป็นสองส่วน ส่วนแรกคืองาน เนื้อแท้ (essential) หรือ "การปั้นแต่งโครงสร้างเชิงมโนทัศน์ที่ซับซ้อนซึ่งประกอบกันขึ้นเป็นตัวตนซอฟต์แวร์เชิงนามธรรม" (the fashioning of the complex conceptual structures that compose the abstract software entity) ส่วนที่สองคืองาน บังเอิญ (accidental) คือแรงงานที่แทนโครงสร้างเหล่านั้นด้วยภาษาหนึ่งแล้วเอาขึ้นเครื่องจริงให้ได้ (No Silver Bullet, 1986) เครื่องมือที่ดีขึ้นโจมตีความซับซ้อนโดยบังเอิญ (accidental complexity) และโจมตีได้ยอดเยี่ยม ทุกวันนี้ไม่มีใครสู้กับคอมไพเลอร์เป็นอาทิตย์อีกแล้ว แต่ความซับซ้อนโดยเนื้อแท้ (essential complexity) คือสเปกของสิ่งที่งานนั้นต้องทำจริงๆ พันกันยุ่งอยู่กับทุกอย่างอื่นที่มันต้องทำด้วย และไม่มีสัญกรณ์ไหนคลายข้อกำหนด (requirement) ที่พันกันให้กลายเป็นเรื่องง่ายได้ Brooks จึงทำนายว่าจะไม่มีความก้าวหน้าจุดเดียวที่ทำให้ดีขึ้นสิบเท่าภายในสิบปี เพราะส่วนที่เหลือไม่เคยเป็นเรื่องของเครื่องมือตั้งแต่ต้น
ความซับซ้อนกำจัดไม่ได้ แต่ กักไว้ ได้ และนี่คือไอเดียเชิงโครงสร้างที่ยิ่งใหญ่หนึ่งเดียวของศาสตร์นี้ David Parnas ถามว่าอะไรทำให้การแตกระบบเป็นมอดูล (module) ดี แล้วปฏิเสธคำตอบที่ดูชัดที่สุด คือมอดูลควรสะท้อนขั้นตอนของกระบวนการ เกณฑ์ของเขาเป็นอีกอย่าง มอดูลแต่ละตัวถูกกำหนดโดยการรู้การตัดสินใจเชิงออกแบบหนึ่งอย่างที่มันซ่อนไว้จากมอดูลอื่นทั้งหมด อินเทอร์เฟซของมันถูกเลือกให้เปิดเผยการทำงานภายในน้อยที่สุดเท่าที่จะทำได้ (Parnas, CACM 15(12), 1972) มอดูลคือการเดิมพันว่าอะไรจะเปลี่ยน ซ่อนการตัดสินใจที่จะเปลี่ยน การเปลี่ยนแปลงก็อยู่เฉพาะที่ เปิดมันออกมา การเปลี่ยนแปลงก็ลามไปทั่ว feature flag ของ Knight Capital คือความล้มเหลวแบบนี้ที่ปรากฏให้เห็นเป๊ะๆ ชื่อเดียวที่โค้ดใหม่ใช้ร่วมกับโค้ดที่ไม่มีใครจำได้ การเกาะเกี่ยว (coupling) ที่มีอยู่จริงในเครื่อง แต่ไม่มีอยู่ในหัวใคร
เครื่องมือที่ทำให้การเปลี่ยนแปลงรอดได้คือเทสต์ (test) และพลังของมันแคบกว่าที่ตาเห็นมาก ข้อสรุปของ Dijkstra ตรงเป๊ะ "การเทสต์โปรแกรมใช้แสดงการมีอยู่ของบั๊กได้ แต่ไม่มีทางใช้แสดงว่าบั๊กไม่มีอยู่ได้เลย!" (Program testing can be used to show the presence of bugs, but never to show their absence!) (EWD249) ชุดเทสต์ (test suite) ไม่ได้พิสูจน์อะไรเกี่ยวกับระบบทั้งระบบเลย สิ่งที่มันทำคือแปลงความหวัง — น่าจะยังทำงานได้อยู่ — ให้เป็นข้อกล่าวอ้างที่เจาะจงและตรวจซ้ำได้ด้วยกลไก ระบบใหญ่จะแก้ได้มากเท่าที่มันแบกข้อกล่าวอ้างแบบนั้นไว้ ระบบควบคุมเวอร์ชัน (version control) ทำงานเดียวกันนี้กับประวัติศาสตร์แทนพฤติกรรม นับจาก Git ในเดือนเมษายน 2005 (Torvalds) อดีตของโปรเจกต์กลายเป็นวัตถุที่คิวรีได้ และคำถามว่า "ทำไมบรรทัดนี้ถึงอยู่ตรงนี้" มีคำตอบที่อยู่ยืนกว่าคนเขียนมัน
หลักการข้อสุดท้ายคือข้อที่คนทำงานจริงค้นพบช้าที่สุด Melvin Conway เขียนไว้ใน Datamation เดือนเมษายน 1968 ว่า "องค์กรใดก็ตามที่ออกแบบระบบหนึ่ง … จะผลิตงานออกแบบที่มีโครงสร้างเป็นสำเนาของโครงสร้างการสื่อสารในองค์กรนั้น" (Any organization that designs a system … will produce a design whose structure is a copy of the organization's communication structure) (Conway's law) สองทีมที่แทบไม่คุยกันจะสร้างสองส่วนประกอบที่มีอินเทอร์เฟซบางเบา เปราะ และสเปกไม่ครบคั่นกลาง เพราะอินเทอร์เฟซนั้นคือแบบหล่อที่ซื่อตรงต่อบทสนทนาของพวกเขาเอง สถาปัตยกรรมจึงเป็นการตัดสินใจเรื่องอัตรากำลังคนอยู่ส่วนหนึ่ง และการปรับโครงสร้างองค์กรคือการเปลี่ยนงานออกแบบแบบเงียบๆ
ศาสตร์นี้ตั้งชื่อให้ตัวเองในปีเดียวกันนั้น ที่การประชุม NATO ณ Garmisch เดือนตุลาคม 1968 คำว่า "software engineering" ถูกเลือกอย่างตั้งใจให้ยั่วยุ เป็นการยืนยันว่างานนี้ ควร ตั้งอยู่บนทฤษฎีและวินัยแบบเดียวกับวิศวกรรมที่ตั้งตัวมานาน พูดในวันที่เห็นกันชัดๆ ว่ามันยังไม่เป็นอย่างนั้น (Naur & Randell, eds., 1969) เกือบหกสิบปีผ่านไป ความมุ่งหวังยังทำงานหนักกว่าความสำเร็จ และคนในวงการที่ซื่อตรงก็พูดแบบนั้น
เชื่อมโยง
ใต้ชั้นนี้คือบทที่ 05 และ 06 ระบบปฏิบัติการกับภาษาโปรแกรมให้โปรแกรมที่รันถูกต้องได้หนึ่งครั้ง และทุกอย่างในบทนี้เริ่มหลังจากนั้น บทที่ 02 ให้ความถูกต้องกับอัลกอริทึมเดี่ยวหนึ่งตัว บทนี้คือสิ่งที่เกิดขึ้นเมื่ออัลกอริทึมหมื่นตัวต้องถูกแก้โดยคนที่ไม่เคยเจอหน้ากัน เหนือขึ้นไปและข้างๆ บทที่ 08, 09 และ 10 คือโดเมนที่วินัยเหล่านี้ถูกทดสอบหนักที่สุด เพราะฐานข้อมูล เครือข่าย หรือระบบกระจาย หยุดแล้วเขียนใหม่ไม่ได้ บทที่ 11 คือกรณีที่การรั่วไม่ได้แพงอย่างเดียว แต่เป็นปฏิปักษ์ด้วย
คำถามที่ยังเปิดอยู่
ฐานหลักฐานของศาสตร์นี้เองยังบางมาก ทุกคนอ้างตัวเลขว่าต้นทุนซอฟต์แวร์กี่เปอร์เซ็นต์เป็นการบำรุงรักษา (maintenance) แทนที่จะเป็นการสร้างครั้งแรก ตัวเลขแกว่งตั้งแต่หกสิบไปจนเกินเก้าสิบเปอร์เซ็นต์ โดยไม่มีนิยามร่วมหรือหน้าต่างการวัดรองรับอยู่ข้างหลัง ศาสตร์ที่วัดข้อกล่าวอ้างหลักของตัวเองหลวมขนาดนี้ควรรู้สึกไม่สบายใจ ลึกลงไปอีก โค้ดที่เครื่องผลิตขึ้นเองขยับสัดส่วนของ Brooks จริงไหม หรือมันแค่ทำให้ครึ่งบังเอิญถูกลง โดยไม่แตะครึ่งเนื้อแท้ที่ยังพันกันยุ่งอยู่ — และถ้าการอ่านกับรีวิวโค้ดกลายเป็นคอขวด นั่นคือชั้นใหม่หรือชั้นเดิม? และถ้ากฎของคอนเวย์ (Conway's law) เป็นจริง มันใช้ย้อนกลับได้ไหม — สร้างองค์กรที่จะผลิตสถาปัตยกรรมแบบนั้นขึ้นมาก่อน แล้วจะได้สถาปัตยกรรมที่ต้องการหรือเปล่า?
อ่านต่อ
- Titus Winters, Tom Manshreck & Hyrum Wright, Software Engineering at Google, O'Reilly, 2020 — คำอธิบายยุคใหม่ที่ชัดเจนที่สุดของมุมมองแบบอินทิเกรตไปตามเวลา อ่านฟรีฉบับเต็มได้ที่ abseil.io
- D. L. Parnas, "On the Criteria To Be Used in Decomposing Systems into Modules," Communications of the ACM 15, no. 12 (1972): 1053–1058 — ฉบับเต็ม
- Frederick P. Brooks, Jr., "No Silver Bullet — Essence and Accident in Software Engineering," IFIP Congress, 1986 — รายงานทางเทคนิคของ UNC