12 คะแนน โดย GN⁺ 2025-07-19 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • Dependency อาจดูเหมือนฟีเจอร์ฟรี แต่ในความเป็นจริงมาพร้อมกับต้นทุนและความซับซ้อนหลากหลายรูปแบบ
  • Dependency ที่ไม่เหมาะสมก่อให้เกิดความเสี่ยงหลายด้าน เช่น ช่วงเรียนรู้, การเปลี่ยนแปลงอินเทอร์เฟซอย่างฉับพลัน, ปัญหาการดีพลอยและการติดตั้ง
  • ตัวอย่างเด่นคือ TigerBeetle ที่มุ่งใช้นโยบาย "zero dependency" เพื่อความปลอดภัย ประสิทธิภาพ และความเรียบง่ายในการปฏิบัติการ
  • ผู้เขียนเสนอ framework สำหรับประเมิน dependency (ความแพร่หลาย, ความเสถียร, ความลึก, การใช้งานง่าย, ความครบถ้วน)
  • การคิดเชิงวิพากษ์เพื่อแยกแยะ dependency ที่ดีกับที่ไม่ดี และการเลือกโดยคำนึงถึงไม่ใช่แค่ผลิตภาพระยะสั้น แต่รวมถึง ต้นทุนและความเสี่ยงโดยรวม เป็นสิ่งจำเป็น

การพึ่งพาสิ่งที่ไม่เหมาะสมมีต้นทุนสูงกว่า NIH (Not Invented Here)

ข้อเสียและต้นทุนแฝงของ dependency

  • นักพัฒนาจำนวนมากมองการเพิ่ม dependency เป็นเหมือน "ฟีเจอร์ฟรี" แต่จริง ๆ แล้วมีต้นทุนดังนี้
    • เวลาและความซับซ้อนที่ต้องใช้ในการเรียนรู้
    • บ่อยครั้งที่แม้แต่การติดตั้ง dependency เองก็ทำได้ยาก
    • เมื่ออัปเกรดเวอร์ชันหลัก ก็มีภาระที่ต้อง แก้โค้ดของตัวเองครั้งใหญ่ ตามไปด้วย
    • Dependency สุดท้ายแล้ว ต้องถูกรวมเข้าไปในสภาพแวดล้อมการดีพลอย/ติดตั้งเสมอ (เช่น container, bundling ซึ่งเพิ่มความซับซ้อน)
  • การนำ dependency ที่ไม่เหมาะสมเข้ามาอาจทำให้เกิด โครงสร้างการดีพลอยที่ซับซ้อนซึ่งไม่เกี่ยวกับฟังก์ชันหลัก ได้

หลักการ zero dependency ของ TigerBeetle

  • TigerBeetle เป็นฐานข้อมูลการเงินที่สร้างบน Vanilla Zig และใช้นโยบาย zero dependency
    • พัฒนาโดยใช้ภาษา Zig เพียงอย่างเดียว และไม่มี dependency ภายนอกนอกจาก Zig toolchain
    • มีเป้าหมายเพื่อหลีกเลี่ยงปัญหาจาก dependency เช่น ความเสี่ยงจาก supply chain attack, ประสิทธิภาพลดลง, เวลาในการติดตั้งที่เพิ่มขึ้น
    • ยิ่งซอฟต์แวร์ฝังรากลึกอยู่ในโครงสร้างพื้นฐานมากเท่าไร ต้นทุนจาก dependency ก็ยิ่งขยายตัวไปทั้งสแตก
    • การใช้ toolbox ขนาดเล็กที่เป็นมาตรฐาน เอื้อต่อการบำรุงรักษาและประสิทธิภาพในการพัฒนา
  • โดยมุ่งเน้นใช้ Zig เพียงตัวเดียวเพื่อรับมือกับปัญหาใหม่ได้อย่างรวดเร็วและลดความซับซ้อน

