3 คะแนน โดย GN⁺ 2023-12-29 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • โครงการซอฟต์แวร์บางส่วนเป็นแบบ เลือดอุ่น ที่ต้องมีการพัฒนาอย่างต่อเนื่องจึงจะดูแลรักษาได้ ส่วนโครงการที่แม้กิจกรรมจะหยุดไปแล้วก็ยังกลับมาทำต่อได้ภายหลัง จะใกล้เคียงกับแบบ เลือดเย็น มากกว่า
  • โครงการแบบเลือดเย็นจะเลือกใช้ เทคโนโลยีที่น่าเบื่อ เพื่อให้แม้หยุดไปนานก็ยังไม่ทำให้การ build และ test พัง และหลีกเลี่ยงการพึ่งพาบริการภายนอกที่อาจเปลี่ยนไปหรือหายไปได้
  • การถูกซื้อกิจการหรือปิดตัวของบริการที่พึ่งพา, การอัปเกรดคอมไพเลอร์, และการยุติการรองรับแพ็กเกจ ล้วนย้อนกลับมาเป็น ต้นทุนการบำรุงรักษา เมื่อต้องเริ่มโครงการที่มีกิจกรรมน้อยใหม่อีกครั้ง
  • โค้ดอย่างโปรเจกต์ส่วนตัวที่ปล่อยทิ้งไว้ 1 ปี, 2 ปี, 3 ปีโดยไม่แตะต้อง มักสร้างความร้อนอย่างต่อเนื่องได้ยาก จึงควรออกแบบโดยตั้งสมมติฐานเรื่อง อัตราการเปลี่ยนแปลงต่ำ ไว้ตั้งแต่แรก
  • ตัวสร้างเว็บไซต์แบบ static สำหรับบล็อกนี้ ตั้งแต่ commit แรกในปี 2012 ก็ยังทำงานต่อเนื่องแทบไม่ต้องแก้อะไร โดยอาศัยเพียง Python 2, โมดูล third-party 4 ตัวที่รวมไว้ในรีโพซิทอรี, การรันบนเครื่องโลคัล, และการ deploy ด้วย rsync over ssh

วิธีดูแลรักษาโปรเจกต์ผ่านอุปมาเรื่องสัตว์เลือดเย็น

  • ในคาบเรียนประวัติศาสตร์ธรรมชาติปี 2004 อาจารย์ได้นำ ลูก painted turtle ที่หยิบมาจากช่องแช่แข็งมาวางไว้ใต้กล้องระหว่างการสอน
  • ลูก painted turtle เป็นหนึ่งในไม่กี่สายพันธุ์ที่สามารถรอดชีวิตได้แม้อยู่ในสภาพเยือกแข็ง
  • ตลอดหนึ่งชั่วโมง เต่าเริ่มจากการเคลื่อนไหวที่แทบมองไม่เห็น และเมื่อจบคาบก็เคลื่อนที่ไปได้ราวครึ่งหนึ่งของหน้าจอ
  • สัตว์เลือดอุ่นต้องรักษาอุณหภูมิร่างกายให้อยู่ในช่วงแคบ ๆ โดยมนุษย์จะมีปัญหาหากหลุดจากประมาณ 37°C
  • สัตว์เลือดเย็นจะปรับการเผาผลาญตามอุณหภูมิรอบตัว กระฉับกระเฉงเมื่ออุ่น และยิ่งร่างกายกับสภาพแวดล้อมเย็นลงก็จะยิ่งเคลื่อนไหวช้าลง
  • โครงการซอฟต์แวร์ก็แบ่งได้คล้ายกัน
    • ซอฟต์แวร์แบบเลือดอุ่น จะทำงานได้ดีเมื่อมีการเคลื่อนไหวและความร้อนในโปรเจกต์อย่างต่อเนื่อง
    • หากปล่อยหยุดไว้ 6 เดือน เมื่อนำกลับมาอีกครั้งก็อาจกลายเป็นโปรเจกต์ที่ตายไปแล้ว

