คำร้องขอซอฟต์แวร์ที่เบาในปี 2024
(spectrum.ieee.org)- ในความเป็นจริงที่แม้แต่ฟังก์ชันเรียบง่ายก็ยังต้องใช้ dependency หลายพันรายการและโค้ดหลายสิบล้านบรรทัด ความ บวมพอง ของซอฟต์แวร์เองจึงกลายเป็นสาเหตุใหญ่ของช่องโหว่ด้านความปลอดภัย
- ความปลอดภัยไม่ได้ขึ้นอยู่กับความหนาแน่นของบั๊กเท่านั้น แต่ยังขึ้นอยู่กับ ปริมาณโค้ดทั้งหมด ที่ผู้โจมตีเข้าถึงได้ด้วย และ attack surface ที่กว้างเกินจำเป็นอาจนำไปสู่การเจาะระบบจริงได้
- ระบบนิเวศของ dependency อย่าง Electron JS, Node.js, Docker image, npm และ PyPI ทำให้ปริมาณและแหล่งที่มาของโค้ดที่นำไป deploy ไม่ชัดเจน และแม้แต่แอปเปิดประตูโรงรถก็อาจมีโค้ดที่ทำงานอยู่ มากกว่า 50 ล้านบรรทัด เข้ามาเกี่ยวข้อง
- Rust, sanitizer และ fuzzer ช่วยยกระดับคุณภาพโค้ดได้ แต่ ความล้มเหลวด้านการออกแบบเชิงตรรกะ เช่น การรันโค้ดในเอกสารโดยอัตโนมัติ เป็นสิ่งที่ยากจะป้องกันได้ด้วยการกำจัดบั๊กเพียงอย่างเดียว
- Trifecta ให้ฟังก์ชันแชร์รูปภาพด้วยโค้ดใหม่ 1,600 บรรทัด, dependency หลักราว 5 รายการ และขนาดรวม 3MB แสดงให้เห็นว่าสามารถสร้างซอฟต์แวร์สมัยใหม่ได้แม้มีโค้ดและ dependency จำกัด
สภาพที่ความปลอดภัยของซอฟต์แวร์ตกอยู่ในอันตราย
- สถานะความปลอดภัยของซอฟต์แวร์ในช่วงหลัง แย่มาก
- ในช่วงหนึ่งปีที่ผ่านมา เกิดเหตุละเมิดร้ายแรงกับซอฟต์แวร์มาตรฐานอุตสาหกรรม เช่น Ivanti, MOVEit, Outlook, Confluence, Barracuda Email Security Gateway, Citrix NetScaler ADC และ NetScaler Gateway
- แม้แต่บริษัทที่มีทรัพยากรมากอย่าง Apple และ Google ก็ยังทำพลาดด้านความปลอดภัยจนทำให้ลูกค้าตกอยู่ในความเสี่ยง
- เพราะเกิดการรับรู้ว่าซอฟต์แวร์อันตรายเกินไป คำแนะนำให้ไม่ต้องรันเอง แต่ฝากไว้กับ X as a service หรือคลาวด์จึงกลายเป็นเรื่องปกติ
- สมมติฐานที่ว่าคลาวด์จะทำให้ซอฟต์แวร์ที่มีช่องโหว่น่าเชื่อถือได้ก็เริ่มสั่นคลอน
- แพลตฟอร์มอีเมลของ Microsoft ถูกแฮ็ก รวมถึงอีเมลรัฐบาลที่เป็นความลับ
- ยังมีความกังวลเกี่ยวกับความปลอดภัยของ Azure cloud
- Okta ประสบเหตุละเมิดเป็นครั้งที่สองภายใน 2 ปี และหลังจากนั้นก็มีกรณีผู้ใช้ Okta ถูกแฮ็กตามมาอย่างน่าสงสัย
- EU กำลังผลักดันกฎหมาย 3 ฉบับเพื่อจัดการความปลอดภัยของซอฟต์แวร์
- NIS2: สำหรับบริการสำคัญ
- Cyber Resilience Act: สำหรับซอฟต์แวร์เชิงพาณิชย์และอุปกรณ์อิเล็กทรอนิกส์แทบทั้งหมด
- Product Liability Directive: ขยายขอบเขตความรับผิดให้ครอบคลุมซอฟต์แวร์
ช่องโหว่มาจากทั้งคุณภาพโค้ดและปริมาณโค้ด
- ความปลอดภัยของซอฟต์แวร์ขึ้นอยู่กับ 2 แกน
- ความหนาแน่นของปัญหาความปลอดภัย ในซอร์สโค้ด
- ปริมาณโค้ด ที่แฮ็กเกอร์เข้าถึงได้
- ยิ่งมีโค้ดมาก ความเสี่ยงก็ยิ่งสูงขึ้น
- แม้ความหนาแน่นของบั๊กจะต่ำ แต่ในโค้ดหลายล้านบรรทัดก็ยังสามารถพบช่องโหว่ที่นำไปใช้โจมตีได้
- กรณี iMessage แสดงให้เห็นว่าการขยาย attack surface สร้างปัญหาอย่างไร
- แม้แต่ iMessage ที่ไม่ต้องการก็ถูกประมวลผลทันทีบน iPhone เพื่อสร้าง preview
- Apple รองรับรูปแบบภาพหลากหลาย และแม้แต่ PDF ที่มีฟอนต์บีบอัดแปลก ๆ ก็ถูกนำมาประมวลผลด้วย
- รูปแบบเก่าเหล่านั้นแทบจะมีภาษาโปรแกรมอยู่ในตัว และผู้โจมตีสามารถใช้มันสำรวจจุดอ่อนอื่น ๆ ของโทรศัพท์ได้
- Apple สามารถลด attack surface ได้โดยจำกัด preview ให้เหลือรูปแบบภาพที่น้อยลงมาก หรือเป็นรูปแบบเดียวที่ “รู้ว่าดี”
- EU Cyber Resilience Act ก็ระบุว่าผู้จำหน่ายต้อง ลด attack surface ให้เหลือน้อยที่สุด
โค้ดที่ดีกว่าอย่างเดียวไม่พอ
- มีความเคลื่อนไหวเพื่อยกระดับคุณภาพโค้ดอยู่แล้ว
- ภาษาที่ปลอดภัยต่อหน่วยความจำ อย่าง Rust
- เครื่องมือเสริมความปลอดภัยอย่าง AddressSanitizer
- fuzzer ที่แปลง input อัตโนมัติเพื่อค้นหาช่องโหว่และบั๊ก
- แต่ปัญหาความปลอดภัยจำนวนมากเกิดจาก ตรรกะ ที่อยู่เบื้องล่าง มากกว่าบั๊กในตัวโค้ดเอง
- ช่องโหว่อีเมลของ Barracuda เกิดจากไลบรารีภายนอกที่สแกนสเปรดชีต Excel เพื่อตรวจไวรัส แต่กลับรันโค้ดจริง ๆ
- การตัดสินใจใส่ฟีเจอร์ที่รันโค้ดในเอกสารโดยอัตโนมัติ ไม่สามารถแก้ได้แม้จะกำจัดบั๊กทั้งหมดในโค้ดแล้วก็ตาม
อยู่ในสภาพที่ไม่รู้ว่ากำลัง deploy อะไร
- ซอฟต์แวร์สมัยใหม่ใหญ่โตจนยากจะรู้ว่าจริง ๆ แล้วกำลัง deploy อะไรอยู่
- Niklaus Wirth วิจารณ์ใน “A Plea for Lean Software” ปี 1995 ว่าซอฟต์แวร์ขยายใหญ่ถึงระดับเมกะไบต์
- ระบบปฏิบัติการ Oberon ของเขามีขนาด 200KB รวม editor และ compiler แล้ว
- ปัจจุบันบางโปรเจกต์มีแค่ไฟล์ตั้งค่าก็เกิน 200KB แล้ว
- แอปทั่วไปในปัจจุบันอาจสร้างบน Electron JS
- Electron JS รวม Chromium และ Node.js ไว้ด้วย
- Node.js ทำให้เข้าถึงแพ็กเกจ JavaScript ได้หลายหมื่นรายการ
- คาดว่าการใช้ Electron JS เพียงอย่างเดียวก็อาจนำโค้ดเข้ามาอย่างน้อย 50 ล้านบรรทัด เมื่อรวม dependency แล้ว
- แอปดึงแพ็กเกจเสริมเข้ามาหลักร้อยถึงหลักพัน และ dependency เหล่านั้นก็ดึง dependency อื่น ๆ ต่ออีก
- สิ่งที่ถูกรวมอยู่ใน build อย่างแน่ชัดอาจเปลี่ยนได้ทุกวัน
- บางแพ็กเกจอาจเปิดเผยผู้ใช้ต่อผู้ลงโฆษณาหรือ data broker โดยค่าเริ่มต้น
- แอปที่ควบคุมอุปกรณ์ในบ้านอาจเชื่อมต่อกับซอฟต์แวร์สแต็กฝั่ง Amazon ด้วย และสแต็กนั้นก็อาจใช้ Node.js กับ dependency หลายรายการเช่นกัน
- ผลลัพธ์คือแม้แต่การเปิดประตูโรงรถ ก็อาจมี โค้ดที่ทำงานอยู่มากกว่า 50 ล้านบรรทัด บน image ของระบบปฏิบัติการหลายตัวและเซิร์ฟเวอร์หลายเครื่อง
ภาระจากคอนเทนเนอร์และ supply chain ของ dependency
- ในอดีต เรา deploy ผลลัพธ์จาก compiler หรือชุดไฟล์ที่จะถูก interpret และต้องคิดระหว่างการติดตั้งกับตั้งค่าว่าในแพ็กเกจมีอะไรอยู่บ้าง
- ทุกวันนี้มัก deploy ผ่านคอนเทนเนอร์ โดยรวมไม่เพียงซอฟต์แวร์ แต่ยังรวมไฟล์ระบบปฏิบัติการเพื่อให้สภาพแวดล้อมรันตรงกันด้วย
- มักกลายเป็นสถานการณ์ที่เหมือน deploy image ของดิสก์คอมพิวเตอร์เต็ม ๆ
- บน Docker Hub มี image จำนวนมากที่เกิน 350MB
- คอนเทนเนอร์ใช้เพื่อวัตถุประสงค์ที่ดีได้ แต่รูปแบบการใช้งานจริงอาจเพิ่มปริมาณโค้ดที่ถูก deploy อย่างมาก
- ยังไม่แน่ชัดว่า security update ของ dependency ถูกส่งต่อไปถึงแอปปลายทางหรือไม่
- ยากจะรู้ว่าบั๊กประมวลผลภาพที่ Google และ Apple รีบปล่อยอัปเดตนั้นยังคงอยู่ในแอป Electron หรือไม่
- ระบบนิเวศ npm มีประวัติเรื่อง repository ของแพ็กเกจถูกยึด, การไฮแจ็ก และการฟื้นคืนชีพแพ็กเกจชื่อเดิม
- PyPI ก็ประสบปัญหาคล้ายกัน
- dependency จำเป็นต้องได้รับการตรวจทาน แต่ยากจะคาดหวังให้ตรวจหลายพันรายการอยู่บ่อย ๆ
- การเขียนทุกอย่างใหม่เองก็ไม่ใช่คำตอบ และยังมีโมดูลดี ๆ อย่าง SQLite ที่มีแนวโน้มปลอดภัยกว่าสิ่งที่เขียนเอง
Trifecta: เครื่องมือแชร์รูปภาพที่สร้างด้วยโค้ดขนาดเล็ก
- Trifecta เป็น ซอฟต์แวร์แชร์รูปภาพแบบ standalone ที่เรียบง่ายแต่ใช้งานได้จริง
- สามารถลากและวางรูปภาพในเบราว์เซอร์เพื่อแชร์ได้ง่าย ๆ
- หากใช้ imgur จะมีคุกกี้และ tracker จำนวนมากถูกติดตั้งในเบราว์เซอร์ และผู้ที่ดูรูปที่แชร์ก็อาจถูกบังคับให้เจอกับ tracker ด้วย
- เครื่องมือแชร์รูปภาพแบบ self-hosted จำนวนมากก็สร้างบน framework ขนาดใหญ่ จึงถูกมองว่าไม่น่าไว้วางใจ
- Trifecta ถูกทำให้เล็กเพื่อให้ตรวจทานโค้ดทั้งหมดได้ภายในไม่กี่ชั่วโมง
- ซอร์สโค้ดใหม่ 1,600 บรรทัด
- dependency สำคัญราว 5 รายการ
- ขนาดโค้ดทั้งหมด 3MB
- โซลูชันแชร์รูปภาพอื่นที่ยกมาเปรียบเทียบถูก deploy เป็น Docker image ขนาด 288MB
- โซลูชันแชร์รูปภาพที่ใช้ Node อีกตัวหนึ่งพบว่ามี dependency 1,600 รายการ และ JavaScript มากกว่า 4 ล้านบรรทัด
- Trifecta ไม่ใช่ไซต์สาธารณะที่ให้ใครก็ได้อัปโหลดรูปภาพ แต่เหมาะกับการใช้งานในบริษัทหรือส่วนบุคคล
ปัญหาการเข้าใจผิดว่าความซับซ้อนคือพลัง
- หนึ่งในปฏิกิริยาที่พบบ่อยต่อ Trifecta คือให้ deploy โดยใช้ชุด Amazon Web Services
- ซึ่งไม่สอดคล้องกับเป้าหมายของซอฟต์แวร์แบบ standalone ที่ไม่พึ่งพาบริการภายนอก
- มีปฏิกิริยาว่าพูดถึง Docker อย่างไม่เป็นธรรมด้วย แต่ก็ยอมรับว่าคอนเทนเนอร์ใช้เพื่อจุดประสงค์ที่ดีได้
- Niklaus Wirth ชี้ในบทความปี 1995 ว่าผู้คนมีแนวโน้ม เข้าใจผิดว่าความซับซ้อนคือความประณีต
- ดังคำกล่าวของ Tony Hoare การออกแบบซอฟต์แวร์มี 2 แนวทาง
- ทำโปรแกรมให้เรียบง่ายจนเห็นได้ชัดว่าไม่มีข้อผิดพลาด
- ทำให้ซับซ้อนจนดูเหมือนไม่มีข้อผิดพลาดที่เห็นได้ชัด
- Wirth มองว่าแรงกดดันด้านเวลาเป็นสาเหตุหลักของซอฟต์แวร์ที่บวมพอง
- แรงกดดันด้านเวลาขัดขวางการวางแผนอย่างรอบคอบ
- ทำให้รีบเพิ่มและแก้ไข แทนที่จะปรับปรุงวิธีแก้ที่พอยอมรับได้
- ค่อย ๆ บ่อนทำลายมาตรฐานคุณภาพและความสมบูรณ์ของวิศวกร
- การระเบิดของปริมาณซอฟต์แวร์ไม่ใช่กฎธรรมชาติ แต่เป็นสิ่งที่วิศวกรซอฟต์แวร์ควรลดลง
การลดปริมาณโค้ดกลายเป็นมาตรการความปลอดภัย
- โลกปัจจุบันกำลัง deploy โค้ดมากเกินไป
- ส่วนใหญ่เป็นโค้ดจากภายนอก
- บางส่วนถูกรวมเข้ามาโดยไม่ตั้งใจ
- ส่วนใหญ่ไม่ได้รับการตรวจสอบอย่างเพียงพอ
- ผลคือเกิด attack surface มหาศาล และมีโค้ดระดับธรรมดาจำนวนมากถูกเปิดเผยอยู่ภายในนั้น
- ความพยายามยกระดับคุณภาพโค้ดยังคงดำเนินต่อไป แต่การโจมตีจำนวนมากมาจากความล้มเหลวด้านตรรกะ และความคืบหน้าในการตรวจจับเรื่องนี้ยังค่อนข้างน้อย
- แค่ลดปริมาณโค้ดที่เปิดเผยต่อโลกก็สามารถสร้างการปรับปรุงได้มากแล้ว
- เวลาในการออกผลิตภัณฑ์อาจยาวขึ้น แต่กฎหมายที่กำลังจะมาถึงอาจทำให้ผู้จำหน่ายจริงจังกับความปลอดภัยมากขึ้น
- Trifecta และ Oberon แสดงให้เห็นว่าแม้มีโค้ดและ dependency จำกัด ก็ยังมอบฟังก์ชันได้มาก
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ใน A Deepness in the Sky ของ Vernor Vinge มนุษยชาติแพร่กระจายไปตามดวงดาวได้ด้วยเพียง เทคโนโลยีความเร็วต่ำกว่าแสง และยานอวกาศระหว่างดาวก็ปรากฏในสภาพที่เทคโนโลยีเก่าแก่จากหลายระบบดาวและหลายอารยธรรมปะปนกันอยู่
ระบบคอมพิวเตอร์เองก็วิวัฒนาการมายาวนานมากจนแทบไม่มีใครเข้าใจโค้ดส่วนใหญ่แล้ว แค่ใช้งานมันและสร้างสิ่งใหม่ทับขึ้นไปอีกชั้น
โดยเฉพาะมีตัวละครคนหนึ่งที่เป็นวิศวกรระบบยุคเก่า ซึ่งผ่านการสลับระหว่างภาวะหยุดนิ่งกับการเดินทางมาเป็นเวลานาน จนจัดว่าเป็นหนึ่งในมนุษย์ที่ยังมีชีวิตอยู่ที่เก่าแก่ที่สุด และในอนาคตที่ทุกคนสร้างหลายชั้นทับบนระบบเหล่านั้น ความรู้ของเขาเกี่ยวกับวิธีทำงานและช่องโหว่ในยุคของตัวเองกลับกลายเป็นข้อได้เปรียบอย่างมาก
ผมคิดว่า Vinge ชี้ประเด็นได้แม่นมาก
แบบแรกเป็นปริศนาจริง ๆ ที่วิทยาศาสตร์สมัยใหม่และคนฉลาดมาก ๆ ต้องแก้ แต่แบบหลังใกล้เคียงกับการขาดความสนใจมากกว่า
ถ้าจ่ายเงินมากพอ วิศวกรที่เก่งก็จะแยกชิ้นส่วนเครื่องซักผ้าแล้วหาข้อบกพร่องที่แน่ชัดได้ แต่ไม่มีใครยอมจ่ายค่าใช้จ่ายนั้น และจะทิ้งแล้วซื้อใหม่แทน
ความรู้เกี่ยวกับซอฟต์แวร์ในอดีตจัดอยู่ในแบบหลังอย่างชัดเจน ไม่ว่าส่วนไหน หากขุดลงไปก็จะเข้าใจได้อย่างสมบูรณ์ในที่สุด แต่ส่วนใหญ่แล้วการเพิกเฉยหรือวางอีกชั้นทับลงไปนั้นถูกกว่าและใช้งานได้จริงกว่ามาก
เป็นเรื่องที่มนุษย์ในอนาคตลืมเลขคณิตพื้นฐานไปแล้ว และเมื่อมีคนค้นพบมันใหม่ ผู้มีอำนาจก็พยายามนำไปใช้ในสงคราม ผมเข้าใจสารที่ต้องการสื่อ แต่คิดว่าฉากหลังมันไม่สมจริงจนน่าขำเกินไปจนทำให้พลังของเรื่องหายไป
หากต้องการอภิปรายเพิ่มเติม ดูที่ http://lambda-the-ultimate.org/node/4424
เรากำลังเจอปัญหาแบบนี้อยู่แล้ว ลุงของผมที่อายุ 60 กว่า ๆ กำลังบำรุงรักษาซอฟต์แวร์ขนส่งด้วยรถบรรทุกเก่าที่เขียนด้วย COBOL และยังมีงานเทคโนโลยีโบราณแบบนี้อยู่ ถ้าสนใจก็แนะนำให้ได้
ปัญหาพื้นฐานเหมือนกับเหตุการณ์ left-pad เราหัวเราะเยาะวิศวกรจูเนียร์ที่ไม่มีคนกำกับดูแลซึ่งติดตั้ง dependency แบบลวก ๆ แต่เมื่อผ่านไปหลายสิบปีและนักพัฒนาหลายรุ่น สุดท้ายซอฟต์แวร์ส่วนใหญ่ก็ต้องพึ่งพา dependency ที่ไม่อาจรู้ที่มาได้ในระดับหนึ่ง
ลองสมมติว่าต้องปล่อยอัปเดตในปี 2100 ก็คงต้องดันมันผ่านระบบจัดการ dependency ของ npm ในยุคนั้น ในขณะเดียวกันอาจมีอุปกรณ์ที่ต้องพึ่งพากันและต้องอัปเดตความปลอดภัยกระจายอยู่ทั่วระดับระบบสุริยะ มีอุปกรณ์เป็นล้านล้านชิ้น และแคชตัวกลางที่ไม่รู้ว่าอัปเดตล่าสุดหรือไม่ ผมนึกภาพไม่ออกเลยว่า dependency tree แบบนั้นจะหน้าตาเป็นอย่างไร
การทำงานในสภาพแวดล้อมที่ต้องขุดโค้ดของเฟรมเวิร์กลงไปเพื่อไปให้ถึงแก่นก็ชวนหงุดหงิดเหมือนกัน รู้สึกเหมือนเสียเวลาเปล่า
ความบวมโตพบได้ในไลบรารีส่วนใหญ่ของ npm ผู้เขียนไม่รู้จักการออกแบบที่ดี และพยายามทำให้ทุกไลบรารีทำได้ทุกอย่าง
อย่างเช่นบอกว่าเป็นไลบรารีแปลงการเข้ารหัสสตริง แต่กลับใส่ทั้งการโหลดไฟล์ บันทึกไฟล์ ดาวน์โหลดจากอินเทอร์เน็ต และเครื่องมือบรรทัดคำสั่งไว้ในรีโพซิทอรีเดียวกัน ไลบรารีควรทำงานของตัวเองเพียงอย่างเดียว แล้วปล่อยส่วนที่เหลือให้ผู้ใช้จัดการ
ฝั่ง Rust ก็ดูไม่ได้ดีกว่าเท่าไร ถ้าลองแก้เอกสารของ Rust จะเห็นว่ามี crate ถูกติดตั้งเข้ามาราว 1,000 ตัว
ปัญหาไม่ได้อยู่ที่ภาษา แต่อยู่ที่ใครก็อัปโหลดไลบรารีได้ และในความเป็นจริงก็มีใครก็ไม่รู้มาอัปโหลด คนที่ “แค่อยากให้งานเสร็จ” จะเลือกไลบรารีที่มีฟีเจอร์มากที่สุด และเรียกร้องฟีเจอร์เพิ่ม เพราะไม่อยากเขียนโค้ด 3 บรรทัดที่ควรอยู่นอกไลบรารี เช่น “ช่วยเพิ่มการเรนเดอร์ PDF ให้ด้วยได้ไหม?”
ยังไม่แน่ใจว่าทางแก้คืออะไร แต่เคยคิดถึงการตั้งกลุ่มสนับสนุน Low Dependency พร้อมแบดจ์ เพื่อให้ผู้เขียนไลบรารีอยากได้แบดจ์นั้น และให้ผู้ใช้มองหาเวลาจะเลือกไลบรารี
ในทางกลับกัน ถ้าต้องการไลบรารีที่มี dependency น้อย ก็จะได้ของไม่กี่ตัวที่ทำงานได้หลายอย่าง
ในมุมมองของผม ไลบรารีที่หนากว่านิดหน่อยแต่ dependency น้อย และมาจากผู้เขียนที่ไว้ใจได้ ดีกว่า Lodash ใหญ่ก็จริง แต่เวอร์ชัน ES6 module รองรับ tree shaking และโดยเนื้อแท้แล้วทำหน้าที่เหมือน standard library ที่ JavaScript เคยขาดไป ส่วน date-fns ก็คล้ายกันสำหรับ Date ผมใส่สองตัวนี้เป็นค่าเริ่มต้นในแทบทุกโปรเจกต์เพื่ออุดช่องว่างของไลบรารีแกนหลักของ JavaScript
เมื่อก่อนเคยรับงานสัญญา Ruby on Rails อยู่ช่วงหนึ่ง ปัญหาประสิทธิภาพหนักมากจนต้องพัฒนาในโหมด release เซิร์ฟเวอร์ถึงขั้นตรวจจับการเปลี่ยนไฟล์แล้ว reload อัตโนมัติไม่ได้
วันหนึ่งทนไม่ไหวเลยเริ่มขุดดู มี gem ถูกดึงเข้ามามากจนจำไม่ได้ว่ากี่ตัว หนึ่งในนั้นเป็น gem ที่มีไว้เพื่อประหยัด โค้ด 3 บรรทัด จริง ๆ
หลังจากนั้นก็อยู่ห่างจากชุมชน RoR มาตลอด เพิ่งกลับมารับงานสัญญา RoR อีกครั้งในรอบหลายปี ถึงจะไม่แย่เท่าเดิม แต่ก็ยังไม่ดีอยู่ดี
บางชุมชนไม่ให้ความเคารพต่อความเสี่ยงที่ dependency นำมาเลย
ในทางกลับกัน การที่ Git repository ไหน ๆ ก็โฮสต์ไลบรารี Go ได้ และใครก็ใช้จาก URL นั้นได้ ถือว่าสะดวกมากจริง ๆ
อย่างแรก ผู้ทำแพ็กเกจอยากสร้างสิ่งนี้ให้เป็นเส้นทางอาชีพ และวิธีเรียกความสนใจก็มีแต่การทำแพ็กเกจจำนวนมาก พวกเขาจึงทำแพ็กเกจที่พึ่งพาแพ็กเกจอื่น ๆ ของตัวเองต่อไปเรื่อย ๆ และพยายามยัดแพ็กเกจที่มีประโยชน์สักหนึ่งสองตัวของตัวเองเข้าไปในโค้ดของคนอื่น
อย่างที่สอง คือคนที่เชื่อว่าการแก้ปัญหาต้องมีแพ็กเกจใหม่เสมอ พวกเขาไม่สนใจว่ามี dependency มากแค่ไหน หรือปัญหาจริง ๆ ยากหรือไม่ สุดท้ายแทนที่จะเรียนรู้วิธีแก้ปัญหา ก็ไปเรียน API ของ wrapper ที่มีดาว GitHub 4 ดวง
ตามอุดมคติ เครื่องมือแบบนี้ควรใช้เวลาน่าเบื่อไปกับการตัดแพ็กเกจที่ไม่จำเป็นออก ลดความรับผิดชอบของแต่ละแพ็กเกจให้เหลือน้อยที่สุด จัดสภาพแวดล้อมให้สมเหตุสมผล แล้วแคชอย่างเป็นอิสระจนไม่ต้องพึ่ง LLM ควรเรียกตัวตรวจอัปเดตหรือ curator เฉพาะเวลามีปัญหาเท่านั้น
พูดตรง ๆ นี่เป็นหนึ่งในปัญหาแย่ที่สุดของซอฟต์แวร์สมัยใหม่ และผมคิดว่ามันทำให้โปรเจกต์เกิน 50% ใช้งานไม่ได้ เป็นปัญหาที่น่าเบื่อแต่แก้ได้ จึงเหมาะมากกับ LLM agent ที่ทำได้จริง และถ้ามีก็คงช่วยได้มาก
“คุณเคยเห็นเครื่องบินสมัยใหม่ไหม? เคยติดตามไหมว่าเส้นสายของมันวิวัฒนาการไปอย่างไรในแต่ละปี? เคยคิดไหมว่า ไม่ใช่แค่เครื่องบิน แต่ทุกสิ่งที่มนุษย์สร้างขึ้น ความพยายามทางอุตสาหกรรม การคำนวณ และค่ำคืนที่ใช้ไปกับแบบร่างของมนุษย์ สุดท้ายล้วนมุ่งไปสู่หลักการเดียวที่ครอบงำทุกอย่าง นั่นคือ ความเรียบง่ายขั้นสูงสุด?
ราวกับมีกฎธรรมชาติที่สั่งว่า หากจะขัดเกลาเส้นโค้งของเฟอร์นิเจอร์ กระดูกงูเรือ หรือลำตัวเครื่องบิน ให้เข้าใกล้ความบริสุทธิ์ดั้งเดิมอย่างเส้นโค้งของอกหรือไหล่มนุษย์ ก็ต้องอาศัยการทดลองของช่างฝีมือหลายชั่วคน ความสมบูรณ์แบบดูเหมือนจะมาถึง ไม่ใช่เมื่อไม่มีอะไรให้เพิ่มอีกต่อไป แต่เมื่อไม่มีอะไรให้ตัดออกได้อีกแล้ว”
— Antoine de Saint Exupéry, Terre des Hommes
พอเขียนออกมาว่า “การเปิดประตูโรงรถอาจต้องใช้โค้ดที่ทำงานอยู่มากกว่า 50 ล้านบรรทัด และอิมเมจระบบปฏิบัติการบนเซิร์ฟเวอร์หลายเครื่อง” ก็รู้สึกว่ามันบ้าจริง ๆ
แค่คิดว่ามีโค้ดมากแค่ไหนที่กำลังทำงานอยู่บนเครื่องที่ผมใช้พิมพ์ข้อความนี้ ก็เวียนหัวแล้ว เป็นโค้ดที่ผมไม่เคยตรวจ และคงแทบไม่เคยผ่านการตรวจสอบอย่างเข้มงวดด้วย
เอาละ กลับไปติดตั้ง dependency ของ npm ต่อก่อน
อุปมาที่ว่า “ซอฟต์แวร์ตอนนี้ถูกมองว่าอันตรายจนถึงขั้นมีคนบอกว่าอย่ารันเอง ให้ฝากไว้กับผู้ให้บริการ ‘X as a service’ หรือไม่ก็ ‘คลาวด์’ แทน ลองเทียบกับสถานการณ์สมมติที่รถยนต์ไฟไหม้บ่อยเกินไปจนมีคำแนะนำว่าอย่าขับเอง แต่ให้ผู้เชี่ยวชาญที่มีนักดับเพลิงมืออาชีพคอยตามประกบตลอดเป็นคนขับให้” นี่น่าเอาไปใช้ต่อจริง ๆ
แฟนเก่าของผมไม่ไว้ใจ “คลาวด์” ด้วยเหตุผลที่สมเหตุสมผลจากการเติบโตในอดีตกลุ่มตะวันออก แต่ทางเลือกคือได้แต่หวังว่าจะไม่ทำโน้ตบุ๊ก HP ที่ซื้อมาในราคาต่ำสุดหาย หลังจากให้ความรู้เล็กน้อย อย่างน้อยในเรื่องนั้นเธอก็สบายใจขึ้น
ปัญหาคือการขาดความรู้โดยรวมและการไม่คำนึงถึงผลลัพธ์ที่ตามมา สุดท้ายก็ต้องยอมรับความเสี่ยง เรียนรู้เอง หรือพึ่งบริษัท SaaS กับคลาวด์ ผมเห็นน้ำตามามาก และไม่ค่อยเห็นกรณีที่เรียนรู้ด้วยตัวเองนัก
มันเป็นเรื่องความรับผิดชอบส่วนบุคคล แต่เมื่อไม่มีใครอยากรับผิดชอบ การมอบให้ผู้เชี่ยวชาญอาจเป็นทางออกที่แย่น้อยกว่าการเชื่อตัวเอง คำตอบที่ถูกคือการศึกษา แต่มันยากอย่างสิ้นหวัง
ซอฟต์แวร์ผอมเพรียวลงกว่านี้ไม่ได้หรอก หากจะทำได้ต้องใช้เวลา ฝีมือ และคนค่าตัวแพง ไม่ใช่แค่คนที่เอาตัวอย่างจากเทคสแต็ก 12 แบบมาต่อกันเป็น ชุดแฟรงเกนสไตน์
ผมเป็นนักพัฒนาอิสระ และคนที่เพิ่งเรียน node.js เมื่อปีที่แล้วแล้วเอา node.js, คอนเทนเนอร์, บริการฐานข้อมูลโฮสต์บน AWS อะไรก็ได้, Lambda, object storage, Cloudflare, YAML, React, Vite และ dependency อื่น ๆ มาประกอบกันในวันเดียวจนได้เว็บแอปที่สำเร็จรูปแต่ยังเปราะบาง มักเสนอราคาต่ำกว่าผมเสมอ
ซอฟต์แวร์ที่ผอมเพรียว เร็ว ต้นทุนรันต่ำ และค่าบำรุงรักษาถูกกว่า แม้ระยะยาวจะถูกกว่า แต่เขียนให้มีกำไรได้ยาก
สมัยก่อนเคยมีความฝันว่าทุกคนจะใช้ hook และ routine มาตรฐานที่ระบบจัดไว้ให้สำหรับอินเทอร์เฟซและอื่น ๆ ให้นึกถึงอย่าง Macintosh Toolbox หรือ QuickDraw
ตอนนั้นว่ากันว่างานหลักของนักพัฒนาคือเขียนลอจิกของโปรแกรม และการเปลี่ยนแปลงหรือการเพิ่มควรโปร่งใส system call ควรทำงานเดิมได้อย่างราบรื่นแม้โค้ดภายในจะเปลี่ยนไป และฟีเจอร์ใหม่ควรเป็น superset ของฟีเจอร์เดิม ทำให้โค้ดเก่ายังคอมไพล์หรือรันได้ไม่มีปัญหา ขณะที่ซอฟต์แวร์ใหม่ได้ความสามารถมากขึ้น
เชื่อกันว่าวิธีนี้จะทำให้บำรุงรักษาง่าย อินเทอร์เฟซเป็นมาตรฐาน และโค้ดผอมเพรียวลงเพราะพึ่งพา system call มาก บรรยากาศตอนนั้นคือควรหลีกเลี่ยงไลบรารีภายนอก
ความฝันนี้พังลงอย่างรวดเร็ว ให้นึกถึง DLL ปัจจุบัน package management และ packaging จำนวนมากดูเหมือนจะเป็นงานที่ทำให้แน่ใจว่ามีไลบรารีที่ถูกต้องอยู่
ตอนนั้นการพัฒนาซอฟต์แวร์ขนาดใหญ่ยังแทบอยู่ในวัยทารก จึงเข้าใจได้ว่ามันไม่เป็นไปตามคาด ตอนนี้เรามีประสบการณ์ร่วมกับปัญหาเหล่านี้สะสมมามากแล้ว เลยสงสัยว่าบทสรุปคือความฝันนี้เป็นไปไม่ได้อย่างมีสติ หรือว่าการผ่านสภาพยุ่งเหยิงในปัจจุบันมามากพอจะพาเราไปสู่ความพยายามแบบสมัยใหม่อีกครั้ง
ถ้าต้องการซอฟต์แวร์ที่เร็ว ผอมเพรียว เสถียร และปลอดภัย ต่อให้จะได้ครบทั้งสี่อย่างพร้อมกันได้ยาก ผมก็ไม่แน่ใจว่าสถานการณ์ปัจจุบันกำลังมุ่งไปทางนั้นหรือไม่
Lisp ยุคแรกใส่สิ่งต่าง ๆ ไว้ในภาษาน้อยมาก แต่ Raku ดันแม้แต่เรื่องเล็ก ๆ ที่น่าจะไปอยู่เป็นไลบรารี npm เข้าไปในสเปกของภาษา
C ปล่อยให้คุณตัดสินใจเองว่าจะ build โค้ดอย่างไร แต่ภาษาแบบคอมไพล์ใหม่ ๆ ส่วนใหญ่ให้ build tool มาพร้อมกันในรูปแบบใดรูปแบบหนึ่ง
ในภูมิทัศน์นี้มีบางอย่างที่ได้ผลค่อนข้างดี แต่เกิดขึ้นนอกบริบท “นาย เครื่องจักร โปรเจกต์ใหม่” ที่ขับเคลื่อนงานของ Wirth ปัญหาคือมันมักมาพร้อมกับธรณีประตูของ dependency ขนาดมหึมา อย่างฐานข้อมูลหรือเอนจินเบราว์เซอร์ และถ้าไม่ชอบวิธีที่ dependency นั้นถูกสร้างขึ้นมา สุดท้ายก็จะไม่มีความสุข
สิ่งที่พูดเรื่อง Rust อยู่เรื่อย ๆ ก็คือนี่แหละ
ถ้า 70% ของช่องโหว่ C++ เก่า ๆ เกี่ยวกับหน่วยความจำจริง ๆ ช่องโหว่ต่อบรรทัดโค้ดก็อาจน้อยกว่า C++ ได้ 70%
แต่ถ้าใน Rust ดึงแพ็กเกจเข้ามาหลายร้อยตัวและจำนวนบรรทัดโค้ดเพิ่มเป็น 10 เท่า เรื่องก็เปลี่ยนไป
30% ของ 100,000 บรรทัด มีปริมาณรวมมากกว่า 100% ของ 10,000 บรรทัด
แม้แต่อย่าง QT ถ้าเขียนด้วย Rust ก็คงกลายเป็น crate หลายร้อยตัวในตัวเอง แต่ปริมาณโค้ดและระดับความเสี่ยงที่ยอมรับจะเหมือนเดิมทุกประการ
แต่ปัญหาใหญ่คือช่องโหว่ แก้บั๊กใน shared library ตัวเดียวเพื่อแก้ไลบรารีหลายร้อยตัวดีกว่าไหม หรือแก้ไลบรารีหลายร้อยตัวทีละตัวดีกว่า?
ข้อเท็จจริงที่ว่า Rust ทำให้ดึง dependency เล็ก ๆ จำนวนมากเข้ามาได้ง่าย แทนที่จะเป็น dependency ขนาดยักษ์ไม่กี่ตัวนั้นไม่เกี่ยวกัน ไม่ได้แปลว่าใช้โค้ดมากกว่า
ตัวอย่างเช่น นับ crate
regexของ Rust เป็น dependency ไหม? ใน C++ สิ่งนั้นอยู่ใน standard libraryนับ Boost เป็น dependency เดียวใน C++ ไหม? ถ้าเป็น Rust มันคงเทียบได้กับ crate แยกกันสัก 30 ตัว
ในโปรแกรม Rust มี remote code execution มากแค่ไหนเมื่อเทียบกับ C++? ความถี่ของ remote code execution ใน C++ น่าจะมากกว่า Rust เกิน 70% ไปไกล
ช่วงนี้แอปมักถูกบอกว่าสร้างด้วย Electron JS แต่ดูเหมือนหลายคนยังไม่ค่อยรู้ว่าอาจใช้ native web control ของแต่ละแพลตฟอร์มโดยไม่ต้องพ่วง Electron ก็ได้
ถ้าทำแบบนั้น แอปที่แจกจ่ายอาจมีขนาดแค่ระดับกิโลไบต์ แนวทางนี้ให้เสรีภาพว่าจะใช้ภาษาแบ็กเอนด์หรือ tech stack ใดก็ได้ ตราบใดที่สื่อสารกับเว็บวิวได้
ทุกวันนี้แอปพลิเคชันสัดส่วนค่อนข้างมากน่าจะทำเป็น PWA ได้ไม่ใช่หรือ?
ไม่แน่ใจนัก แต่ Discord ก็น่าจะเป็น PWA แทนแอป Electron ได้หรือเปล่า?
ช่องว่างที่ใหญ่ที่สุดน่าจะเป็นความต่างระหว่างบางสิ่งที่ทรงพลังเทียบเท่า SQLite กับ IndexedDB แต่ถึงอย่างนั้น แอปส่วนใหญ่ก็ดูไม่น่าจำเป็นต้องมีภาษา query ระดับสูงกว่าโมเดล B-tree ของ IndexedDB เสมอไป
ขอส่งเสียงเชียร์ปรัชญา suckless อีกครั้ง ไชโย
[0 ]https://suckless.org/