การเรียนรู้สถาปัตยกรรมซอฟต์แวร์
(matklad.github.io)- การออกแบบซอฟต์แวร์ เรียนรู้ได้ลึกซึ้งกว่าจากการรับผิดชอบในโปรเจ็กต์จริงและเมื่อปัญหากลายเป็นเรื่องที่ต้องจัดการด้วยตัวเอง มากกว่าจากการเรียนแบบบรรยาย
- Conway’s Law คือมุมมองที่ว่าซอฟต์แวร์สะท้อนโครงสร้างทางสังคมขององค์กร และความต่างระหว่างโค้ดทางวิทยาศาสตร์กับโค้ดอุตสาหกรรมก็อาจเกิดจากโครงสร้างแรงจูงใจ
- rust-analyzer ทำให้ ผู้มีส่วนร่วมประสิทธิภาพสูง โฟกัสได้ง่ายขึ้นด้วยการบิลด์ที่รวดเร็ว รองรับ stable ตัด dependency ของ C ออก และมีการทดสอบที่ใช้เวลาเพียงไม่กี่วินาที
- rust-analyzer ปกป้องฟังก์ชันอิสระด้วย
catch_unwindและตั้งเกณฑ์สำหรับ PR ให้ต่ำลง แต่ใช้มาตรฐานคุณภาพที่เข้มงวดกว่ามากกับ spine หลัก - โครงสร้างเชิงทดลองอาจกลายเป็นความจริงระยะยาวได้ และ rust-analyzer ก็พัฒนาจาก ต้นแบบ สถาปัตยกรรม LSP ไปสู่การต้องดูแลคอมไพเลอร์อีกตัวหนึ่ง
การออกแบบซอฟต์แวร์เรียนรู้ได้ดีที่สุดจากการลงมือจริง
- การออกแบบซอฟต์แวร์ เรียนรู้ได้ดีกว่าจากการรับผิดชอบในโปรเจ็กต์จริงและแก้ปัญหาด้วยตัวเอง มากกว่าจากการเรียนอย่างเป็นทางการ
- มากกว่าตอนรับบท “สถาปนิก” ในวิชาออกแบบของมหาวิทยาลัยและโปรเจ็กต์ในคอร์ส การเรียนรู้เริ่มจริงจังเมื่อปัญหาด้านการออกแบบกลายเป็นเรื่องของตัวเองใน IntelliJ Rust ซึ่งเป็นโปรเจ็กต์ภาคสนามลำดับที่สอง
- ใน IntelliJ Rust มีความผิดพลาดอยู่บ้าง แต่ไม่ถึงขั้นร้ายแรง และกระบวนการนั้นทำให้เรียนรู้อะไรได้มาก
- วิศวกรรมซอฟต์แวร์ ก็มีด้านที่เรียบง่ายพอสมควรจนคนที่มีความอยากรู้อยากเห็นสามารถเรียนรู้ได้ด้วยการคิดจากหลักการและอ่านงานเขียนหลากหลายชิ้น
โครงสร้างแรงจูงใจกับ Conway’s Law
- Conway’s Law คือมุมมองที่ว่าซอฟต์แวร์สะท้อนโครงสร้างทางสังคมขององค์กรที่สร้างมันขึ้นมา
- ความต่างระหว่างซอฟต์แวร์อุตสาหกรรมกับโค้ดทางวิทยาศาสตร์อาจไม่ได้มาจากความรู้เรื่องการสร้างซอฟต์แวร์เอง แต่เกิดจาก โครงสร้างแรงจูงใจ ที่ทำให้ผู้คนสร้างซอฟต์แวร์
- สถานการณ์อย่าง “นักศึกษาปริญญาเอกที่ต้องตีพิมพ์งานภายใน 3 เดือน” อาจเป็นปัจจัยสำคัญที่กำหนดลักษณะของโค้ดทางวิทยาศาสตร์
- โดยใหญ่ ๆ แล้วมีสองวิธีในการรับมือกับโครงสร้างแรงจูงใจ
-
ออกแบบหรือขยับแรงจูงใจของโปรเจ็กต์
- โอกาสในการออกแบบหรือปรับ โครงสร้างแรงจูงใจ ของโปรเจ็กต์มีไม่บ่อย แต่ถ้ามี ผลกระทบจะสูงมาก
- แก่นของ TIGER_STYLE ไม่ได้อยู่ที่รายการกฎเอง แต่อยู่ที่ บริบททางสังคม ที่ทำให้กฎเหล่านั้นกลายเป็นตัวเลือกที่ดี
-
ถ้าเปลี่ยนไม่ได้ ก็ปรับตัวเข้ากับข้อจำกัด
- โครงสร้างแรงจูงใจแทบไม่เคยถูกมอบมาในแบบที่ต้องการ และถ้าเปลี่ยนไม่ได้ ก็ต้องปรับตัวให้เข้ากับมัน
- แม้แต่ในโปรเจ็กต์ซอฟต์แวร์อุตสาหกรรม ก็มักแทบไม่มี “เวลาพอจะทำให้ถูกต้องจริง ๆ” และต้องทำให้ดีที่สุดภายใต้ข้อจำกัดที่มี
วิธีที่ rust-analyzer จับคู่โครงสร้างกับผู้มีส่วนร่วม
- rust-analyzer เป็นโปรเจ็กต์ที่มีทั้งความลึกและความกว้าง
- ในด้านที่ ลึก ด้วยความเป็นคอมไพเลอร์ มันสามารถดึงดูดผู้มีส่วนร่วมที่เก่งและทุ่มเทได้
- ในด้านที่ กว้าง IDE แบบดั้งเดิมมีฟังก์ชันเฉพาะทางตามจุดประสงค์อยู่มาก จึงเหมาะกับคนที่กำลังเรียน Rust หรือผู้มีส่วนร่วมช่วงสุดสัปดาห์ที่เข้ามาไม่สม่ำเสมอและอยากใช้เวลาสักหนึ่งหรือสองชั่วโมงแก้ความไม่สะดวกของตัวเอง
- เหตุผลที่ rust-analyzer ยืนกรานว่าไม่ควรต้องบิลด์
rustcต้องบิลด์ได้บน stable ไม่มี dependency ของ C และให้ชุดทดสอบทั้งหมดจบได้ภายในไม่กี่วินาที ก็เพื่อดึงดูด ผู้มีส่วนร่วมประสิทธิภาพสูง - มีความพยายามขัดเกลาระบบบิลด์ เพื่อให้คนสามารถโฟกัสกับงานอย่าง borrow checker ได้โดยไม่ต้องเสียสมาธิกับเรื่องอื่น
- เพื่อดึงดูดผู้มีส่วนร่วมช่วงสุดสัปดาห์ ภายในของ rust-analyzer ถูกแบ่งออกเป็น ฟังก์ชันอิสระ หลายส่วน และแต่ละส่วนได้รับการปกป้องที่รันไทม์ด้วย
catch_unwind - เกณฑ์สำหรับ PR ของฟังก์ชันถูกลดลงเหลือแค่ “เส้นทางการทำงานปกติใช้งานได้และมีเทสต์” และมองว่ายอมรับได้แม้โค้ดส่วนนั้นจะล่ม
- แต่มีสองเงื่อนไข
- ปัญหาด้านคุณภาพต้องถูก กักไว้ภายในฟังก์ชันเดี่ยว และไม่ลามไปยังส่วนอื่น
- การล่มที่รันไทม์ต้องไม่ปรากฏต่อผู้ใช้ และเพื่อให้เป็นเช่นนั้น ฟังก์ชันของ rust-analyzer จึงทำงานบน snapshot แบบ immutable และต้องไม่สามารถทำให้ข้อมูลปนเปื้อนได้
- ในทางกลับกัน spine หลักที่ค้ำจุนฟังก์ชันต่าง ๆ ถูกบังคับใช้มาตรฐานคุณภาพที่เข้มงวดกว่ามาก
ความเสี่ยงที่โครงสร้างเชิงทดลองจะกลายเป็นความจริงระยะยาว
- เมื่อเลือกปรับตัวแทนการแก้โครงสร้างแรงจูงใจ ต้องระวังว่าภายภาคหน้าเต็มไปด้วยความไม่แน่นอน และมักกลายเป็นจริงในแบบที่น่ารำคาญที่สุด
- แรงจูงใจดั้งเดิมของ rust-analyzer คือหลีกเลี่ยงการต้องเขียนคอมไพเลอร์คู่ขนานเพิ่มอีกตัวภายใน IntelliJ Rust และตรวจสอบ ต้นแบบ ของสถาปัตยกรรมที่ดีกว่าสำหรับ LSP เพื่อนำสิ่งที่เรียนรู้กลับไปสู่
rustc - เพราะอย่างนั้น โค้ดจึงมีความ เป็นงานทดลอง สูงมาก แม้กระทั่งในแกนหลัก
- ผลลัพธ์คือสุดท้ายก็ต้องดูแลคอมไพเลอร์อีกตัวหนึ่งอยู่ดี
- ในทำนองเดียวกัน โปรเจ็กต์ uutils ก็เริ่มจากการเป็นจุดหมายหลักสำหรับคนที่อยากเรียน Rust ก่อนจะกลายเป็น implementation ของ coreutils สำหรับ Ubuntu
แหล่งข้อมูลและหนังสือที่น่าอ้างอิง
- ไม่มี หนังสือเล่มเดียวที่มีคำตอบถูกทั้งหมด และดูเหมือนว่าประสบการณ์จริงเป็นองค์ประกอบที่ขาดไม่ได้
- Boundaries by Gary Bernhardt
- เป็นแหล่งข้อมูลที่ให้คำแนะนำเชิงรูปธรรมอย่างแข็งแรง และจุดประกายการสำรวจในระดับที่สูงขึ้น
- How to Test
- เข้าใจความสำคัญของการทดสอบได้ทันที แต่กว่าจะยอมรับว่าคำแนะนำเรื่องเทสต์ที่ถูกอ้างถึงอย่างแพร่หลายจำนวนมากนั้นใช้จริงไม่ได้ และกว่าจะทำให้เป็นแนวคิดที่ใช้งานได้จริง ก็ใช้เวลานาน
- ∅MQ guide และ งานเขียนของ Pieter Hintjens
- เป็นแหล่งข้อมูลที่ทำให้ได้รู้จักวิธีคิดแบบ Conway’s Law
- สถาปัตยกรรมการพัฒนาฟังก์ชันของ rust-analyzer เป็นรูปแบบที่ประยุกต์ใช้ optimistic merging
- Reflections on a decade of coding by Jamii
- กล่าวถึงเนื้อหาที่เป็นเมตาอย่างมาก และยอดเยี่ยมจนถูกวางไว้เป็นรายการแรกใน ชุดลิงก์รวม
- บล็อกของ Ted Kaminski
- อยู่ในรูปแบบบันทึกสำหรับหนังสือที่ไม่มีอยู่จริง และใกล้เคียงที่สุดกับการมีทฤษฎีที่สอดคล้องกันเกี่ยวกับการพัฒนาซอฟต์แวร์
- Software Engineering at Google และ The Philosophy of Software Design ของ Ousterhout
- เป็นหนังสือดี ๆ ที่มักถูกแนะนำอยู่เสมอ โดยเฉพาะ Software Engineering at Google ที่ช่วยในการจัดระเบียบ ชื่อเรียกสำคัญเกี่ยวกับ unit test และ integration test
- แต่ในมุมมองส่วนตัวก็ไม่ได้เป็นหนังสือที่พลิกโลกแต่อย่างใด
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ถ้าจะสรุปเป็นชีตสั้น ๆ การออกแบบที่ดีควรมีแนวคิดหลักหนึ่งเดียวซึมอยู่ทั่วทั้งระบบและ ลดความประหลาดใจให้น้อยที่สุด
ถ้าระบบเปิดทางไว้ ผู้คนก็จะใช้มันแบบนั้นในที่สุด และวิธีแก้ที่เริ่มต้นด้วย “ถ้าทุกคนช่วยทำแค่ X” นั้นไม่ใช่วิธีแก้จริง ๆ
ควรแยกส่วนที่แปลงข้อมูลออกจากส่วนที่ใช้งานข้อมูล โมเดลข้อมูลมีอายุยืนกว่าตัวโค้ด และ coupling คือรากของปัญหาหลายอย่าง
การจัดการเวอร์ชันเป็นสิ่งที่หลีกเลี่ยงไม่ได้ state ต้องถูกแสดงอย่างชัดเจน และข้อมูลแต่ละอย่างควรมีแหล่งความจริงเพียงหนึ่งเดียว
ควรใช้เวลากับการตั้งชื่อให้มากขึ้น ถ้าทดสอบยากแปลว่าออกแบบผิด และการตัดสินใจที่ไม่ถูกบันทึกไว้จะกลายเป็นเรื่องที่ต้องเสียใจภายหลัง
การสื่อสารมีต้นทุน จึงควรมีเหตุผลก่อนจะจ่ายมัน และงานของวิศวกรคือการแก้ปัญหาด้วยหลักประสบการณ์ภายใต้ข้อมูลที่ไม่สมบูรณ์
ตัวบทความเองก็ดูไม่ค่อยสอดคล้องกันนักในมุมมองของสถาปัตยกรรมซอฟต์แวร์
มุมมองสถาปัตยกรรมแบบ 4+1 เป็นกรอบแนวคิดที่ดีสำหรับการคิดภาพใหญ่ถ้าตัด UML ออกไป และซีรีส์ Pattern-Oriented Software Architecture ก็สรุปสถาปัตยกรรมหลายแบบที่ผู้คนไปถึงได้ดี
Grady Booch เคยทำคู่มือสถาปัตยกรรมซอฟต์แวร์อยู่พักหนึ่ง แต่ตอนนี้เหมือนแทบหยุดไปแล้ว และตอนนั้นใน mailing list ก็มีการบันทึกสถาปัตยกรรมระบบขนาดใหญ่ของบริษัทหรือโครงการโอเพนซอร์สขนาดใหญ่ไว้ด้วย
ถ้าขุดแหล่งข้อมูลพวกนี้ลงไป จะเห็นว่าสถาปัตยกรรมถูกสร้างขึ้นโดยมีจุดเน้นต่างกัน เช่น ขนาด ความปลอดภัย ประสิทธิภาพ การทำงานร่วมกันได้ และ fail-safe และแต่ละเป้าหมายก็มี trade-off ที่สมจริงของมัน
ถ้าดีกว่าตามเกณฑ์นี้ ต่อให้ดูเหมือนการออกแบบแย่ ก็อาจเป็นการออกแบบที่ดีจริง ๆ
interface ควรถูกทำให้ใช้อย่างถูกต้องได้ง่าย และใช้ผิดได้ยาก ต้องคิดด้วยว่าคนที่ไม่รู้จักโปรเจกต์จะใช้มันอย่างไร
โค้ดที่ถูกต้องควรเขียนได้ง่าย โค้ดน่าสงสัยควรสังเกตเห็นได้ง่าย และควรดัน bug ไปทางซ้ายให้มากที่สุด
การกำจัด กลุ่มของ bug ดีกว่าการแก้ bug ทีละตัว และเพราะ interface เปลี่ยนยากกว่า implementation ถ้า interface ถูกต้อง implementation ที่หน้าตาไม่สวยก็ยอมรับได้
คอมเมนต์และเอกสารควรอธิบายว่าเหตุใดโค้ดจึงมีรูปแบบนั้น และถ้ามีวิธีที่ดูง่ายกว่าบนผิวเผินแต่ใช้ไม่ได้เพราะข้อจำกัดบางอย่าง ก็ควรบันทึกเรื่องนั้นไว้
ในมุมมองของข้อมูล ไม่ควรทำซ้ำ เพราะถ้าเก็บข้อเท็จจริงเดียวกันไว้หลายที่ สุดท้ายมันจะไม่ตรงกันและเกิด bug
การออกนอกทางที่คนส่วนใหญ่ใช้กันมีต้นทุน จะทำก็ได้ถ้ามันคุ้มจริง แต่ไม่ควรประเมินต้นทุนนั้นต่ำเกินไป
เทคโนโลยีธรรมดา ๆ ที่น่าเบื่อซึ่งดูแย่กว่า อาจเป็นเทคโนโลยีที่ดีกว่าก็ได้ และไม่ควรถามแค่ว่า “คุ้มค่าจะทำไหม?” แต่ควรถามว่า “เมื่อเทียบกับการทำอย่างอื่นแล้วคุ้มไหม?” โดยมองเป็น expected value
ต่อให้คิดว่าตัวเองฉลาดกว่าคนอื่น ก็ยังมีปัญหาที่แก้ด้วยความฉลาดอย่างเดียวไม่ได้ และบางปัญหาก็มองไม่เห็นจนกว่าจะระเบิดจริง จึงควรเรียนรู้จากความผิดพลาดของคนอื่น
แรงเสียดทานคือฆาตกรเงียบ
การวางแผนเป็นเรื่องดี แต่บางครั้งก็ต้องลองทำด้วยตัวเอง และทุกอย่างมีต้นทุน
ถ้าออกแบบโดยไม่คำนึงถึงต้นทุน สุดท้ายจะถูกบังคับให้เลือกทางยากในภายหลัง
ต่อให้เป็นโปรเจกต์ที่ทำคนเดียว ข้อจำกัดของเอนจินก็ยังกลายเป็นตัวชี้ทางว่า “ฟีเจอร์ใหม่แปลก ๆ ที่โผล่มาตอนทดสอบ ควรเพิ่มเข้าไปแบบนี้”
จึงไม่ต้องคอยถือเอกสารมหึมาประเภท “ถ้าจะทำ sound effect ใหม่ที่ถูกเรียกหลายครั้งระหว่างการเปลี่ยน state แบบหนึ่ง ให้ทำแบบนี้” ตลอดเวลา
ผู้ออกแบบระบบต้องเข้าใจอุตสาหกรรม ไม่จำเป็นต้องรับศัพท์หรือวิธีทำแบบจำลองของพวกเขามาทั้งหมด แต่ต้องเข้าใจเหตุผลและมุมมองที่พวกเขาใช้มอง dataset
มีบางส่วนที่เราจงใจทำให้ความซับซ้อนของตลาดการแพทย์ง่ายลง เพื่อตัดการนิยามเกินจำเป็นออกและให้โมเดลที่รวมศูนย์มากขึ้น แต่การเปลี่ยนแบบนั้นทำได้อย่างมั่นใจเพราะเราเข้าใจโดเมนปัญหาดีพอ
โดยเฉพาะ ชื่อนั้นแทบไม่ตาย
บางครั้งมันก็ตายได้ แต่การเปลี่ยนชื่อจำเป็นต้องใช้ความพยายามระดับสุดโต่ง ดังนั้นการให้ผู้เชี่ยวชาญโดเมนช่วยทบทวนตัวเลือกชื่ออยู่นาน ๆ เพื่อเช็กว่ายังตกหล่นอะไรไหม จึงคุ้มค่ามาก
บางแนวคิดอาจผลักดันให้ผ่านได้ แต่ฝั่งธุรกิจอย่างฝ่ายขายหรือการตลาดจะยังคงเรียกร้องคำศัพท์ของอุตสาหกรรมอยู่เรื่อย ๆ และกดดันให้โมเดลสะท้อนมุมมองปัจจุบันของอุตสาหกรรม
ถ้าตัดสินใจจะหักออกจากกระแสนั้น ความขาดตอนนั้นต้องมีเจตนาและเป้าหมายที่ชัดเจน
คุณสมบัติที่ควรเน้นมากที่สุดในซอฟต์แวร์คือ ความสามารถในการบำรุงรักษา
ค่าใช้จ่ายในการสร้างเป็นคำถามหนึ่ง แต่ค่าใช้จ่ายในการใช้งานจริงมีผลมากกว่ามาก เพราะไม่ได้มีแค่โครงสร้างพื้นฐาน แต่รวมถึงคำขอฟีเจอร์ที่สะสมขึ้น การ refactor โค้ด และการคงเวอร์ชันของซอฟต์แวร์ third-party ด้วย
รายการแนะนำพวกนี้มักดี เช่น A Philosophy of Software Design ของ Ousterhout แต่โดยรวมแล้วใกล้เคียงกับ แนวคิดทั่วไปของการพัฒนาซอฟต์แวร์ มากกว่าสถาปัตยกรรมซอฟต์แวร์โดยตรง
ถ้าอยากดูสถาปัตยกรรม แนะนำงานคลาสสิกอย่าง Software Architecture: Perspectives on an Emerging Discipline ของ Shaw/Garlan และงานเขียนของ Mary Shaw
งานวิจัยใหม่อย่าง Myths and Mythconceptions: What Does It Mean to Be a Programming Language, Anyhow? หรือ Revisiting Abstractions for Software Architecture and Tools to Support Them ที่สำรวจว่าทำไมวงการสถาปัตยกรรมซอฟต์แวร์จึงไม่เป็นไปตามที่คาดไว้ก็น่าสนใจ
ในเชิงปฏิบัติ การดูว่า Unix pipe และ filter หรือ REST ประสบความสำเร็จเพราะอะไร และล้มเหลวตรงไหนเพราะอะไร ก็มีประโยชน์มาก และ Hexagonal Architecture ก็เป็นแกนสำคัญ
ส่วนตัวผมยังมี Beyond Procedure Calls as Component Glue: Connectors Deserve Metaclass Status ซึ่งพยายามเชื่อมสถาปัตยกรรมซอฟต์แวร์เข้ากับ metaobject protocol เพื่อมองเป็นรากฐานใหม่ของภาษาโปรแกรมและการเขียนโปรแกรม
มันเป็นงานตอบกลับต่อ Procedure Calls Are the Assembly Language of Software Interconnection: Connectors Deserve First-Class Status ของ Mary Shaw โดยตั้งคำถามว่า ถ้า procedure call คือภาษาแอสเซมบลี แล้วภาษาระดับสูงจะหน้าตาเป็นอย่างไร
บางทีสถาปัตยกรรมซอฟต์แวร์อาจยังมีอนาคตที่สดใสและใช้งานได้จริงมากกว่าที่คิด
แค่มีโครงสร้างข้อมูลและ type อยู่ไม่กี่อย่าง พร้อมชุดฟังก์ชันพื้นฐานเล็ก ๆ แล้วก็นำมาประกอบกันก็พอ
อย่างหนึ่งที่ผมชอบใน Lisp คือ type ที่ซับซ้อนกว่านั้น โดยเฉพาะ type ที่มาจาก FFI จะทึบแสงอยู่เสมอ
ผมอยากเห็น การทำ CLOS ที่ถ้าเรากำหนด struct แบบภาษาคล้าย C แล้วจะได้ชุดฟังก์ชันมาตรฐานติดมาด้วย
วิธีที่ดีที่สุดในการเรียนรู้สถาปัตยกรรมไม่ใช่การสร้างโปรเจกต์ที่ใหญ่พอ แต่คือการ บำรุงรักษา มัน
และควรทำกับอย่างน้อยสองหรือสามโปรเจกต์
ถ้าโปรเจกต์เล็กเกินไป สถาปัตยกรรมแบบไหนก็ดูใช้ได้ดี และคำว่า “ใหญ่” ก็มักตัดสินจากจำนวนคนที่เคยทำกับมัน หรือดีกว่านั้นคือจำนวนทีม มากกว่าจำนวนบรรทัดโค้ด
ควรมีอย่างน้อยสองโปรเจกต์ที่ต่างกันเพื่อให้เทียบกันได้ และผมก็เคยเห็นคนที่ติดอยู่กับโปรเจกต์เดียวเป็นสิบ ๆ ปีจนไม่รู้วิธีแก้ปัญหาแบบสมัยใหม่
แต่ในความเป็นจริง คนที่สร้างโปรเจกต์มักได้เลื่อนขั้นไปเป็นสถาปนิก มากกว่าคนที่คอยดูแลบำรุงรักษา
ที่ Google ยิ่งชัด เพราะต้องออกของใหม่ถึงจะเลื่อนขั้นได้ งานบำรุงรักษาไม่ช่วยเรื่องเลื่อนขั้น และถ้าเป็นไปได้ การย้ายออกหลังปล่อยของไม่นานมักได้เปรียบกว่า
น่าแปลกที่คนที่อยู่ในตำแหน่งดีที่สุดสำหรับการเป็นสถาปนิก อาจเป็นผู้รับเหมาภายนอกที่ถูกส่งมาให้ดูแลโปรเจกต์เก่าที่ไม่มีใครในบริษัทอยากรับ
คนพวกนี้ต้องรักษาสถาปัตยกรรมไว้ และได้ผ่านหลายโปรเจกต์ จึงเปรียบเทียบกันได้
แต่ถ้าคิดค่าจ้างเป็นรายชั่วโมง ก็มีความเสี่ยงว่าจะทำให้สถาปัตยกรรมซับซ้อนเกินจำเป็นเพื่อจะได้คิดเวลาเพิ่ม
ในบริบทแบบนี้ ผมแนะนำ Architecture of Open Source Applications อย่างยิ่ง
เป็นชุดหนังสือที่แต่ละบทเขียนโดยผู้ดูแลโปรเจกต์นั้น ๆ จึงเรียนรู้สถาปัตยกรรมผ่านตัวอย่างได้
ไม่ได้เห็นแค่ว่าสถาปัตยกรรมคืออะไร แต่ยังเห็นข้อจำกัดที่หล่อหลอมมันขึ้นมา ซึ่งมักเป็นเรื่องประวัติและวิสัยทัศน์ของโปรเจกต์ที่เปลี่ยนไปด้วย
ด้วยข้อจำกัดของหนังสือรวมบทจากผู้เขียนหลายคน ไม่ใช่ทุกบทจะดีหรือชวนสนใจเท่ากัน และทั้งหมดก็ค่อนข้างเก่าแล้ว แต่ก็ยังคุ้มค่าจะอ่าน
http://aosabook.org/
ผมอยากใช้เวลากับการทำความเข้าใจ mental model ของโปรเจกต์ที่กำลังทำให้ดีกว่านี้ แต่พอเริ่มรู้สึกไม่ชอบภาษาโปรแกรม การเลือกสถาปัตยกรรมบางอย่าง หรือส่วนที่ซับซ้อนจนดูเหมือนไม่คุ้มจะเสียเวลา แรงจูงใจก็ลดลงมาก
มันขึ้นอยู่กับโปรเจกต์ แต่การทำงานเป็น “full-stack developer” ให้ความรู้สึกเหมือนมันดึงความสนุกของการเขียนโปรแกรมออกไป
ตอนนี้ก็ใช้เวลา 40 ชั่วโมงต่อสัปดาห์ไปกับการมองโปรเจกต์ที่น่าเบื่อที่สุดเท่าที่จะจินตนาการได้อยู่แล้ว
ไม่มีโปรเจกต์ไหนสมบูรณ์แบบไร้ที่ติเลยสักโปรเจกต์
และถ้าภาษาโปรแกรมเป็นปัญหาใหญ่ขนาดนั้น เปลี่ยนเรืออาจจะดีกว่า
ผมคิดว่าทุกคนควรใช้ได้หลายภาษา แต่สุดท้ายการเลือกก็เป็นเรื่องของคุณเอง
ต้องแน่ใจว่าการตัดสินใจที่ใช้เวลามากที่สุดคือการตัดสินใจที่สำคัญที่สุดจริง ๆ
โครงสร้างข้อมูลที่ออกแบบดีส่งผลต่อประสิทธิภาพและความสามารถในการบำรุงรักษามากกว่ากรอบงาน ภาษา หรือแพลตฟอร์มอย่างมหาศาล
ส่วนตัวผมต้องทำงานร่วมกับ ADHD ทุกวัน จึงต้องคอยผลักให้เกิดความคืบหน้า ซึ่งหมายถึงเลือกปิดการตัดสินใจที่ไม่สำคัญ เพื่อจะได้โฟกัสกับพื้นที่ปัญหาที่เหลือซึ่งต้องใช้การคิดอย่างรอบคอบ
คำอย่าง “clean code” หรือ “beautiful code” ไม่ค่อยช่วยให้จูเนียร์เรียนรู้แนวปฏิบัติที่ดีของสถาปัตยกรรมซอฟต์แวร์ได้เท่าไร
ถ้าจูเนียร์ถามว่าทำไมถึงใช้ ORM แล้วซีเนียร์ตอบว่า “เพราะมันสะอาดกว่า” สิ่งที่เหลืออยู่ก็มีแต่เครื่องหมายคำถาม
สู้กำหนดรายการเป้าหมายที่ชัดเจนไปเลยดีกว่า
เกณฑ์อย่างความสามารถในการบำรุงรักษา ประสิทธิภาพและความสามารถในการขยายตัว ประสิทธิผล ความยืดหยุ่นต่อความล้มเหลว observability การทดสอบได้และมีการทดสอบ ความปลอดภัย และการที่นักพัฒนาใหม่อ่านเข้าใจง่าย ล้วนต้องถ่วงดุลกัน
ยิ่งเพิ่มเกณฑ์มากขึ้น เวลาลังเลก็ยิ่งเลือกได้ดีขึ้น และมันยังมีความหมายกับคนนอกทีมพัฒนาด้วย จึงทำให้ตกลงกับลูกค้าได้ว่าพวกเขากำลังจ่ายเงินเพื่ออะไร
โปรเจกต์ใช้เวลาส่วนใหญ่ของวงจรชีวิตอยู่ในโหมดบำรุงรักษา ซึ่งจริง ๆ แล้วเป็นสัญญาณที่ดีว่าโปรเจกต์ประสบความสำเร็จ ดังนั้น ความสามารถในการบำรุงรักษา ก็สามารถนิยามให้ชัดได้
จุดเริ่มต้นที่ดีคือความสามารถในการเพิ่มฟีเจอร์ใหม่โดยไม่ทำลายสถาปัตยกรรม หรือถ้าให้ดีขึ้นไปอีกคือไม่ทำลายแม้แต่ signature ของเมธอดเดียว
ต้องระวังเรื่อง abstraction อย่างมาก
คำกล่าวที่ว่า “abstraction มักซ่อนว่าของที่คุณต้องการนั้นจริง ๆ เรียบง่ายแค่ไหน” นั้นถูกต้อง และ ORM ก็เป็นตัวอย่างตรงตัว
ในกรณีส่วนใหญ่ ข้อมูลควรถูกปฏิบัติเป็นพลเมืองชั้นหนึ่ง และยังช่วยให้ทำงานร่วมกับ DBA ได้ดีขึ้นด้วย
การไม่ตกหลุม premature optimization แต่ก็ยังคิดถึงกรณีนอก happy path ไปพร้อมกันเป็นเรื่องท้าทาย และมันช่วยกันไม่ให้เราพุ่งเข้าไปทำไอเดียที่ดูดีโดยไม่มองข้อดีข้อเสีย
ถ้าจินตนาการว่าคนที่จะมาดูแลโค้ดของผมกำลังเจอวันที่แย่มากอยู่ มันจะช่วยให้ผมเขียนให้อ่านแล้วสบายใจขึ้น
คอมเมนต์ตามจุดต่าง ๆ ตัวแปร local ที่ใส่ไว้แม้จะตัดออกได้ หรือชื่อตัวแปร ล้วนเป็นส่วนหนึ่งของเรื่องนี้
ควรเลือก framework อย่างระมัดระวัง เพราะมันเป็นคนรับใช้ที่ดี แต่เป็นเจ้านายที่แย่
สำนวนจากบทความ “อย่าเป็น frameworker จงเป็นวิศวกร” นั้นตรงมาก
ผมไม่ได้เกลียดการมีจุดยืนชัดเจน และกับ library หรือเครื่องมือ มันอาจเป็นคุณสมบัติที่ยอดเยี่ยมด้วยซ้ำ
library หรือเครื่องมือนั้นอาจมีความเชี่ยวชาญเชิงโดเมนในขอบเขตของตัวเองสูงมาก
แต่ใน framework จุดยืนที่แรงมักกลายเป็น “พอมีค้อนอยู่ในมือ ทุกอย่างก็ดูเป็นตะปู” ได้ง่าย
ไม่ได้เป็นแบบนั้นเสมอไป และก็ไม่ได้เป็นปัญหาเสมอไป แต่เมื่อเอาหลักความเชื่อดั้งเดิมของโดเมนหนึ่งไปใช้กว้าง ๆ เหตุผลที่เคยใช้ได้เพราะมันเฉพาะโดเมน อาจพังลงในบริบทที่กว้างกว่า
เพราะแบบนั้นผมจึงชอบ framework ที่มีความเป็นโมดูลาร์ สามารถเสียบ ORM ตัวอื่น หรือชั้น integration สำหรับ persistence ตัวอื่นเข้าไปได้ และเปลี่ยน router, validator หรือองค์ประกอบอื่นที่ไม่เหมาะกับปัญหาได้
มันยิ่งมีค่ามากเมื่อใน ecosystem ของ framework นั้นมีทางเลือกจากหลาย paradigm
เพราะเราจะหาเครื่องมือสำเร็จรูปที่เกือบพอดีได้ มองเห็นจุดอ่อนได้ชัด และอุดช่องโหว่ได้เมื่อจำเป็น
เพื่อความสามารถในการบำรุงรักษา การต่อสู้กับอาการ NIH syndrome สำคัญมาก
มันเป็นความเสี่ยงถาวรที่ดูดทรัพยากรได้เร็วอย่างน่าตกใจ
ในขณะเดียวกัน บางองค์ประกอบก็ให้ประโยชน์มากเมื่อเราปรับจูนเองหรือเป็นเจ้าของทั้งหมด และส่วนที่ยากคือการตัดสินว่าอะไรควรอยู่ฝั่งไหน
ผมเห็นด้วยอย่างมากกับการเอาความสามารถในการบำรุงรักษาไว้ต้นรายการ
จากมุมมองวิศวกรรมระบบ สถาปัตยกรรมซอฟต์แวร์คล้ายกับ การออกแบบระบบท่อ
มันสำคัญมาก แต่คนไม่ได้อาศัยอยู่ในท่อ คนอาศัยอยู่ในบ้านที่มีท่อ
ถ้าระบบท่อไม่คำนึงถึงส่วนอื่นของบ้าน ค่าแก้ไขอาจสูงมหาศาล
เป็นบทความที่ดี
การ “เรียนรู้สถาปัตยกรรมซอฟต์แวร์” คือการเข้าใจว่าไม่มีคำตอบที่ถูกต้องเพียงหนึ่งเดียว
มันเป็นทั้งศิลปะและวิทยาศาสตร์
ถ้าอยากหาอะไรอ่าน แนะนำ Simplify IT - The art and science towards simpler IT solution
https://nocomplexity.com/documents/reports/SimplifyIT.pdf
Gary Bernhardt talks นั้นยอดเยี่ยมจริง ๆ
มีแนวคิดมากมายอยู่ในนั้นที่จะพาไปยังที่น่าสนใจอื่น ๆ ได้อีก