เงื่อนไขและตัวอย่างของซอฟต์แวร์แบบเลือดเย็น

  • สาเหตุที่โครงการแบบเลือดอุ่นกลับมาเริ่มใหม่ได้ยาก เป็นเพราะความเปลี่ยนแปลงภายนอกสะสมตัวขึ้นเรื่อย ๆ
    • บริการที่ CI พึ่งพาอาจถูกซื้อกิจการหรือเงินหมดจนใช้งานไม่ได้
    • การเพิ่ม dependency ใหม่อาจทำให้จำเป็นต้อง อัปเกรดคอมไพเลอร์
    • แพ็กเกจอื่นอาจหยุดการรองรับจนไม่ทำงานกับคอมไพเลอร์รุ่นล่าสุด
  • โครงการที่ทำคนเดียว เปลี่ยนแปลงเฉพาะตอนมีแรงบันดาลใจ และปล่อยทิ้งไว้นานกว่าหนึ่งปีก่อนกลับมาแตะอีกครั้ง เป็นสิ่งที่ดูแลในแบบเลือดอุ่นได้ยาก
  • โครงการแบบเลือดเย็นควรเป็นเหมือนลูก painted turtle ที่ถูกแช่แข็ง คือแม้ผ่านไป 1 ปี ก็ยังกลับมาเริ่มต่อจากจุดที่หยุดไว้ได้
  • เพื่อให้เป็นเช่นนั้น จึงควรใช้ boring technology และหลีกเลี่ยงการพึ่งพาบริการภายนอกที่สคริปต์ build และ test อาจเปลี่ยน พัง หรือหายไปโดยสิ้นเชิง
  • dependency ควรถูกรวมไว้ในรีโพซิทอรีของโปรเจกต์เอง เช่นแนวทาง vendored dependencies
  • ซอฟต์แวร์ที่ขับเคลื่อนบล็อกนี้คือตัวอย่างของโครงการแบบเลือดเย็น
    • commit แรกคือวันที่ 8 มกราคม 2012 และเป็น ตัวสร้างเว็บไซต์แบบ static ขนาดเล็กเพื่อใช้แทนการติดตั้ง Wordpress เก่า
    • เขียนด้วย Python 2 และพึ่งพาโมดูล third-party 4 ตัว ซึ่งทั้งหมดถูก commit ไว้ในรีโพซิทอรีของโปรเจกต์
    • ทุกขั้นตอนรันบนเครื่องโลคัล และนำผลลัพธ์ไป deploy ด้วย rsync over ssh
    • นอกจากการปรับปรุงเล็กน้อยบางอย่างแล้ว มันก็ทำงานต่อเนื่องมาได้โดยไม่ต้องแก้ไข และคาดว่าจะยังทำงานได้ต่อไปอีก 12 ปีข้างหน้า

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

 
GN⁺ 2023-12-29
ความเห็นจาก Hacker News
  • เว็บเฟรมเวิร์ก Express ในอีโคซิสเต็มของ Node และ JavaScript ปัจจุบันยังคงรักษาเวอร์ชันหลัก 4.x.x มานานกว่า 10 ปีแล้ว https://www.npmjs.com/package/express?activeTab=versions
    ถึงอย่างนั้นก็ยังถูกใช้งานอย่างแพร่หลายจนมียอดดาวน์โหลดมากกว่า 17 ล้านครั้งต่อสัปดาห์ https://www.npmjs.com/package/express แม้ฟีเจอร์จะไม่ได้ครบที่สุดหรือประสิทธิภาพไม่ได้ดีที่สุด https://fastify.dev/benchmarks/ แต่ข้อดีคือช่วยให้พัฒนาได้รวดเร็วและเสถียร รวมถึงวางแผนระยะยาวได้
    ไม่ต้องกังวลเรื่องการหยุดแพตช์ความปลอดภัยของเวอร์ชันเก่า หรือการเปลี่ยน API แบบรุนแรง ส่วน Go นั้นเสถียรกว่านั้นอีก เพราะมี standard library ที่กว้างขวางและคำมั่นเรื่องความเข้ากันได้ ทำให้โปรแกรมที่เกิน 10 ปีก็ยังรันได้ https://go.dev/doc/go1compat

    • รู้สึกประหลาดใจแบบดี ๆ ที่ Express มีอายุถึง 13 ปีแล้ว
      ตอนเพิ่งออกมาใหม่ ๆ มักถูกมองว่าเป็นขยะสำหรับโปรแกรมเมอร์ปลอม ๆ ที่ห่วยแตก เพียงเพราะเขียนด้วย JavaScript แต่หลังจากนั้นมันก็ช่วยให้หลายบริษัทสร้างบริการเจ๋ง ๆ ที่ทำเงินได้จริงด้วย Express และตอนนี้ก็น่าจะกำลังรองรับคำขอจำนวนมหาศาลอยู่
      ทุกวันนี้ก็เขียนด้วย Go เยอะเหมือนกัน แต่ก็ยังพอใจกับ การสร้างบริการด้วย Express อยู่มาก และโดยรวมมองว่าเป็นซอฟต์แวร์ที่ดี
    • ว่ากันว่า Express v5 จะออกมาในอนาคตอันใกล้ (https://github.com/expressjs/express/issues/4920)
    • CakePHP ก็ให้เสถียรภาพแบบนี้เหมือนกัน และนั่นทำให้ไม่ค่อยอยากเลือก RoR
      ไม่ใช่ว่าไม่ชอบจริง ๆ หรอก แต่คงไม่เลือกเพราะวงล้อการอัปเกรดเวอร์ชันที่ไม่มีวันจบ
  • Python เป็นตัวอย่างของซอฟต์แวร์เลือดเย็นที่แย่มาก
    มี breaking changes ทั้งฝั่ง runtime และเครื่องมืออยู่เรื่อย ๆ และผู้เขียนก็อยู่ในสถานการณ์ที่ยังต้องใช้ Python 2 ซึ่งสิ้นสุดการซัพพอร์ตไปนานแล้ว
    ตัวอย่างที่ดีกว่าคือภาษาที่โค้ดอายุ 10 ปีก็ยังทำงานได้ดีกับเครื่องมือสมัยใหม่ เช่น Go หรือ Java และถ้าสุดขั้วกว่านั้นก็คือ Perl ที่โค้ดอายุ 30 ปีก็ยังทำงานได้ดีอยู่

    • พูดถูกแล้ว
      เวลาสร้างซอฟต์แวร์ เรามักทำพลาดจนเปิดทางให้ผู้ใช้ทำบางอย่างในแบบที่เราไม่ได้ตั้งใจ ในโลก Java วิธีแก้คือเพิ่มฟีเจอร์ที่ใหม่กว่า ปลอดภัยกว่า และแสดงเจตนาได้ชัดกว่า แล้วแนะนำให้ผู้ใช้ย้ายไปใช้สิ่งนั้น
      Python ก็คล้ายกัน แต่จะมี “แล้วอีกไม่นานจะปิดฟีเจอร์เก่า” พ่วงมาด้วย ส่วน Java ไม่ทำแบบนั้น
      ตัวอย่างเช่น เมธอด equals ของ java.net.URL เป็นที่รู้กันว่าออกแบบพังและถูกแนะนำอย่างหนักว่าไม่ควรใช้ แต่ก็ยังซัพพอร์ตต่อเนื่องมากว่า 20 ปี
      ใน Python Airflow ตัวดำเนินการว่างเคยรองรับชื่อ DummyOperator อยู่พักหนึ่ง แต่ผู้ดูแลเปลี่ยนให้ใช้ EmptyOperator เพราะคำว่า “dummy” เคยถูกใช้เป็นคำเหยียดในเชิงประวัติศาสตร์และวัฒนธรรม แล้วทำให้ชื่อเดิมพัง
      เมื่ออัปเกรดแล้ว โค้ดจะเกิดข้อผิดพลาดตั้งแต่ตอนโหลด จนกว่าจะเปลี่ยนชื่ออ้างอิง โดยส่วนตัวแล้วคงไม่ทำให้ผู้ใช้พังด้วยวิธีแบบนี้
      ถ้าเป็นโลก Java การเปลี่ยนชื่อระดับนี้รับมือได้ด้วยการแทนที่ข้อความก็พอ ดังนั้นก็น่าจะยังซัพพอร์ตต่อไปจนกว่าจะมีเหตุผลที่ทนไม่ไหวจริง ๆ
      ดังนั้นโดยรวมจึงมองว่า Java และ dependency ของไลบรารี Java สามารถอัปเกรดได้อย่างอิสระกว่า Python มาก
    • Maven ยอดเยี่ยมมาก
      ถ้าใช้ Java เวอร์ชัน LTS และเลือก dependency ที่ดี ก็ทำให้กลับมารันได้อีกเมื่อไรก็ได้
      ใน Python เคยมีกรณีในคลาสแมชชีนเลิร์นนิงที่ dependency ตัวหนึ่งใส่การเปลี่ยน API ที่ทำให้พังภายในคืนเดียว และผู้สอนก็ไม่ทันสังเกต เพราะยังใช้เวอร์ชันล่าสุดจากเมื่อหลายสัปดาห์ก่อนตอนเริ่มเตรียมสอนอยู่
    • ไม่ค่อยเข้าใจว่าคำว่า “มี breaking changes อยู่เรื่อย ๆ” หมายถึงอะไร
      ตอนย้ายจาก Python 2 ไป 3 นั่นเป็น breaking change ก็จริง แต่มันเป็นการเปลี่ยนแปลงครั้งเดียว ไม่ใช่ “breaking changes ที่เกิดขึ้นต่อเนื่อง”
      ถ้ายังอยู่ในเวอร์ชันหลักเดียวกัน โค้ดเก่าจะไม่พังเพราะเวอร์ชันย่อยใหม่ เช่น โค้ด 2.x เก่า ๆ ก็รันได้ดีบน 2.7 และโค้ด 3.x เก่า ๆ ก็รันได้ดีบน 3.12
      การเปลี่ยนเวอร์ชันย่อยอาจเพิ่มฟีเจอร์ใหม่ได้ แต่โค้ด 3.x เก่า ๆ ไม่ได้พังเพียงเพราะไม่ได้ใช้คีย์เวิร์ด async หรือ type hints
    • เห็นด้วย
      นี่เป็นหนึ่งในเหตุผลที่พยายามเลี่ยง Python ถ้าเป็นไปได้
      รู้สึกว่า โค้ด Python ที่เขียนวันนี้มีโอกาสน้อยที่จะยังทำงานได้ในอีกไม่กี่ปีข้างหน้า และมองว่าเป็นปัญหาค่อนข้างใหญ่
    • สำหรับคำว่า “โค้ด Java อายุ 10 ปียังทำงานได้ดีกับเครื่องมือสมัยใหม่” อันนี้ไม่แน่ใจ
      แค่พยายามรัน โค้ด Java อายุ 3 ปี ด้วย SDK ใหม่ ก็มักมีอะไรบางอย่างพังอยู่เสมอ
  • ผมทำงานกับเมนเฟรม IBM (z/OS) และแทบไม่เคยเห็นที่ไหนใกล้เคียง IBM ในเรื่อง การรักษาความเข้ากันได้ย้อนหลัง
    ผมคิดว่า Microsoft Windows น่าจะเป็นอันดับ 2 และ Linux kernel ABI ประมาณอันดับ 3 แต่ถ้ามองทั้งอีโคซิสเต็มของ Linux ก็เป็นเพียงส่วนเล็ก ๆ เท่านั้น
    ส่วนที่เหลือส่วนใหญ่แทบจะเป็น churn และในโอเพนซอร์สดูเหมือนจะมีไม่กี่คนที่อยากใช้เวลากับความเข้ากันได้ย้อนหลังเป็นงานอดิเรก
    ในเชิงเศรษฐศาสตร์มันดูเหมือนภาวะกลืนไม่เข้าคายไม่ออกของนักโทษ ทุกคนผลักภาระต้นทุนการรักษาความเข้ากันได้ไปให้คนอื่น และสุดท้ายก็สร้างงานที่ไร้ประโยชน์ต่อทุกคนมากขึ้น

    • ในโอเพนซอร์สมีแนวโน้มไล่ตามสิ่งใหม่และแวววาวอยู่มาก แต่ก็ยากจะบอกว่าเป็นแบบนั้นกับทุกคน
      ตัวอย่างเช่น ถ้าดู ชุมชนเรโทรคอมพิวติ้ง ก็ไม่ใช่เรื่องแปลกที่จะเขียนไดรเวอร์เพื่อให้ฮาร์ดแวร์ใหม่ทำงานกับระบบปฏิบัติการเก่าได้
    • ถ้าได้รับเงินสำหรับการบำรุงรักษา ก็ช่วยได้มากจริง ๆ
      ถ้าไม่มีค่าตอบแทน สุดท้ายก็ขึ้นอยู่กับว่าคุณรักแพลตฟอร์มที่กำลังสร้างมากแค่ไหน และผมตัดสินใจเล็งเป้าไปที่ Linux kernel โดยตรงผ่าน system call เพราะมันมีความทุ่มเทที่พิสูจน์แล้วต่อเสถียรภาพของ ABI
      ในทางกลับกัน ภาษาโปรแกรมที่ผมสร้างเองทำให้ผมอยากทำให้มัน “สมบูรณ์แบบ” เท่าที่เป็นไปได้ จึงคอยอยากแก้มันอยู่เรื่อย ๆ
      เผื่อมีใครคิดจะเอาไปใช้อย่างบ้าคลั่ง ผมจึงใส่ประกาศไว้ใน README ว่ายังอยู่ในช่วงพัฒนาเริ่มต้นและยังไม่เสถียร
      ผมคิดว่าคนทำ Ruby หรือ Python ก็น่าจะรู้สึกคล้ายกัน และเพราะภาษารู้สึกเหมือนลูกของตัวเอง อยากให้มันประสบความสำเร็จ จึงอาจมองว่าต้องแก้ความผิดพลาดอย่างกรณีที่ print เคยเป็นคีย์เวิร์ด
    • ไม่ใช่แค่ปัญหาความเข้ากันได้ย้อนหลังเท่านั้น แต่ถ้าปล่อยทิ้งไว้ไม่นาน โอกาสที่มันจะพังด้วยเหตุผลสุ่ม ๆ ก็สูงด้วย
      หลายครั้งส่วนที่ทำไว้เพื่อความเข้ากันได้ย้อนหลังกลับเป็นสิ่งที่พังเสียเอง
      ที่ทำงานก่อนหน้านี้ เราสร้างแอป Node แบบคอนเทนเนอร์ และ CI สร้างอิมเมจจากซอร์สของ Node แต่จู่ ๆ การ deploy ของบริการที่ไม่ได้แตะมาพักใหญ่ก็เริ่มล้มเหลว
      ปรากฏว่า Dockerfile อิงกับอิมเมจ Ubuntu ที่หมดระยะซัพพอร์ตแล้ว และคลังอัปเดตถูกย้ายไปยังคลังเก็บถาวร ทำให้ถ้าไม่แก้ Dockerfile ก็สร้างอิมเมจไม่ได้
      นี่เป็นตัวอย่างของซอฟต์แวร์ที่ไม่ได้แตะต้องแต่กลับพัง และนั่นคือเหตุผลที่ผมชอบ Go กับไบนารีเดี่ยว
      ถ้าแพ็กเกจไว้เป็นรีลีส ก็ไม่ต้อง build ใหม่อีก และใน Distroless Docker image ก็ไม่มี dependency อื่นนอกจากไบนารีของผม
      ผมใช้ Go มานานแต่ไม่เคยเจอปัญหาซอฟต์แวร์เสื่อมสภาพตามอายุ และปัญหาหลายแบบที่เคยรู้สึกเวลาใช้ Node หรือ PHP ก็หายไป
      ปัญหาใหญ่อันดับสองฝั่ง Node คือแพตเทิร์นการอ้อมชั้น (indirection) ของเฟรมเวิร์ก ส่วนอันดับหนึ่งคือการจัดการแพ็กเกจ
      ปัญหา peer dependency แบบ “ติดตั้งเวอร์ชัน X แล้ว แต่โมดูล Y ต้องการเวอร์ชัน Z” โผล่มาเรื่อย ๆ
  • วิศวกรจำนวนมากเวลาไลบรารีใน GitHub จะดูเวลาคอมมิตล่าสุด
    พวกเขามักคิดว่ายิ่งคอมมิตใหม่เท่าไร ไลบรารีนั้นก็ยิ่งได้รับการซัพพอร์ตดีเท่านั้น
    แต่ถ้าเป็นโปรเจกต์ที่ถูกเก็บถาวรซึ่งทำสิ่งที่ต้องการได้ตรงเป๊ะ ไม่มีบั๊กเลย และเสถียรมาหลายปี ก็เหมือนเจออัญมณีที่ซ่อนอยู่ในร้านของมือสอง
    ทุกวันนี้วิศวกรหลายคนจะทิ้งไลบรารีที่ไม่ได้ถูกอัปเดต “อย่างต่อเนื่อง” โดยอัตโนมัติ และดูเหมือนจะมองว่านั่นเป็นเรื่องดี

    • ไลบรารีจะคงที่ได้ สภาพแวดล้อมที่มันถูกใช้ก็ต้องคงที่ด้วย
      สภาพแวดล้อมการพัฒนาซอฟต์แวร์สมัยใหม่มักไม่เป็นเช่นนั้น และเว็บฟรอนต์เอนด์เป็นตัวอย่างชัดเจนที่เปลี่ยนบ่อย
      ถ้าเป็นไลบรารีที่เป็นอิสระอย่างสมบูรณ์ การไม่มีอัปเดตก็อาจไม่เป็นไร แต่ไลบรารีที่พึ่งพาเว็บฟรอนต์เอนด์เฟรมเวิร์กจะก่อปัญหาถ้าไม่อัปเดตตามการเปลี่ยนแปลงของอีโคซิสเต็ม
    • พูดให้เข้มงวดแล้วมันไม่ได้ถูกเสมอไป แต่การดู ว่ามีการอัปเดตล่าสุดหรือไม่ เป็น heuristic ที่ยอดเยี่ยม
      ผมไม่รู้ตัวเลขจริง แต่คิดว่าในกรณีส่วนใหญ่แบบท่วมท้น การไม่มีความเคลื่อนไหวล่าสุดหมายถึง “ถูกทิ้ง” ไม่ใช่ “เสร็จสมบูรณ์และไม่มีบั๊ก”
    • ผมเคยเห็นกราฟที่แสดงว่าภาษาโปรแกรมต่าง ๆ เปลี่ยนไปอย่างไรเมื่อเวลาผ่านไป และโค้ดเดิมเหลืออยู่มากแค่ไหน
      บางภาษากลายเป็นแทบคนละหน้ากับเวอร์ชัน 1.0 ส่วนบางภาษายังคงโค้ดที่เขียนไว้ส่วนใหญ่ไว้ แล้วแค่เติมของใหม่ไว้ด้านบน
      สุดท้ายแนวโน้มนั้นก็ดูเหมือนสะท้อนอยู่ในชุมชนและอีโคซิสเต็มด้วย
      ผมจำได้ว่า Clojure อยู่แถวบนของรายการเพราะแทบไม่ทำ breaking change และไลบรารีที่เปลี่ยนครั้งสุดท้ายเมื่อ 5 ปีก่อนก็ยังรันได้สมบูรณ์ดีบนเวอร์ชันภาษาปัจจุบัน
      การเป็นตระกูล Lisp ที่สามารถขยายแกนของภาษาได้โดยไม่ต้องเปลี่ยน upstream ก็น่าจะช่วยด้วย แต่แน่นอนว่ามันก็มีข้อเสียของมันเอง
      ถึงอย่างนั้น สิ่งที่ดีคือมันทำให้ผมหยุดคิดว่า “ความสดใหม่” เท่ากับ “ความยอดเยี่ยม”
      ทุกวันนี้ผมใช้ไลบรารีที่แทบไม่เปลี่ยนมาหลายปีบ่อยกว่าไลบรารีที่เพิ่งสร้างขึ้นเมื่อปีที่แล้ว และก็ไม่มีปัญหาใหญ่
    • ขึ้นอยู่กับภาษา
      บางภาษาออกรีลีสทุก 1–2 ปี และเพิ่มไวยากรณ์ใหม่ที่สวยงาม หรือ abstract data type ในไลบรารีมาตรฐาน เพื่อแทนที่แพตเทิร์นที่ใช้กันบ่อยแต่ดูขัด ๆ
      ชุมชนของภาษานั้นมักมองไวยากรณ์ใหม่ว่าเป็น “สำนวนที่ถูกต้อง” แทบจะทันที และเห็นว่าโค้ดที่เขียนด้วยวิธีทื่อ ๆ แบบเดิมควรถูกแก้
      เหตุผลที่เสนอให้เปลี่ยน codebase หนึ่ง ๆ มักเป็นเพราะวิธีเดิมเมื่อเทียบกับไวยากรณ์ใหม่แล้วคลุมเครือกว่า และทำให้การบำรุงรักษากับ code review ยากขึ้น
      ตรรกะคือ ถ้าไวยากรณ์ใหม่นั้นมีมาตั้งแต่แรก ก็ไม่มีใครคิดว่าวิธีเดิมเป็นโค้ดที่ดี ดังนั้นควรอัปเดตโค้ดเพื่อลดกำแพงด้านความอ่านง่ายและการเข้ามามีส่วนร่วมของนักพัฒนาใหม่
      ถ้าไลบรารีที่เขียนด้วยภาษาแบบนี้ ไม่ได้อัปเดตมานานกว่า 3 ปี ก็มักเป็นสัญญาณไม่ดี
      อาจหมายความว่านักพัฒนาไม่ได้เชื่อมต่อกับชุมชนมากพอที่จะรักษาโค้ดให้เป็นสำนวนที่นักพัฒนาคนอื่นซึ่งเรียนภาษารูปแบบล่าสุดอ่านได้ง่าย และอาจไม่มีความสนใจรับ PR จากภายนอกด้วย
    • ถ้า “บั๊ก 0 ตัว” หมายถึง GitHub issue 0 รายการ ก็ต้องระวัง
      อาจมี ช่องโหว่ด้านความปลอดภัย อยู่ เพียงแต่ไม่มีใครรายงานเพราะโปรเจกต์ถูกมองว่าถูกทิ้งไปแล้ว
  • ซอฟต์แวร์ที่อยู่ได้โดยไม่ต้องอัปเดต มีเพียงซอฟต์แวร์ที่ทำมาอย่างถูกต้องตั้งแต่แรกเท่านั้น
    ถ้าเป็นซอฟต์แวร์ที่ทำเพื่อตัวเอง เรื่องนี้ค่อนข้างง่าย เพราะมีโอกาสสูงที่ผ่านไป 10 ปีรสนิยมของตัวเองจะไม่เปลี่ยนไปมาก และเนื่องจาก n เล็ก ปัญหาเล็ก ๆ ที่ถึงจะมีฟังก์ชัน O(n) แต่ใช้ฟังก์ชัน O(n^2) ก็ยังพอมองข้ามได้
    แต่ถ้าเป็นซอฟต์แวร์ที่คนอื่นจะใช้ ความต้องการก็จะแตกต่างกัน และเมื่อ N ใหญ่พอ ฟังก์ชัน O(n) ก็จะมีคุณค่าขึ้นมา จนเกิดปัญหาในทำนองนี้
    ไม่ว่าจะเขียนไว้ใช้เองหรือให้คนอื่นใช้ ก็อาจมีปัญหาที่คาดไม่ถึงโผล่มาได้
    เช่น ถ้าประมวลผลไฟล์เกิน 1GB แล้วแครช แต่ปกติใช้แค่ไฟล์ต่ำกว่า 100KB เลยไม่เคยสนใจ และพอจะแก้ก็อาจต้องเขียนใหม่ครึ่งหนึ่ง
    ข้อโต้แย้งที่สำคัญที่สุดต่อความคิดที่ว่า ซอฟต์แวร์ที่ไม่เปลี่ยนแปลงย่อมดีกว่าซอฟต์แวร์ที่เปลี่ยนบ่อยโดยเนื้อแท้ อยู่ตรงนี้เอง
    ซอฟต์แวร์ที่ไม่เปลี่ยนแปลงอาจสมบูรณ์แบบมาตั้งแต่แรกก็ได้ แต่ก็อาจมีความน่ากลัวซ่อนอยู่ลึก ๆ และแยกแยะล่วงหน้าได้ยาก
    ทั้งนี้ไม่ได้หมายความว่าซอฟต์แวร์ที่อัปเดตเร็วจะดีกว่าซอฟต์แวร์ที่อัปเดตช้าโดยเนื้อแท้เช่นกัน เพราะยังมีปัจจัยอื่นอีกมากนอกจากความเร็วในการอัปเดต

    • ผมมองว่าไม่ได้หมายความว่าซอฟต์แวร์ห้ามเปลี่ยนแปลงโดยเด็ดขาด
      ถ้าความต้องการเปลี่ยน ซอฟต์แวร์ก็ต้องเปลี่ยนตามเป็นธรรมดา
      แต่ตลอด 10 ปี อาจมีเรื่องมากมายที่เกิดขึ้นโดยไม่เกี่ยวกับการเปลี่ยนแปลงของความต้องการ
      โปรเจกต์โอเพนซอร์สอาจถูกทิ้งหรือเปลี่ยนทิศทาง ซอฟต์แวร์เชิงพาณิชย์อาจถูกยุติ บริษัทอาจถูกซื้อกิจการ กฎของ App Store หรือ Play Store อาจเปลี่ยน API อาจหายไปหรือเปลี่ยนราคา จนทำให้ความคุ้มค่าทางเศรษฐกิจของโปรเจกต์พังลง
      toolchain, framework, ภาษาโปรแกรม, paradigm และ best practice ก็เปลี่ยนไปด้วย
      แก่นสำคัญคือการไม่ปล่อยให้การเปลี่ยนแปลงภายนอกที่ไม่เกี่ยวกับความต้องการมาบังคับให้เราต้องเปลี่ยนตาม
      เป็นหลักการที่ดี แต่ก็เหมือนเคย คือมีเรื่องที่ต้องแลกเปลี่ยน
      ความเสถียรกับความล้าสมัยนั้นต่างกัน และความต่างนั้นมักตัดสินกันที่ความปลอดภัย
      ถ้าการตอบสนองความต้องการใหม่ที่สำคัญทำได้ง่าย แต่ต้องอัปเกรดไลบรารีที่ vendored ไว้ขึ้นไป 7 major versions และผลคือเกิดการพังในส่วนที่ไม่เกี่ยวข้องเต็มไปหมด จะทำอย่างไร
      ถ้าคนที่คุ้นกับชุดเครื่องมือที่เวลาหยุดนิ่งมีจำนวนไม่พออีกต่อไป และไม่มีใครอยากเรียนรู้ จะทำอย่างไร
      การเลือก dependency อย่างรอบคอบและอนุรักษนิยมเป็นเรื่องดี แต่การไม่ตามให้ทันแม้กระทั่งการเปลี่ยนแปลงเล็ก ๆ ของ dependency ที่คัดให้เหลือน้อยแบบนั้น ผมว่าก็เกินไปก้าวหนึ่ง
  • ผมเห็นด้วยกับอารมณ์ของบทความ
    ผมเกลียดจริง ๆ ที่แม้แต่แอปมือถือที่ทำไว้เมื่อไม่กี่ปีก่อน ตอนนี้ถ้าจะ patch แล้วส่งอัปเดต ก็ต้องใช้เวลา หลายสิบชั่วโมง
    ช่วงท้ายที่ผู้เขียนเรียก static site generator ของตัวเองว่าเป็นซอฟต์แวร์เลือดเย็น และบอกว่ามันรันบน Python 2 ได้ ก็น่าสนใจ
    ทุกวันนี้ Python 2 ติดตั้งยากขึ้นเรื่อย ๆ และสุดท้ายโปรเจกต์นั้นก็คงกลายเป็นโปรเจกต์เลือดอุ่น

    • ผมมีโปรเจกต์งานอดิเรกเล็ก ๆ (iOS และ macOS) ที่ไม่ได้พัฒนาเป็นประจำ แต่ใช้บ่อยในฐานะผู้ใช้ และคอยรักษาให้คอมไพล์และรันบน OS ล่าสุดได้
      ทุกครั้งที่อัปเกรด Xcode จะต้องแก้จุกจิกหลายอย่างเพื่อให้โปรเจกต์คอมไพล์ได้สะอาดและทำงานได้ ซึ่งน่าหงุดหงิดมาก และจริง ๆ ควรเป็นเรื่องที่ยอมรับไม่ได้เลย
      ข้อความในประวัติ git ช่วงหลังทั้งหมดเป็นรูปแบบต่าง ๆ ของ “แก้ให้ทำงานบน Xcode รุ่นล่าสุด”
      ถ้าการเปลี่ยนแปลง SDK หรือ OS ระดับล่างพวกนี้จำเป็นเพราะภัยคุกคามด้านความปลอดภัย ก็พอเข้าใจได้บ้าง แต่แทบไม่ใช่กรณีนั้นเลย
      ส่วนใหญ่เป็นการเปลี่ยนแปลงโง่ ๆ แบบเลิกใช้ API, เพิ่ม warning ค่าเริ่มต้น, หรือบอกว่าตอนนี้ให้ใช้ framework นี้แทน framework นั้น
      แพลตฟอร์มและ framework ควรเลิกตั้งใจทำตัวเป็นเป้าที่เคลื่อนอยู่ตลอด โดยเฉพาะเมื่อมันควรเป็นระบบปฏิบัติการที่เสถียรและเชื่อถือได้มากแล้ว
      โปรเจกต์อายุ 10 ปี ควรหยิบออกมาจากช่องแช่แข็งแล้วคอมไพล์ได้สะอาดและรันได้เหมือนเมื่อ 10 ปีก่อน
      ผู้ขายระบบปฏิบัติการพวกนี้เป็นบริษัทระดับล้านล้านดอลลาร์ ดังนั้นผมไม่อยากฟังข้ออ้างว่าการรักษา backward compatibility ต้องใช้แรงวิศวกรรมมาก
  • ผมยังดูแล side project ส่วนตัวต่อไป
    เริ่มจาก PHP ล้วน ๆ เมื่อ 12–13 ปีก่อน ต่อมาเขียนใหม่ด้วย Laravel แล้วราวปี 2017 ก็เขียนใหม่อีกครั้งด้วย Symfony
    มีช่วงที่ทำงานฟรีแลนซ์เต็มเวลาจนไม่มีพลัง เหลือแค่ commit เล็ก ๆ 2–3 ครั้งในช่วง 6–18 เดือน แต่พอมีเวลาก็เพิ่มฟีเจอร์ อัปเกรด ทดลอง และเรียนรู้
    มันมีประโยชน์มากในการเรียนรู้วิธีดูแลโปรเจกต์ระยะยาว
    ผมได้เรียนรู้เรื่องการอัปเดต dependency, การลบสิ่งที่ไม่จำเป็น, การตรวจสอบอัปเดตความปลอดภัย, การมองหาโอกาสทำให้เรียบง่ายขึ้น (จาก Vagrant ไป Docker, จาก Vue + Axios + Webpack ฯลฯ ไป Htmx) และได้เรียนรู้สิ่งที่ควรหลีกเลี่ยงด้วย
    โดยส่วนตัวแล้วผมเริ่มหลีกเลี่ยง dependency ที่เพิ่งพัฒนาขึ้นใหม่, microservices และ infrastructure ซับซ้อนอย่าง Kubernetes
    ช่วงหลังผมทำฟีเจอร์หลายอย่างและอัปขึ้นเป็น PHP 8.2 กับ Symfony 7 รวมถึงรวมฟีเจอร์ที่ใช้ ChatGPT เข้าไปแล้ว ดังนั้นถ้าต้องการก็น่าจะพักได้สัก 1–3 ปี
    ในช่วง 4–5 ปีที่ผ่านมา โปรเจกต์นี้ทำรายได้ใกล้เคียงกับรายได้หนึ่งปีของฟรีแลนซ์ทั่วไป จึงไม่ใช่ side project นิรนามที่หลับใหลอยู่

    • PHP เวลากลับไปใช้อีกครั้งจะรู้สึกแย่มาก แต่ผมมองว่ามันเป็นตัวอย่างของการรักษา backward compatibility อย่างแท้จริง ถึงขั้นยอมเสียประโยชน์ของตัวเอง
      พอกลับไปหลังไม่ได้ใช้มาหลายปี ก็พบว่าฟังก์ชันจัดการรูปภาพอันเลวร้ายเหมือนเดิมเมื่อ 8 ปีก่อนตอนที่จากมา ยังอยู่ครบ
    • ผมกำลังคิดว่าจะลองเรียน Symfony ให้เป็นแบบ native มากขึ้น เลยสงสัยว่าเหตุผลที่ย้ายจากอะไรอย่าง Laravel ไปเป็น Symfony คืออะไร
  • นอกจากสิ่งที่บทความพูดถึงแล้ว threat model ที่ปลอดภัยโดยเนื้อแท้ก็สำคัญ
    เช่น เว็บไซต์ทั้งเว็บต้องรับมือกับผู้โจมตีและ spam bot อยู่ตลอด จึงโดยเนื้อแท้แล้วใกล้เคียงกับเลือดอุ่น
    ในทางกลับกัน static page อย่าง TiddlyWiki อาจไม่ต้องเอาขึ้นเว็บเลยก็ได้ และ browser ก็เป็นแพลตฟอร์มที่เสถียรมหาศาล จึงดีกว่ามาก

  • ความแตกต่างของความชอบระหว่างโปรเจกต์เลือดเย็นกับโปรเจกต์เลือดอุ่น น่าจะเกี่ยวข้องกับ Buxton Index ที่กล่าวถึงใน https://www.cs.utexas.edu/users/EWD/transcriptions/EWD11xx/EWD1175.html

    • ลองอ่านบทความที่ลิงก์ไว้แล้ว Buxton Index คือช่วงเวลาที่ตัว主体 เช่น คนหรือองค์กร วางแผนเป็นหน่วยกี่ปี
      ร้านขายของชำเล็ก ๆ ในละแวกบ้านอยู่ที่ประมาณ 0.5 ปี, คริสเตียนที่แท้จริงคืออนันต์, นักการเมืองทั่วไปที่หวังได้รับเลือกตั้งอีกครั้งอยู่ที่ประมาณ 4 ปี, อุตสาหกรรมส่วนใหญ่ยาวกว่านั้นเล็กน้อย และผู้จัดการที่ต้องเขียนรายงานรายไตรมาสก็สั้นกว่านั้นมาก ประมาณนี้
      เหตุผลที่ Buxton Index สำคัญคือ ความร่วมมืออย่างใกล้ชิดระหว่างตัว主体ที่มี Buxton Index ต่างกันมาก ๆ ย่อมล้มเหลวและนำไปสู่การตำหนิทางศีลธรรม
      ฝั่งที่สั้นกว่าจะถูกตำหนิว่าฉาบฉวยและสายตาสั้น ส่วนฝั่งที่ยาวกว่าจะถูกตำหนิว่าละเลยหน้าที่ หลบเลี่ยงความรับผิดชอบ หรือเอาเปรียบโดยไม่ลงแรง
      ต่างฝ่ายต่างก็จะมองอีกฝ่ายว่าโง่ด้วย
      ข้อดีของ Buxton Index คือเป็นแนวคิดตัวเลขที่เรียบง่าย จึงเป็นกลางทางศีลธรรม และยกระดับความแตกต่างให้พ้นจากการถกเถียงเชิงศีลธรรม
      สำคัญเป็นพิเศษเมื่อนึกถึงความร่วมมือระหว่างแวดวงวิชาการกับภาคอุตสาหกรรม
    • ฟังดูคล้ายกับ time preference มาก: https://en.wikipedia.org/wiki/Time_preference
  • ชื่อนี้แย่จริง ๆ
    สัตว์เลือดเย็น พึ่งพาสภาพแวดล้อมอย่างมาก ส่วนสัตว์เลือดอุ่นใช้เมตาบอลิซึมเพื่อลดการพึ่งพาอุณหภูมิภายนอก
    ไม่ว่าอย่างไรก็คลุมเครือโดยไม่จำเป็น
    ถ้าเรียกตรง ๆ ว่า “ซอฟต์แวร์ที่ไม่มีการพึ่งพาภายนอก” ก็จะตัดย่อหน้าอธิบายยืดยาวออกไปได้

    • นี่เป็นคำตอบเดียวที่ชี้เข้าประเด็นหลักของบทความได้ถูกต้อง แต่แน่นอนว่าที่นี่ไม่มีใครโหวตแนะนำเลย
      ผมไม่ชอบบทความพัฒนาซอฟต์แวร์ที่หยิบอุปมาไม่เหมาะสมจากธรรมชาติมาแล้วกระโดดไปสู่ข้อสรุปตื้น ๆ อยู่แล้ว แต่ยิ่งไม่ชอบบทความที่ทำแบบนั้นทั้งที่เข้าใจปรากฏการณ์ธรรมชาติซึ่งเป็นต้นแบบของอุปมานั้นผิดไปหมด
      การที่บางสปีชีส์รวมถึงเต่าทาสีสามารถรอดชีวิตได้แม้ตัวแข็ง ไม่ใช่เพราะเป็นสัตว์เลือดเย็น แต่เป็นเพราะมี โปรตีนต้านการแข็งตัว แบบพิเศษ
      กิ้งก่าหรือสัตว์เลือดเย็นชนิดอื่น ๆ จะมีเนื้อเยื่อของตัวเองฉีกขาดเมื่อถูกละลายน้ำแข็ง
    • ก็มีคนที่อ่านการตีความทางชีววิทยาอย่างใจดีกว่านั้นอยู่เหมือนกัน
      https://lobste.rs/s/hitos3/cold_blooded_software#c_mxjzwh