Framework สำหรับประเมิน dependency

  • แม้จะยอมรับว่าการไม่มี dependency เลยเป็นไปไม่ได้สำหรับนักพัฒนาทุกคน แต่ก็เน้นว่า dependency ต้องได้รับการประเมินอย่างรอบคอบ
  • ผู้เขียนเสนอให้ประเมิน dependency ตาม 5 เกณฑ์ ต่อไปนี้
    • ความแพร่หลาย (Ubiquity): มีอยู่ทั่วไปในสภาพแวดล้อมเป้าหมายมากแค่ไหน? ต้องดีพลอย/ติดตั้งแยกหรือไม่
    • ความเสถียร (Stability): ความเข้ากันได้ย้อนหลัง, ความถี่ในการเปลี่ยน API, การยุติการสนับสนุน และปัญหาลักษณะนี้เกิดบ่อยเพียงใด
    • ความลึก (Depth): ปริมาณความสามารถจริงที่ซ่อนอยู่ใต้ API และถ้าต้องทำเองจะยากแค่ไหน
    • การใช้งานง่าย (Ergonomics): API ใช้งานได้ตรงไปตรงมา/เชิงประกาศและเขียนใช้ง่ายหรือไม่ รวมถึงคุณภาพของเอกสาร
    • ความครบถ้วน (Watertightness): abstraction ทำงานได้ดีเพียงใด และต้องใส่ใจกับเทคโนโลยีชั้นล่างมากน้อยแค่ไหน
  • โดยทั่วไปชุมชนนักพัฒนามักพูดถึงแค่การใช้งานง่าย ขณะที่อีกสี่ด้านที่เหลือมักถูกมองข้าม

ตัวอย่าง dependency ที่ดี

  • POSIX system calls

    • ความเป็นสากล: ใช้งานได้บนเกือบทุกแพลตฟอร์ม เช่น Linux, Android, macOS, BSD
    • ความเสถียร: ความเข้ากันได้ของอินเทอร์เฟซสูงมากและแทบไม่มีการเปลี่ยนแปลง
    • ความลึก: API เดียวซ่อนโค้ดเคอร์เนลระดับหลายแสนบรรทัดไว้
    • การใช้งานง่าย: แม้จะเป็นสไตล์ C แบบดั้งเดิมอยู่บ้าง แต่ก็ไม่ได้ใช้งานลำบากมาก
    • ความครบถ้วน: โดยมากไม่มีปัญหาใหญ่ แต่ยังมีประเด็นย่อย เช่น การทำให้ข้อมูลคงอยู่ถาวรบนอุปกรณ์จัดเก็บ
  • โค้ดควบคุมเทอร์มินัล ECMA-48

    • ความเป็นสากล: รองรับในเทอร์มินัลส่วนใหญ่ ยกเว้น cmd.exe ของ Windows
    • ความเสถียร: ไม่มีการเปลี่ยนแปลงตั้งแต่ปี 1991
    • ความลึก: การสร้างมาตรฐานนี้ขึ้นมาเองเป็นเรื่องยากอย่างไม่น่าเชื่อ
    • การใช้งานง่าย: นอกจากความอ่านยากที่เกิดจากอักขระ Esc แล้ว โดยรวมถือว่าใช้ได้
    • ความครบถ้วน: แทบไม่ต้องกังวลเรื่องการพึ่งพาฮาร์ดแวร์
  • แพลตฟอร์มเว็บ (Web API, HTML, JS, CSS ฯลฯ)

    • ความเป็นสากล: เว็บเบราว์เซอร์ติดตั้งอยู่ในแทบทุกสภาพแวดล้อมทั่วโลก
    • ความเสถียร: มีนโยบายความเข้ากันได้ย้อนหลังที่เข้มแข็ง
    • ความลึก: ลึกมากจนการสร้างเบราว์เซอร์เองแทบเป็นไปไม่ได้ในทางปฏิบัติ
    • การใช้งานง่าย: แม้มีความซับซ้อนอยู่บ้าง แต่มีเอกสารและเครื่องมือพัฒนาที่ยอดเยี่ยม
    • ความครบถ้วน: ยกเว้นกรณีพิเศษอย่างไฟล์ เสียง วิดีโอ ก็ถือว่ามีความครบถ้วนสูงมาก

บทสรุป

  • แทนที่จะ copy-and-paste หรือใช้ dependency มากเกินไป การคิดเชิงวิพากษ์และการวิเคราะห์ต้นทุนโดยรวมเป็นสิ่งจำเป็น
  • เมื่อนำ dependency เข้ามาใช้ ต้อง ประเมินต้นทุนและประโยชน์ของ dependency ทุกตัวอย่างมีวิจารณญาณ และ
    พิจารณาความเสี่ยงและต้นทุนแฝงของทั้งระบบโดยรวม ด้วยเสมอ เพื่อให้เลือกได้อย่างรอบคอบ ซึ่งในระยะยาวทั้งประหยัดและปลอดภัยกว่ามาก

2 ความคิดเห็น

 
GN⁺ 2025-07-19
ความคิดเห็นใน Hacker News
  • เพิ่งเคยได้ยินตัวอย่างอย่าง TigerBeetle เลยไปลองหาดูเอง ถ้ากำลังสร้างฐานข้อมูลเลดเจอร์การเงินบน Zig ที่ให้ความสำคัญกับความปลอดภัยและประสิทธิภาพ และต้องรองรับธุรกรรมหนึ่งล้านรายการต่อวินาทีบน CPU คอร์เดียว การหลีกเลี่ยงการเพิ่ม dependency เพราะความเสี่ยงนั้นถือว่าสมเหตุสมผลอย่างยิ่ง แต่สำหรับนักพัฒนาทั่วไปส่วนใหญ่ พวกเขากำลังทำระบบ CRUD ธรรมดา ๆ และโดยทั่วไปก็ไม่ได้แข็งแกร่งขนาดนั้น dependency จำนวนมากอาจเต็มไปด้วยบั๊กก็จริง แต่บ่อยครั้งคุณภาพก็ยังดีกว่าสิ่งที่นักพัฒนาส่วนใหญ่จะทำขึ้นเองได้ ในความเป็นจริงบริษัทก็มักติดคอขวดที่ระดับของนักพัฒนาที่สามารถจ้างเพิ่มได้ สถานการณ์ของแต่ละคนต่างกัน จึงควรจำไว้ว่าแม้คำแนะนำจะขัดกัน แต่ก็อาจถูกต้องได้ภายในบริบทของตัวเองทั้งหมด

    • ฉันทำงานอยู่ที่ TigerBeetle จริง ๆ ประเด็นสำคัญคือบริบท TigerBeetle กับ rust-analyzer ต่างก็มีวัฒนธรรมการพัฒนาที่เข้มแข็ง แต่ปัญหาที่แก้ต่างกันจึงก่อให้เกิดวัฒนธรรมคนละแบบ dependency ที่บทความพูดถึง เช่น POSIX, ECMA-48, เว็บแพลตฟอร์ม เป็นสิ่งที่ใกล้เคียงกับ system interface มากกว่าจะเป็นไลบรารี ถ้าเป็น library dependency แล้วมีปัญหา ก็แค่เขียนใหม่เองได้ แต่ของพื้นฐานอย่าง system interface เปลี่ยนแทบไม่ได้หรือมีต้นทุนสูงมาก การตัดสินใจไม่ทำสิ่งที่ไม่สอดคล้องกับขอบเขตของซอฟต์แวร์นั้นทรงพลังมาก ตัวอย่างเช่น ถ้ามีทีมผู้เชี่ยวชาญด้านการเขียนโค้ดคูณเมทริกซ์ ก็อาจใช้ไลบรารีภายนอกสำหรับสิ่งอื่นที่ไม่เกี่ยวกับงานแกนหลักได้ แต่ฉันคิดว่าควรออกแบบการแยกความรับผิดชอบของผลิตภัณฑ์ให้ดีกว่านี้ แบบนี้จะช่วยแยก dependency ที่จำเป็นออกภายในระบบได้ดีขึ้น

    • ปัญหาของมุมมองนี้คือมันจะใช้ได้ก็ต่อเมื่อคุณนึกถึงแต่นักพัฒนาระดับล่างสุด วงการเทคโนโลยีมีแนวโน้มจะเจือจางคำแนะนำให้เหมาะกับระดับต่ำสุด และพูดตรง ๆ ว่าสถานที่ที่ฉันเคยทำงานมาแทบไม่มีที่ไหนเลยที่นักพัฒนาจะอ่อนแอถึงขั้น clone dependency ที่พังมาแก้ปัญหาเองไม่ได้ การดูแคลน CRUD ก็มีมากเกินไป abstraction ที่แย่สามารถทำให้เสียเวลาและสร้างความเจ็บปวดมหาศาลในงาน CRUD ได้เหมือนกัน แม้แต่ของยอดนิยมก็ยังมีปัญหาจุกจิกและประสิทธิภาพการทำงานต่ำ ถ้าไม่ใช่แค่ระดับ tutorial พื้นฐานจริง ๆ

    • มันไม่เกี่ยวกับระดับของนักพัฒนาเลย ไม่ว่าจะใช้ toolchain หรือผลิตภัณฑ์อะไร สุดท้ายก็ใช้ dependency ที่คนอื่นสร้างอยู่ดี คนที่ลงมือเขียนโค้ดคูณเมทริกซ์เองมีน้อยมาก และแม้แต่คนเหล่านั้นก็ไม่ได้ลงมือทำไลบรารีโอเพนซอร์สที่ไม่เกี่ยวกับตัวเองทุกตัวด้วย โดยมากคนจะหมกมุ่นกับ dependency เองเมื่อมีข้อกำหนดด้านกฎระเบียบ ความสนใจส่วนตัว หรือความผูกพันกับไลบรารีบางตัวเท่านั้น ถ้าทุกคนยึดหลักนี้แบบสุดทาง เราคงใช้ชีวิตเก็บทรายอยู่บนชายหาด

    • ความเห็นที่ว่า "นักพัฒนา CRUD โดยเฉลี่ยไม่แข็งแกร่ง" เป็นการสรุปแบบเหมารวมเกินไป นักพัฒนาส่วนใหญ่เลือกไม่ได้ว่าจะได้ทำงานกับระบบแบบไหน และระหว่างการพัฒนาก็มีทรัพยากรไม่พออยู่เสมอ ต้องใช้ dependency ราคาถูกเพื่อปล่อยซอฟต์แวร์ที่ใช้งานได้จริงให้เร็ว ดูเหมือนเป็นคำพูดจากคนที่ไม่เข้าใจความจริงข้อนี้ดีพอ

    • TigerBeetle เป็นสตาร์ทอัปที่เปิดตัวได้ไม่นาน และยังไม่มีเวอร์ชัน 1.0 อย่างเป็นทางการด้วย ผมคิดว่าเร็วเกินไปที่จะบอกว่าแนวทางนี้ได้ผลจริงหรือไม่

  • NIH(Not Invented Here) เองมีประโยชน์มากเมื่อใช้โดยตัดสินอย่างเป็นจริงเป็นจังว่าจะรับผิดชอบถึงแค่ไหน ตัวอย่างเช่น framework เว็บฝั่ง frontend ที่พอดีกับโดเมนของฉันแบบเป๊ะ ๆ มักคุ้มค่าที่จะสร้างและดูแลเอง แต่ถ้าเป็นฐานข้อมูล game engine เว็บเซิร์ฟเวอร์ หรือฟังก์ชันพื้นฐานด้านการเข้ารหัส เรื่องจะต่างออกไป ถ้าปัญหายากจนแก้ไม่ได้ด้วยโซลูชันที่มีอยู่แล้ว สิ่งแรกที่ควรคิดคือการนิยามปัญหาใหม่ ฉันคิดว่าการจัดกรอบปัญหาใหม่ถูกกว่าการสร้างชุดทดสอบทั้งหมดของ SQLite ขึ้นใหม่ตั้งแต่ต้นมาก

    • ฐานข้อมูล game engine เว็บเซิร์ฟเวอร์ cryptographic primitive เองก็มีหลายกรณีที่ทำเองดีกว่า ถ้าใช้แค่ไฟล์ธรรมดากับดัชนีในรันไทม์ หลายครั้งแม้แต่ SQLite ก็ยังมากเกินความจำเป็น หลายเกม โดยเฉพาะทีมเล็ก ๆ มักได้ประโยชน์จากเอนจินแบบคัสตอมมากกว่า จุดเด่นของเอนจินสำเร็จรูปมีแค่ pipeline แต่ก็แลกกับ overhead สูง เว็บเซิร์ฟเวอร์เองก็ไม่ได้ซับซ้อนกว่าแอป FastCGI มากนัก เรื่องคริปโตก็ไม่ใช่ทุกสถานการณ์ที่จะเป็นปัญหาความปลอดภัย ดังนั้นของง่าย ๆ อย่างการตรวจแฮชก็อาจเขียนเองได้ ผมคิดว่าไม่ควรเกิดภาวะ learned helplessness กับหัวข้อที่ดูยาก และที่สำคัญ ต่อให้โซลูชันเดิมแก้ปัญหาได้ ก็ไม่ได้แปลว่ามันเป็นวิธีที่ดีที่สุดหรือมีประสิทธิภาพที่สุดเสมอไป

    • ถ้าอย่างนั้นทำไมถึงมี database engine มากมาย? สุดท้ายระบบคอมพิวเตอร์ที่ซับซ้อนต่างก็มี trade-off ของตัวเอง ข้อจำกัด scalability concurrency security ลักษณะข้อมูล วิธีเก็บข้อมูล ฯลฯ ล้วนมีตัวเลือกหลากหลาย ผมคิดว่ายิ่ง dependency มีต้นทุนสูงเท่าไร ก็ยิ่งเชื่อใจ dependency ที่ตัวมันเองพยายามลด dependency อื่นให้น้อยที่สุดมากขึ้นเท่านั้น ระบบจัดการ dependency แบบอัตโนมัติมักยิ่งทำให้ปัญหาซับซ้อนขึ้น ดังนั้นการดูแลแบบ manual อย่างระมัดระวังจึงช่วยลดภาระได้

    • ผมคิดว่าเหตุผลในการใช้ third-party dependency มีอยู่สองอย่าง: (1) มันถูก publish โดยผู้ให้บริการโดยตรง และวงจรชีวิตค่อนข้างสอดคล้องกัน (2) มันมาแทนโค้ดซับซ้อนที่ผมไม่อยากเขียนเอง แบบ (1) ไม่มีปัญหาเพราะเป็นเหตุผลทางธุรกิจ เพียงแต่ต้องยอมรับว่าถ้าบริการนั้นอัปเดตอาจมีการเปลี่ยนแปลงใหญ่เกิดขึ้น แบบ (2) คุณค่าของมันจะขึ้นอยู่กับว่าความซับซ้อนของโค้ดที่ฉันกำลังหลีกเลี่ยงนั้นมากแค่ไหน การนำ dependency เข้ามาหมายความว่าฉันต้องใช้เวลาและทรัพยากรไปกับการอัปเดต/ทดสอบตามตารางของอีกฝ่าย และต้องรับภาระนั้นไว้เอง

    • คุณจะเจอปัญหาที่ RDBMS แก้ไม่ได้ค่อนข้างเร็ว RDBMS ถูกออกแบบโดยเน้นการแก้ไขข้อมูลพร้อมกันและรองรับชุดข้อมูลที่เปลี่ยนแปลงได้ ถ้าไม่ต้องการสิ่งนี้ ดัชนีแบบง่ายก็ให้ประสิทธิภาพที่ดีขึ้นอย่างมหาศาลได้ ถ้าข้อมูลเป็น immutable ก็สามารถทำระบบเองที่เร็วกว่า RDBMS มากได้

    • กรณีของ RDBMS น่าสนใจ ในวิกิพีเดียมี RDBMS มากกว่าร้อยแบบ และแต่ละตัวก็มีปัญหาที่แก้ได้กับแก้ไม่ได้ต่างกัน นี่คือผลลัพธ์ของการคิดเชิงปฏิบัติและลงมือทำจริง

  • dependency สร้างความเสี่ยง แต่ถ้าไม่ใช้เลยก็อาจเสียเปรียบด้านการพัฒนาและการแข่งขันในการออกสู่ตลาด ดังนั้นกระบวนการจัดการ dependency จึงสำคัญ

    1. พิจารณาเฉพาะ open-source dependency
    2. เวลานำเข้ามาใหม่ ไม่ใช่แค่ code review แต่ต้องดูหลายมิติ เช่น ไลเซนส์ ความพยายามที่ต้องใช้หากจะถอดออก ประวัติช่องโหว่ความปลอดภัยและบั๊ก ความต่อเนื่องของการอัปเดต และความคึกคักของชุมชน
    3. ถ้าเป็นไปได้ ควรมีการตรวจประเด็นความปลอดภัยของ dependency เป็นระยะ การทำในสเกลใหญ่มีต้นทุนสูง (ไอเดียที่เกี่ยวข้อง: https://blog.majid.info/supply-chain-vetting/)
    4. รับเฉพาะ dependency ที่สามารถแบกรับภาระการดูแล/ฟอร์กได้ อย่างน้อยควรเคยลอง build จาก source ด้วยตัวเอง
    5. ฟอร์ก dependency ทั้งหมดไว้ล่วงหน้า เพราะ repository อาจหายไปกะทันหันได้เหมือนกรณี left-pad
    • ข้อ 4 และ 5 สำคัญมากจริง ๆ แต่คนมักลืม แม้แต่ในโปรเจกต์ส่วนตัว พอกลับมาดูอีกทีหลังปล่อยทิ้งไว้พักหนึ่ง ก็พบว่า dependency ล้าสมัยไปแล้วหรือ repo ถูกลบไปแล้ว ดังนั้นเดี๋ยวนี้ฉันมักฟอร์ก source แบบ private และลอง build เอง รวมถึงฟอร์กในระดับ source ของ dependency ทั้งหมดของ dependency อีกทีด้วย แบบนี้แม้ ecosystem จะเปลี่ยนแรงในภายหลัง ก็ลดความเสียหายได้มาก ทำให้ฉันเริ่มชอบ source library มากกว่า binary

    • การฟอร์กในข้อ 5 อาจเป็นภาระมากเกินไป การ vendor dependency ไว้ใน git ของตัวเองหรือเก็บผ่าน cache proxy ก็เป็นวิธีที่ดี โดยเฉพาะกับโปรเจกต์อายุยืน ถ้าเป็นแบบ NodeJS ที่มีไฟล์ dependency เยอะ ๆ เครื่องมืออย่าง Yarn หรือ PNPM จะมีประสิทธิภาพกว่า

    • สำหรับข้อ 4 dependency ชื่อดังอย่าง SQLite น่าจะอยู่ได้นานกว่าผลิตภัณฑ์ที่ฉันกำลังสร้างมาก ถ้าคิดว่าผลิตภัณฑ์ของฉันจะอยู่นานกว่าโอเพนซอร์สตัวนั้นเอง นั่นต่างหากคือความหยิ่งเกินไป ฉันก็ไม่ได้คิดจะ build Linux kernel เองเหมือนกัน

    • โค้ดทั้งหมดควร build ได้อย่างน้อยโดยไม่ต้องเชื่อมต่อเครือข่าย จะดีที่สุดถ้าไม่มี binary artifact แต่ก็ใช่ว่าจะทำได้จริงเสมอไป

    • เป็นข้อสังเกตที่ยอดเยี่ยม ฉันจะเพิ่มเรื่องนี้ลงในเอกสารกระบวนการนำ dependency เข้ามาใช้ของทีม ปัญหาคือกรณีอย่าง JavaScript ที่ dependency tree ลึกเกินไป

  • จากประสบการณ์ยาวนาน ทุกอย่างสุดท้ายก็เป็นเรื่องของ "ขึ้นอยู่กับสถานการณ์(It Depends™)" ตอนหนุ่ม ๆ ฉันยึดติดกับหลักการและไม่ยอมรับข้อยกเว้น จนฝืนเอาไลบรารีหรือพาราไดม์ที่ไม่เหมาะมาใช้และสร้างโค้ดที่แย่มาก ทุกวันนี้ฉันมักทำสิ่งที่ใช้บ่อยให้เป็นไลบรารีของตัวเองและแยกเป็นแพ็กเกจไว้ ส่วนสิ่งจำเป็นที่ตัวเองทำไม่ได้จึงค่อยเติมด้วย dependency ภายนอก ถ้าคุณภาพหรือการดูแลรักษาดีพอจะยอมรับได้ ฉันก็ยินดีใช้ของภายนอก

  • อุตสาหกรรมพลังงานมีแนวโน้มจะหลีกเลี่ยง dependency โดยตั้งใจ เพราะถ้านำ dependency ภายนอกเข้ามา ก็ต้องตรวจดูการเปลี่ยนแปลงทั้งหมด เครื่องมือเขียนโค้ด AI ช่วยได้มาก และส่วนใหญ่ใช้สร้างเครื่องมือ CLI เอกสาร OpenAPI ก็ทำด้วย LLM แล้วนำไปเสิร์ฟด้วย Go standard library ตัว LLM เองเป็น dependency ภายนอกก็จริง แต่เครื่องมือ CLI ที่สร้างด้วยมันไม่ได้เกี่ยวกับโค้ดจริงโดยตรง จึงมีข้อกำหนดด้านคุณภาพต่ำกว่า แน่นอนว่านักพัฒนาฝั่ง frontend อาจไม่อยากทำงานโดยไม่มี React แต่เพราะผลิตภัณฑ์แบบนั้นถูกจัดการภายนอก จึงเป็นข้อยกเว้น ถ้าคุณมีเครื่องมือเล็ก ๆ ที่ช่วยให้นักพัฒนาไม่ต้องยึดติดกับ dependency เพื่อคุณภาพมากเกินไป นโยบายลด dependency ก็จะทำได้ง่ายขึ้น

    • LLM บางทีก็ปล่อยโค้ดโอเพนซอร์สบางส่วนที่ใช้ในการฝึกออกมาเป็นโค้ดจริง แล้วถ้าเป็นแบบนั้น มันก็แทบไม่ต่างจากการฟอร์ก dependency มาแล้วทำเหมือนเป็นของตัวเองหรือเปล่า?
  • ความสามารถในการแยกแยะ dependency ที่ดีและไม่ดีเป็นทักษะสำคัญ สำหรับฉัน dependency แบบเสียเงินมักเสียเปรียบ เพราะมีโอกาสสูงที่บริษัทผู้ให้บริการจะออกแบบมาเพื่อสร้าง lock-in แนวคิด "dependency minimalism" เป็นคอนเซปต์ที่ดี (ทวีตที่เกี่ยวข้องของ VitalikButerin)

    • dependency แบบเสียเงินมีแหล่งซัพพอร์ตเพียงแห่งเดียว ถ้าบริษัทที่ให้บริการปิดตัวลง ทั้งโปรเจกต์ก็เสี่ยงทันที เพราะบริษัทส่วนใหญ่ไม่ได้อยู่ตลอดไป จึงต้องพิจารณาเสมอว่าอนาคตของ dependency นั้นมีผลต่อทิศทางของโปรเจกต์เราหรือไม่

    • ฉันเคยมีประสบการณ์แย่กับ dependency แบบเสียเงินที่ถูกบังคับโดยทีมที่ไม่ได้มาจากสายเทคนิค ในทางกลับกัน dependency แบบ 'open-core' ที่ชุมชนใช้กันกว้างขวางอย่าง Sidekiq ดูน่าเชื่อถือกว่า เพราะโอกาสจะหายไปกะทันหันต่ำกว่ามาก ข้อดีของแบบเสียเงินคือถ้าบริษัทดำเนินงานอย่างมั่นคง ก็ไม่ต้องกังวลเรื่องซัพพอร์ต

    • ทั้งคอมโพเนนต์แบบเสียเงินและฟรีจากบริษัทต่างก็มี vendor lock-in อยู่แล้ว การบริหารความเสี่ยงเป็นหน้าที่ของทีมที่ทำ integration ต้องหาทางเลือกหรือออกแบบให้เป็นโมดูลเพื่อควบคุมความเสี่ยง

    • ถ้าเป็นแบบเสียเงิน ก็ควรส่งมอบผ่านอินเทอร์เฟซที่ยึดตาม open standard หรือมี implementation อื่นทดแทนได้เท่านั้น เพื่อป้องกัน lock-in ถ้ามีทางเลือกอื่น คุณก็ยังมีทางย้ายออกได้

    • การบอกว่า dependency แบบเสียเงินไม่ดี อาจสะท้อนว่ามีงบไม่พอ ผมอยากให้การดูแลโค้ดของผมเป็น "งาน" ของใครสักคน ถ้ามีผู้สนับสนุนมากหรือมีนักพัฒนาหลายคนที่รับผิดชอบด้วยความสมัครใจ ความมั่นคงก็จะดีขึ้น และแม้จะเป็นโปรเจกต์ส่วนตัว ถ้าเปิดซอร์สแล้วเกิดปัญหา ก็ยังต้องการคนที่ช่วยซัพพอร์ตได้ ความรับผิดชอบที่จะไม่ปล่อยทิ้งแม้บริษัทจะหยุดทำไปแล้วเป็นเรื่องสำคัญ

  • หลายคนดูจะหมกมุ่นกับการเขียนโค้ดใหม่ แต่ในความเป็นจริง แม้แต่ dependency ที่ห่วยก็ยังมีประสิทธิภาพกว่าการสร้างเองถึง 90% ของกรณี

    • dependency เป็นดาบสองคม บริษัทยักษ์ใหญ่ด้านซอฟต์แวร์หลายแห่งเลือกเขียนใหม่เพราะถูกกว่าการรักษาโค้ดเดิมไว้ ในบริษัทเว็บ/แบรนดิงขนาดเล็ก แทบไม่จำเป็นต้องมี backend คุณภาพสูงด้วยซ้ำ ในทางกลับกัน enterprise pattern ที่น่าหวาดหวั่นเกิดขึ้นก็เพราะต้องการให้โค้ดถูกแยกส่วนและบำรุงรักษาได้แม้ผ่านไป 5 ปี เอกสารถูกลืม และความทรงจำในอุตสาหกรรมหายไป โดยไม่ต้องพึ่ง dependency ภายนอก dependency ภายนอกมีความเสี่ยงหลักสองอย่างคือหยุดซัพพอร์ต หรือเกิดการเปลี่ยนแปลงแบบทำลายความเข้ากันได้ สุดท้ายมันกระทบ flow การพัฒนาฟีเจอร์ ถ้าเป็นคอมโพเนนต์ภายใน trade-off พวกนี้ก็ยังควบคุมกันเองได้ ถ้าเป็น SaaS การใช้ dependency ให้เร็วเพื่อความสำเร็จระยะสั้นอาจถูกต้อง แต่ถ้าความปลอดภัยและการซัพพอร์ตระยะยาวเป็นสิ่งจำเป็น ก็ต้องมองไกลกว่าเดิม การเขียนโค้ดใหม่แทบไม่เคยเป็นคอขวดขององค์กรจริง ๆ

    • ผมสงสัยว่าบริษัทของคุณจัดการช่องโหว่ความปลอดภัยและไลเซนส์อย่างจริงจังแค่ไหน เมื่อก่อนผมเคยผ่อนปรนกับ dependency มาก แต่พอย้ายไปบริษัทที่เข้มงวดเรื่องความปลอดภัยและไลเซนส์ มุมมองก็เปลี่ยนไปอย่างมาก

  • ความต่างระหว่างไลบรารีกับเฟรมเวิร์กก็สำคัญ ไลบรารีคือเครื่องมือที่ทำสิ่งหนึ่งได้ดี ส่วนเฟรมเวิร์กคือสิ่งที่กำหนดโครงสร้างทั้งแอป ชุมชน Go มักหลีกเลี่ยงเฟรมเวิร์กขนาดใหญ่ และชอบ standard library, ไลบรารีเบา ๆ และถ้าจำเป็นก็ถึงขั้นคัดลอก source มาใช้เอง ตัวอย่างเช่นเฟรมเวิร์กอย่าง Gin (เว็บ API), GORM (ORM) ใช้งานสะดวกก็จริง แต่จำกัดโครงสร้างภายในและเพิ่มความซับซ้อน Go มี standard SDK ที่แข็งแรงอยู่แล้ว จึงคิดว่าไม่ควรเพิ่ม dependency เกินจำเป็น

  • ผู้เขียนเป็นชาวนิวซีแลนด์ เบื้องหลังมีแนวคิดแบบ Number 8 wire ของนิวซีแลนด์ หรือก็คือทัศนคติแบบ 'ใช้ของที่มีอยู่กับฝีมือแก้ปัญหาเอาให้ได้' (บทความวิกิ Number 8 wire)

    • นักพัฒนาที่มีประสบการณ์จากประเทศอื่นนอกนิวซีแลนด์ก็เห็นด้วยคล้ายกันอยู่มาก แทบทุกคนเคยเจ็บตัวจากการเลือก dependency ผิดหรืออัปเกรดไลบรารีที่ไม่จำเป็น

    • ในฐานะคนจากนิวซีแลนด์ ฉันรู้สึกว่าแนวคิด Number 8 Wire หายไปแล้วตั้งแต่ 20 ปีก่อนด้วยซ้ำ

    • วันนี้เพิ่งรู้เรื่องนี้เป็นครั้งแรก ทำให้นึกถึงสำนวนออสเตรเลีย "She'll buff out, mate"

  • ขนาดการใช้งานเชิงพาณิชย์ในวงกว้างของ dependency ก็ส่งผลต่อ scalability ด้วย ถ้าเป็นเครื่องมือที่ถูกใช้ในที่ที่ deploy ใหญ่กว่าของฉัน 100 ถึง 1000 เท่า โอกาสที่มันจะชนข้อจำกัดในปัญหาของฉันก็น้อยลง บั๊กในระดับนั้นก็มักถูกเจอหรือแก้ก่อน ทำให้สุดท้ายมันปลอดภัยกว่าสำหรับฉันด้วย

    • แม้แต่ไลบรารีใหญ่ก็มีกรณีที่ใช้ในสภาพแวดล้อมเล็กแล้วใช้ไม่ได้เลย ตัวอย่างเช่นคอมไพเลอร์โปรโตคอลบัฟเฟอร์ของ Swift เคยแครชเมื่อเจอฟิลด์ที่คาดไม่ถึง บริษัทใหญ่หลายแห่งก็ทดสอบเฉพาะเส้นทางที่เกี่ยวกับสเกลใหญ่ของตัวเองเท่านั้น

    • ฉันเคยเจอบั๊กร้ายแรงในไลบรารียอดนิยมจากบริษัทใหญ่อย่าง Meta, Google, Microsoft ด้วย แจ้ง issue ไปกว่าจะได้แก้ก็นาน แถมยังต่อต้านการเปลี่ยนแปลงสูง ในสถานการณ์แบบนี้ กลับกลายเป็นว่าการทำเองเร็วกว่าและได้ประสิทธิภาพดีกว่าด้วย โดยเฉพาะในวงการคอนซัลต์ ทิศทางงานมักสั่นคลอนจากคำขอที่ไม่สมเหตุสมผลของลูกค้า พอในฐานะนักพัฒนาฉันมั่นใจมากขึ้นว่าทำเองได้ บ่อยครั้งการลงมือเขียนเองก็ดีกว่าพึ่ง dependency ภายนอกขนาดใหญ่ แน่นอนว่าฉันไม่ได้ทำสิ่งระดับเบราว์เซอร์หรือโมเดล AI แต่ของอย่าง inference engine แบบ local, HTML renderer, หรือแม้แต่ graph database ที่เขียนเอง ฉันก็ทำ ลูกค้าไม่ได้คาดหวังนวัตกรรมใหม่ แต่คาดหวังการลดความเสี่ยง ถ้าฉันพัฒนาเอง การคุมกำหนดส่งก็ง่ายกว่ามาก ใช้เวลาสร้างเองยังคุ้มกว่านั่งไล่อ่านเอกสารของ Google หรือบริษัทยักษ์อื่น ๆ ด้วยซ้ำ ช่วง 3 เดือนที่ผ่านมา ฉันทำงานวันละ 12 ชั่วโมงเพื่อกอบกู้โปรเจกต์ที่ทีมก่อนหน้าปล่อยทิ้งไว้ เลยคิดเรื่องพวกนี้อยู่มากตอนดึก ๆ