ประกาศเตือนเรื่อง Makefile effect
(blog.yossarian.net)- Makefile effect หมายถึงแพตเทิร์นที่ไม่เริ่มใช้เครื่องมือที่ซับซ้อนหรือไม่คุ้นเคยตั้งแต่ศูนย์ แต่คัดลอกการตั้งค่าที่เคยทำงานได้ดีมาก่อน แล้วปรับแก้ให้เข้ากับสถานการณ์ใหม่
- ในขั้นตอนแก้ปัญหา อาจเป็น การตอบสนองทางวิศวกรรมที่สมเหตุสมผล เพราะนำตัวอย่างที่ผ่านการพิสูจน์แล้วมาใช้ซ้ำ จึงลดความเสี่ยงของบั๊กได้
- แต่ในมุมมองด้านการออกแบบ อาจมองได้ว่าเป็น สัญญาณด้าน usability ว่าเครื่องมือนั้นใช้ตั้งแต่ต้นได้ยากหรือน่ารำคาญ จนการเปลี่ยนแปลงถูกสะสมต่อเนื่องอยู่ในสำเนา
- ไม่ใช่แค่ Make เท่านั้น แต่ยังพบโฟลว์คัดลอกแล้วแก้คล้ายกันใน GitHub Actions, GitLab CI/CD, การตั้งค่า linter/formatter และระบบ build ด้วย
- ผู้ออกแบบเครื่องมือควรตรวจสอบว่าจำเป็นต้องมีการตั้งค่าและไวยากรณ์เฉพาะของตัวเองจริงหรือไม่ สามารถนำ convention ของ CLI ที่คุ้นเคยมาใช้ซ้ำได้หรือไม่ และผู้ใช้กำลังถูกทำให้ต้องพึ่งพาการคัดลอก-วางหรือไม่
Makefile effect คืออะไร
- Makefile effect คือปรากฏการณ์ที่ไม่เขียนเครื่องมือซึ่งมีความซับซ้อนหรือความไม่คุ้นเคยในระดับหนึ่งขึ้นมาใหม่ แต่คัดลอกและแก้ไขตัวอย่างเดิมที่ “เคยทำงานได้ดี”
- โฟลว์โดยทั่วไปเป็นดังนี้
- ต้องทำงานรูปแบบที่พบบ่อยให้เสร็จ และเคยมีการทำงานที่คล้ายกันมากหรือเหมือนกันมาแล้วก่อนหน้านี้
- Make หรือเครื่องมือที่คล้ายกันถูกเลือกให้เป็นเครื่องมือที่เหมาะกับงานนั้น
- แทนที่วิศวกรจะเขียน
Makefileใหม่ จะคัดลอกMakefileจากงานก่อนหน้า แล้วแก้จนกว่าจะทำงานได้ในสภาพแวดล้อมใหม่
- Make เป็นเพียงตัวอย่างที่เด่นชัดเท่านั้น และเพราะแต่ละทีมหรือแต่ละคนถนัดเครื่องมือไม่เหมือนกัน กลุ่มเครื่องมือบางประเภทจึงเปราะบางต่อ effect นี้มากกว่า ขณะที่บางกลุ่มค่อนข้างเปราะบางน้อยกว่า
- มุกเรื่อง “heritage
Makefile” หมายถึงสถานการณ์ที่Makefileที่สืบทอดมาจากวิศวกรอาวุโส อาจารย์ ฯลฯ ถูกส่งต่อไปเรื่อย ๆ โดยมีแค่การแก้ไขเล็กน้อย
ปัญหาการออกแบบเครื่องมือที่การคัดลอกซ้ำ ๆ เผยให้เห็น
- ในจังหวะที่ต้องแก้ปัญหาเฉพาะหน้า การใช้ตัวอย่างที่ทำงานได้ซ้ำ อาจเป็นทางเลือกที่กระชับ
- เพราะงานส่วนใหญ่ไม่เปลี่ยน ในทางทฤษฎีจึงอาจสร้างบั๊กใหม่น้อยลง
- Makefile effect ไม่ใช่ปรากฏการณ์ที่เลวร้ายหรือไร้ประสิทธิภาพโดยเนื้อแท้
- แต่ในช่วงออกแบบ มันกลายเป็นสัญญาณว่าเครื่องมือหรือวิธีนำเครื่องมือไปใช้นั้นซับซ้อนหรือยุ่งยากเกินไป
- ผู้ใช้คัดลอกวิธีแก้เดิมซ้ำ ๆ แทนที่จะแก้ปัญหาตั้งแต่ต้น
- หาก effect นี้เกิดในกรณีใช้งานที่เรียบง่าย เครื่องมือนั้นอาจซับซ้อนเกินไปสำหรับวัตถุประสงค์นั้น
- แพตเทิร์นนี้ปรากฏในเครื่องมือที่เน้นการตั้งค่าอื่นนอกเหนือจาก Make ด้วย
- ใน การตั้งค่า CI/CD มักคัดลอก YAML ของ GitHub Actions หรือ GitLab CI/CD จากการตั้งค่าล่าสุดที่ทำงานได้ แล้วรันซ้ำเพื่อแก้ไปเรื่อย ๆ
- การตั้งค่า linter และ formatter มักคัดลอกชุดกฎพื้นฐานข้ามโปรเจกต์ แล้วปรับให้เข้มหรือผ่อนตามเงื่อนไขในเครื่อง
- คอนฟิกของระบบ build ที่ไม่ใช่เรื่องเล็กน้อย มักค่อย ๆ คล้ายกับระบบ build เดิม
ต้นทุนที่เกิดกับการเรียนรู้ การดีบัก และความปลอดภัย
- เครื่องมือที่มี Makefile effect รุนแรงอาจขัดขวางการเรียนรู้ในวงกว้าง
- ผู้เชี่ยวชาญส่วนน้อยตั้งค่าเครื่องมือ ส่วนคนอื่นคัดลอกและแก้โดยเข้าใจเฉพาะส่วนที่จำเป็น
- กราฟ dependency ของระบบ build เป็นความซับซ้อนโดยเนื้อแท้ แต่การต้องจำความแตกต่างระหว่าง
$<กับ$^ใน Make ไม่ใช่เช่นนั้น
- หาก การสนับสนุนด้านการวินิจฉัยและการดีบัก ไม่เพียงพอ ผู้ใช้จะต้องพึ่งพาวิธีรันเครื่องมือหลายครั้งเพื่อเก็บเบาะแสเล็ก ๆ น้อย ๆ
- ใน CI/CD จะกลายเป็นการดีบักแบบ print ผ่านชั้น network และ orchestration ของ VM ระหว่างทาง
- ในแง่ความปลอดภัย ความเสี่ยงก็อาจเพิ่มขึ้นได้
- มาตรการความปลอดภัยมักต้องเข้าใจอย่างลึกซึ้งว่า “ทำไม” พฤติกรรมนั้นจึงเกิดขึ้น
- ระบบที่มี Makefile effect มักยอมให้เกิดความสับสนระหว่างโค้ดกับข้อมูล หรือความสับสนของ in-band signaling โดยทั่วไป
- template injection ของ GitHub Actions เป็นตัวอย่างที่เด่นชัด
คำถามที่ผู้ออกแบบเครื่องมือควรตรวจสอบ
- เมื่อสร้างเครื่องมือใหม่ ควรดูว่าผู้ใช้สามารถเขียนตั้งแต่ต้นได้หรือไม่ หรือจำเป็นต้องหา “ตัวอย่างที่ทำงานได้” มาคัดลอกเท่านั้น
- คำถามสำหรับตรวจสอบมีดังนี้
- ความสามารถในการตั้งค่าจำเป็นจริงหรือไม่
- จำเป็นต้องมีไวยากรณ์เฉพาะของตัวเองจริงหรือไม่
- สามารถนำไวยากรณ์และ convention ที่คุ้นเคยจากเครื่องมืออื่นหรือ CLI มาใช้ซ้ำได้หรือไม่
- แม้แต่ผู้สร้างเองก็ต้องคัดลอก-วางวิธีใช้หรือไม่ หากใช่ ผู้ใช้อื่นก็มีแนวโน้มจะทำเช่นเดียวกันหรือไม่
- สำหรับคำว่า “cargo cult” บทความของ Ken Shirriff กล่าวถึงปัญหาว่าเป็นศัพท์เฉพาะทางที่ไม่เหมาะสม
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
“ระบบซับซ้อนที่ใช้งานได้ มักพบว่าวิวัฒน์มาจากระบบเรียบง่ายที่เคยใช้งานได้เสมอ ระบบซับซ้อนที่ออกแบบขึ้นตั้งแต่แรกจะไม่มีวันใช้งานได้ และไม่สามารถซ่อมให้ใช้งานได้ด้วย ต้องเริ่มใหม่จากระบบเรียบง่ายที่ใช้งานได้”
— John Gall (1975), Systemantics: How Systems Really Work and How They Fail
https://en.wikipedia.org/wiki/John_Gall_(author)#Gall's_law
โดยปกติจะเริ่มมีฟีเจอร์เหมือนรอยปะเพื่อจัดการกรณีพิเศษ และนั่นดูเหมือนเป็นสัญญาณว่าจะมีข้อยกเว้นแบบนั้นเพิ่มขึ้นไม่รู้จบในอนาคต
มีความแตกต่างที่ละเอียดแต่สำคัญระหว่าง “การนำประสบการณ์มาใช้” กับ “การออกแบบระบบใหม่ทั้งหมดโดยมีสิ่งหนึ่งที่ทำให้ตัวเองหงุดหงิดเป็นศูนย์กลาง”
มีสมมติฐานอีกอย่างหนึ่ง นักพัฒนาประมาณ 10% เข้าใจหลักการจริง ๆ จึงสามารถเริ่มอะไรบางอย่างจากศูนย์ได้ และอีก 40% สามารถคัดลอก·วางจากโค้ดในเครื่อง, Stack Overflow, GitHub, LLM พร้อมพอรู้คร่าว ๆ ว่าเกิดอะไรขึ้นและจัดการงานประจำวันได้
ส่วนอีก 50% ที่เหลือไม่ค่อยรู้อะไรเกินจากโจทย์ LeetCode ไม่กี่ข้อ และยังขาดความเข้าใจจริง ๆ ในสิ่งที่ตัวเองคัดลอก·วางอยู่
ถ้าการกระจายเป็นแบบนี้ Makefile มากกว่าครึ่งก็น่าจะเป็นชิ้นส่วนคัดลอก·วางที่พอใช้งานได้แบบลวก ๆ ถ้าเอามาจากที่ที่เคยใช้งานได้อยู่แล้ว งานก็เสร็จไปก่อน แล้วก็ย้ายไปตั๋วถัดไป
ไม่ได้โทษตัวเครื่องมือเอง Makefile เป็นที่รู้จักดี และในโปรเจ็กต์เล็ก ๆ ก็ไม่ได้เยิ่นเย้อ อาจเป็นตัวเลือกที่แย่สำหรับสัตว์ประหลาดขนาด 10,000 ไฟล์ แต่ก็เคยเห็น Makefile ที่เรียบร้อยในโปรเจ็กต์ขนาดใหญ่มาแล้ว โดยส่วนตัวคงไม่ใช่ตัวเลือกแรก แต่ก็ชอบ Makefile และใช้เป็นครั้งคราวมานานกว่า 30 ปีแล้ว
ตัวอย่างเช่น เมื่อโปรแกรมเมอร์ C++ จัดการตั๋วหนึ่งใบ เขาต้องรู้ลึกลงไปกี่ชั้นของสแตก? ต้องรู้ชื่อรีจิสเตอร์ของ CPU ด้วยไหม? นักวิจัย AI ที่ใช้แต่ Jupyter ต้องรู้อะไรถึงระดับไหน? การเรียนรู้เครื่องมือและสแตกให้มากที่สุดเป็นสิ่งที่ควรทำ แต่เวลาก็มีจำกัด
ถ้าเห็นกฎแยกสำหรับสร้างไฟล์เดี่ยว ๆ หนึ่งไฟล์ใน Makefile ผมมองว่าแทบจะเป็นความผิดพลาดเสมอ
โดยทั่วไปควรมีเพียง กฎการบิลด์ทั่วไป ที่ใช้บิลด์ไฟล์ทั้งหมดของประเภทหนึ่ง ๆ เท่านั้น
ใน Makefile แทบไม่ควรมีรายชื่อไฟล์ซอร์สหรือรายชื่อ dependency เลย ควรมีแค่รายชื่อไดเรกทอรีที่มีไฟล์ซอร์สอยู่ก็พอ
Make ควรค้นหาไดเรกทอรีซอร์สเพื่อหาไฟล์ จัดประเภทตามชนิด สร้างรายชื่อ dependency และเรียกกฎการบิลด์ที่เหมาะสม อย่างน้อยใน GNU make ก็ทำได้ง่ายมากและมีอยู่ในคู่มือผู้ใช้ด้วย
ถ้าเขียนแบบนี้ ไม่ว่าโปรเจ็กต์จะมีไฟล์ 1 ไฟล์หรือ 10,000 ไฟล์ ความพยายามในการสร้างหรือแก้ Makefile ก็เล็กน้อยไม่ต่างกัน และไม่ต้องอัปเดต Makefile ทุกครั้งที่ไฟล์ซอร์สถูกสร้าง เปลี่ยนชื่อ ย้าย หรือลบ
เพราะแต่ละภาษาและเฟรมเวิร์กมี “ลำดับ” การบิลด์ต่างกันจนจำไม่ไหว แต่
$ makeนั้นง่ายเห็นด้วยว่านักพัฒนาแอปที่ทำงานได้มีประสิทธิภาพจำนวนมากอาจตั้งค่าโปรเจ็กต์ใหม่จากศูนย์ไม่ได้ แต่บ่อยครั้งนั่นเป็นปัญหาเรื่องการรู้กฎเวทมนตร์และคาถาที่ถูกต้องเพื่อทำให้เครื่องมือต่าง ๆ ทำงานเข้ากัน มากกว่าจะเป็นเรื่องความเข้าใจเชิงลึก
อีกปัจจัยหนึ่งที่เห็นในที่ทำงานคือ เครื่องมือและระบบ ที่นักพัฒนาสักวันหนึ่งก็ต้องจัดการ แต่โดยปกติแล้วรู้สึกว่ามีคุณค่าต่ำที่จะเรียนรู้
ตัวอย่างเช่น ระบบ build และการตั้งค่า CI เป็นสิ่งจำเป็นแน่นอน แต่นักพัฒนาไม่ได้คิดว่าต้องจัดการมันทุกวัน CI ถูกมองเหมือนระบบแบบ “ตั้งค่าแล้วลืมไปได้เลย” และมักมีปฏิกิริยาว่า เพื่อจะ build แอป จำเป็นต้องเรียนรู้ทั้งหมดนี้ด้วยหรือ
นักพัฒนาคาดหวังให้มัน “แค่ทำงานได้” และถ้ามีความซับซ้อน ก็เห็นว่าเป็นเรื่องที่ทีมอื่น หรือคนที่มีบทบาทแบบผม จะจัดการให้ ดังนั้นทุกครั้งที่นักพัฒนาต้องแตะระบบนี้ แรงจูงใจจึงมักเป็นการคัดลอกการตั้งค่าที่ทำงานครั้งล่าสุดส่งต่อไป แล้วกลับไปทำงาน “จริง”
ทางออกที่ดีที่สุดคือพบกันครึ่งทางกับนักพัฒนา จัดหาเครื่องมือที่เรียบง่ายหรือซับซ้อนให้เหมาะกับงาน มีเอกสารประกอบ และลดความเชื่อว่ามันเป็น “เวทมนตร์” ให้น้อยที่สุด เครื่องมืออย่าง Make มักล้มเหลวในจุดนี้ เพราะรู้สึกซับซ้อนเกินไปและเหมือนกล่องดำ
ดังนั้นวงจร feedback จึงยาวมาก และตัวงานเองก็กลายเป็นความเจ็บปวดอย่างหนัก
ถ้านักพัฒนาสามารถรันงาน “CI/CD” บางส่วนหรือทั้งหมดในเครื่อง local ได้ ก็จะมองเห็น ควบคุม และเข้าใจได้ แทนที่จะรู้สึกว่า “CI/CD เป็นเรื่องคนละโลก” วิธีนี้ช่วยอย่างมากในการสร้างความรู้สึกเป็นเจ้าของและความมั่นใจ
แต่ไม่ได้ใช้ได้เสมอไป เคยมีทีมสองคนที่แม้จะมี CI/CD แบบ wrapper บาง ๆ ก็ยังทำเหมือนเป็นกระบวนการจากต่างดาวและปฏิเสธที่จะแตะต้อง
เช่นเดียวกัน CI ต้องมีความเป็นเจ้าของ และวิธีที่ความรับผิดชอบตกไปอยู่กับนักพัฒนาคนสุดท้ายที่พยายามใช้ CI นั้นไม่ใช่วิธีทำงานที่มีประสิทธิภาพ
ทางออกคืออย่านำเครื่องมือที่บริษัทใหญ่ใช้มาใช้เพียงเพราะบริษัทใหญ่ใช้ ความเห็นนี้อาจไม่เป็นที่นิยม แต่ผมมองว่า CI/CD ไม่จำเป็นในสถานที่ส่วนใหญ่ที่มีการใช้งานมัน
แม้ขั้นตอนจะเพิ่มขึ้นเล็กน้อย ก็ควรหาวิธี build และ deploy โดยลดชิ้นส่วนที่เคลื่อนไหวให้เหลือน้อยที่สุดเท่าที่ทำได้ จากนั้นค่อยพิจารณาต้นทุนอย่างรอบคอบว่าจะ automate ส่วนไหน การนำระบบขนาดใหญ่มาใช้เพื่อทำเรื่องง่าย ๆ โดยทั่วไปไม่คุ้มค่าในระยะยาว
ถ้าจำเป็นต้องมี CI/CD จริง ๆ จะรู้ได้จาก pain point ที่เกิดขึ้น ถ้าระบบนั้นสร้างความเจ็บปวดให้นักพัฒนา ก็ไม่ใช่เครื่องมือที่เหมาะ
makeรู้สึกว่า “ซับซ้อนเกินไปและเหมือนกล่องดำ” แปลว่ายังไม่เคยเห็นcmakeMake และ Makefile นั้นเรียบง่ายมาก หากไม่ได้ถูก autoconf สร้างขึ้นอัตโนมัติ ถ้าเป็นสิ่งที่ autoconf สร้าง ก็ไม่ควรแก้ไข เพราะมันเป็นผลลัพธ์จากการ build ถ้าเป็นไปได้ก็ควรทิ้ง autoconf ไปด้วย
มองให้กว้างขึ้น เอฟเฟกต์นี้มีอยู่จริง เราอาจตกเป็นเหยื่อของมัน หรือใช้ประโยชน์จากมันก็ได้ วิธีใช้ประโยชน์เป็นแบบนี้: เขียนโค้ดเล็กน้อยหรือคัดลอก-วางมาจากที่ไหนสักแห่ง ลองใช้ในโปรเจกต์ ปรับแต่งตามจำเป็น เมื่อเริ่มโปรเจกต์ถัดไป ก็คัดลอกโค้ดนั้นกลับเข้าไปอีกครั้งและแก้ให้เหมาะกับโปรเจกต์ที่สอง ดูว่าสามารถนำการเปลี่ยนแปลงกลับไปใช้กับโปรเจกต์เดิมได้หรือไม่ ถ้าทั้งสองฝั่งทำงานได้และซิงก์กันอยู่ ก็แยกโค้ดนั้นออกมาเป็นไลบรารี บางครั้งต้องผ่านอีกหลายโปรเจกต์กว่าจะขัดเกลาให้เป็นรูปแบบที่สมกับเป็นไลบรารีได้ ในกรณีที่ดีที่สุด ก็เผยแพร่เป็นโอเพนซอร์สให้คนอื่นใช้ด้วย
Git ไม่บันทึก timestamp ไว้ภายใน ดังนั้นแม้เนื้อหาจะเหมือนเดิม mtime ของไฟล์ก็อาจถูกอัปเดตได้ และในทางปฏิบัติก็มักเกิดขึ้นบ่อย ทำให้เกิดการ rebuild ที่ไม่จำเป็น
เครื่องมือสมัยใหม่ที่ input หรือ output เป็นทั้งไดเรกทอรี หรือเครื่องมือที่ไม่รู้ชื่อ output ก่อนรัน ก็จัดการไม่ได้อย่างถูกต้องเลย
ผมชอบ make และใช้งานมาได้ดีแม้จะมีข้อเสีย อีกทั้งรู้ทั้ง workaround และ workaround ของ workaround เหล่านั้นด้วย แต่ทันทีที่เครื่องมือเริ่มไม่ตรงกับสิ่งที่ make คาดหวังอย่างสมบูรณ์ ความยากและความซับซ้อนในการสร้าง Makefile ที่ถูกต้องจะพุ่งสูงขึ้นอย่างรวดเร็ว
ไม่ได้ล้อเล่น การมีอยู่ของแนวคิด
make cleanเองก็เป็นหลักฐานชี้ขาดแล้วMakefile อาจเป็นการเปรียบเทียบที่ไม่ค่อยถูกนัก ปัญหาที่คนส่วนใหญ่เจอกับ Makefile อยู่ที่เขียนมันไม่บ่อยเกินไป พอมีความรู้สึกโดยทั่วไปว่า make ทำอะไร แต่ไม่มี muscle memory สำหรับการเขียนตั้งแต่ต้น
ถึงอย่างนั้น ประเด็นหลักก็ยังถูกต้อง เคยเห็นโค้ดที่คัดลอกมาจากเว็บแล้วมี dead code ที่ไม่ได้ใช้จริงเหลืออยู่เต็มไปหมด แนวปฏิบัติที่ดีคือค่อย ๆ ลบต่อไปจนกว่าจะพัง แล้วคืนกลับเฉพาะส่วนที่พัง ควรลบให้มากที่สุดเท่าที่เป็นไปได้ และต้องลบทุกอย่างที่ไม่ได้ใช้อยู่ในปัจจุบันอย่างแน่นอน
วิธีแก้ที่ใช้มาจนถึงตอนนี้คือสร้าง repository แยกไว้รวบรวมโน้ตและโปรแกรมตัวอย่าง shell script เกี่ยวกับเครื่องมือ ไลบรารี และเฟรมเวิร์กเหล่านี้
ทุกครั้งที่ต้องใช้เครื่องมืออีกครั้ง ก็อ้างอิงโน้ตเพื่อรื้อฟื้นความจำ และถ้ามีสิ่งใหม่ที่ได้เรียนรู้ก็อัปเดตโน้ต ตอนนี้ยังสามารถชี้ LLM ไปที่ repository นั้นแล้วถามคำถามได้ด้วย
repository นั้นเป็นของใช้ส่วนตัวและมีข้อมูลเกี่ยวกับเครื่องมือที่หาได้สาธารณะ ความรู้เฉพาะขององค์กรจะเก็บไว้ใน repository แยกที่คล้ายกัน และเมื่อสัญญากับลูกค้าหรือนายจ้างสิ้นสุดลง ก็จะทำลายทิ้ง
คำที่เรียกสิ่งนี้ได้ดีที่สุดคือ การพัฒนาแบบลัทธิบูชาสินค้า ลัทธิบูชาสินค้าเกิดขึ้นในแปซิฟิกช่วงสงครามโลกครั้งที่สอง เมื่อชาวเกาะพื้นเมืองเห็นเครื่องบินราวปาฏิหาริย์ที่ขนอาหาร เหล้า และเสบียงมาให้ จึงเลียนแบบพฤติกรรมของทหาร และอธิษฐานให้เครื่องบินไม้ไผ่กับอุปกรณ์จากมะพร้าวทำให้เหล่าเทพประทับใจจนเสบียงกลับมาอีก
ปัญหาอยู่ตรงที่ชาวเกาะไม่เข้าใจวิทยาศาสตร์ของสิ่งอย่างเครื่องบิน วิทยุสื่อสาร หรือปืน
ในทำนองเดียวกัน นักพัฒนาแบบลัทธิบูชาสินค้ามองเห็นผลลัพธ์ที่เป็นไปได้ แต่ไม่เข้าใจหลักการปฐมฐาน จึงเลียนแบบพฤติกรรมของมหาปุโรหิตแห่งเทคโนโลยี หวังว่าจะคัดลอกความสำเร็จได้
ดังนั้นจึงกลายเป็นการทำซ้ำ ๆ ทั้งคัดลอก วาง ทดลอง ลูบคลำ ค้นหา ดึง ผลัก ปรับแต่ง แล้วหวังว่าคราวนี้มันจะพอทำงานได้กับข้อมูลเฉพาะในเย็นวันอังคาร
ในความเป็นจริงคือการทำซ้ำการกระทำหรือพิธีกรรมที่ตัวมันเองไม่ได้ผล แล้วหวังผลลัพธ์แบบเดียวกับที่บริษัทอื่นซึ่งทำพิธีกรรมเดียวกันเคยได้
สิ่งที่บทความต้นฉบับพูดถึงต่างออกไป สิ่งที่ถูกคัดลอกคือ โค้ด ซึ่งตัวมันเองมีผลลัพธ์อยู่แล้ว สามารถทดสอบและตัดสินได้ว่าทำงานหรือไม่
ความแตกต่างนี้สำคัญ เพราะอาการของ Makefile effect ในบทความต้นฉบับกับลัทธิบูชาสินค้าไม่เหมือนกัน ถ้าปฏิบัติต่อทั้งสองอย่างราวกับเป็นสิ่งเดียวกันก็จะวินิจฉัยได้ยาก ในลัทธิบูชาสินค้า คนเสียเวลาไปกับสิ่งที่จริง ๆ แล้วไม่ทำงานเพราะความเชื่องมงาย ส่วนใน Makefile effect สิ่งนั้นพิสูจน์แล้วว่าทำงานได้ แต่ชิ้นส่วนคล้ายอวัยวะหลงเหลือถูกคัดลอกต่อ ๆ กันไป ทำให้บำรุงรักษายากขึ้นเรื่อย ๆ
พูดให้ชัดคือ ผมไม่ได้คิดว่าสิ่งนี้เหมือนกับลัทธิบูชาสินค้าพอดี ลัทธิบูชาสินค้ามีนัยของการขาดความเข้าใจ แต่ถึงจะเข้าใจระบบใดระบบหนึ่งดี ก็ยังอาจใช้ชีวิตแบบคัดลอก·วางเป็นหลักได้ เพราะความซับซ้อนภายในของระบบนั้นชักนำให้ทำแบบนั้น นั่นคือประเด็นหลักของบทความ
โดยปกติผมเข้าใจว่าเกิดอะไรขึ้นภายใน แต่การเปลี่ยนไปอยู่ใน “โหมด” นั้นเองหนักทางปัญญา จึงหลีกเลี่ยงเท่าที่ทำได้
เครื่องมือที่เข้าข่ายนี้มักเป็นเครื่องมือฝั่งปฏิบัติการอย่าง CI/CD, k8s, Docker ซึ่งมีความซับซ้อนมหาศาล แต่ไม่ใช่ปัญหาหลักที่ผมกำลังแก้ Make โดยเฉพาะเลี่ยงการสลับบริบทได้ยาก ช่วงนี้เลยมักจะหลีกเลี่ยงไปเลย
ไม่เกี่ยวกับคาถาวิเศษ และผมรู้ว่าตัวเองกำลังแลกอะไรอยู่ ถึงอย่างนั้นก็ยังมีความเสี่ยงที่จะกลายเป็น Makefile effect
“Makefile effect คล้ายกับปรากฏการณ์อื่น ๆ เช่น ลัทธิบูชาสินค้า การทำให้ความเบี่ยงเบนเป็นเรื่องปกติ และ ‘ภาษาแบบเขียนอย่างเดียว’ ในบทความนี้ ผมจะโต้แย้งว่าสิ่งนี้ไม่ได้ไร้ประสิทธิภาพหรือเลวร้ายโดยเนื้อแท้ และต่างจากแต่ละอย่างเล็กน้อยตรงที่ว่ามันว่าด้วยผลของการออกแบบบางแบบ”
ถ้าอย่างนั้น หากใครสักคนอยาก “หนีออกจากเกาะแล้วขับเครื่องบิน” ได้จริง ๆ สุดท้ายมหาวิทยาลัยคือเส้นทางที่ใกล้เคียง “ของจริง” ที่สุดหรือเปล่า?
ผมรู้ว่าพูดแบบสัมบูรณ์ได้ยาก มีข้อยกเว้นมากมาย และแต่ละคนก็มีข้ออ้างหลากหลายว่า “ฉันทำแบบนั้นไม่ได้” ตรงนี้ผมถามจากมุมมองทฤษฎีเชิงเหมาะที่สุด เส้นทางที่ดีที่สุดในการหลีกเลี่ยงพฤติกรรมแบบบูชาคือการเข้าใจทั้งหมด และ “ทั้งหมด” นั้นไม่ได้มาจากการศึกษาระดับสูงหรือ?
ในเชิงตรรกะ ต่อให้ตั้งใจเรียนจากหนังสืออย่างจริงจังเพื่อไล่ตามหรือแซง “ความครบถ้วน” ของมหาวิทยาลัย ก็ดูเหมือนว่าจะยังขาดบางแง่เมื่อเทียบกับเวลาที่ใช้กับผู้สอนที่มีความรู้และในห้องเรียน แม้จะสมมติว่าคนคนเดียวกันทุ่มเทเท่ากันทั้งสองทางก็ตาม
อีกปัจจัยหนึ่งคือ ความถี่ในการใช้งาน การเขียนงานชิ้นใหญ่ด้วย LaTeX เกิดขึ้นปีละครั้งหรือน้อยกว่านั้น
LaTeX ในระดับที่ผมใช้ไม่ใช่เครื่องมือที่ยาก แต่ถ้าใช้แค่ไม่กี่สัปดาห์ต่อปี ก็ไม่มีทางจำรายละเอียดวิธีใช้ได้เลย จึงมักคัดลอก·วางเอกสารเก่ามาเริ่มเอกสารใหม่
แต่แม้ผมจะเคยใช้ LaTeX เยอะมากในอดีต ผมก็ยังคัดลอก·วางจากเทมเพลตเสมอ งานนำเสนอ beamer กับภาพ TikZ ยิ่งหนักกว่า คือคัดลอกจากงานนำเสนอหรือภาพก่อนหน้า ไม่ใช่จากเทมเพลต
สำหรับ TikZ ผมมองว่าเครื่องมือตัวมันเองซับซ้อนโดยเนื้อแท้ และผมไม่ได้เรียนรู้มันมากพอ
ส่วน LaTeX ผมคิดว่าตัวเองเรียนรู้มาพอแล้ว ดังนั้นอาจมีเหตุผลอื่น
ผมคิดว่าอาจเป็นปัญหาเรื่อง ค่าเริ่มต้นที่ปกติ เครื่องมือที่ดีควรให้ค่าเริ่มต้นที่ดี แต่ LaTeX ไม่ใช่เครื่องมือที่ดีตามเกณฑ์นี้ เอกสารของผมมักเริ่มประมาณนี้ ส่วนใหญ่มีไว้เพื่อให้ได้การรองรับพื้นฐานสำหรับอักขระที่ไม่ใช่ ASCII ซึ่งจำเป็นต่อภาษาแม่ของผม หรือเพื่อเปิดค่าเริ่มต้นปกติอย่างกระดาษ A4, microtype หากเป็นเครื่องมือสมัยใหม่อย่าง pandoc/Markdown ก็อาจไม่จำเป็น
ดังนั้นเป้าหมายของการคัดลอก·วางสิ่งเหล่านี้มักเป็นการได้ค่าเริ่มต้นที่ดี ซึ่งเครื่องมือที่ดีกว่าน่าจะให้มาตั้งแต่แรก
ถ้าไม่ได้เขียนคณิตศาสตร์หรือไม่ได้เขียนข้อความล้วนภาษาอังกฤษ LaTeX ก็ใกล้เคียงกับ “ไม่รวมแบตเตอรี่”
นี่คือ การพัฒนาแบบขับเคลื่อนด้วยการคัดลอกวาง [0] และไม่ใช่เรื่องที่เกี่ยวกับ Makefile เท่านั้น แต่เกี่ยวกับการที่ทั้งวงการคัดลอกโค้ดไปทั่วโดยไม่รู้ด้วยซ้ำว่าตัวเองกำลังคัดลอกอะไรอยู่
พูดตรง ๆ ผมคิดว่า Copilot ทำให้เรื่องนี้แย่ลง เพราะเรากำลังยอมรับก้อนโค้ดเข้ามาใน codebase แบบหลับหูหลับตา
[0] https://andrew.grahamyooll.com/blog/copy-pasta-driven-develo...
makeแต่ไม่เคยได้รับเวลาให้ทำงานกับมันทุกวันจนจำได้หมดจริง ๆถึงจุดหนึ่งก็แค่ยอมแพ้ไป มันกลายเป็นความรู้ลึกลับแบบนอกกระแสที่สร้าง muscle memory ไม่ได้ และต้องเรียนใหม่ตั้งแต่ต้นทุกครั้งที่จำเป็นต้องใช้ ผมเลยย้ายไปใช้เครื่องมือที่ง่ายกว่า
การสูญเสีย deep work ไม่ใช่ความผิดของโปรแกรมเมอร์ที่ดี แต่เป็นความผิดของคนฝั่งธุรกิจ
ผมเห็นมามากเกินไปแล้วกับการคัดลอก Makefile จากโปรเจกต์อื่นมาเหมือนเดิม แล้วแก้ให้ “ใช้ได้” โดยปล่อยขั้นตอน build และ target ที่ไม่จำเป็นและไม่เกี่ยวข้องเลยไว้เหมือนเดิม
ส่วนตัวแล้วเรื่องนี้ขัดใจมาก
ลองผิดลองถูกเหรอ? งั้นก็ขอให้สนุกนะ :p
จริงอยู่ที่มีเครื่องมือจำนวนมากที่ซับซ้อนเกินจำเป็น แต่ผมไม่คิดว่า Make เองเป็นตัวอย่างที่เหมาะกับเรื่องนั้น
ในเครื่องมือสมัยใหม่ ปัญหาความซับซ้อนมักถูกทำให้แย่ลงเพราะเอกสารที่ไม่เพียงพอ เอกสารแบบ cookbook เอกสารที่ไม่อธิบายโมเดลเชิงแนวคิดที่จำเป็นต่อการใช้เหตุผลว่าเครื่องมือทำงานอย่างไร และเอกสารที่ไม่มีสเปกที่ละเอียดและถูกต้องเพียงพอ
Make ไม่ใช่แบบนั้น มันมีเอกสารที่ดี และถ้าแค่ยอมใช้เวลาอ่านเอกสารจริง ๆ ก็ไม่ได้เข้าใจยาก
แนวทางแบบ cookbook ข้างต้นนำไปสู่วัฒนธรรมที่ให้ความสำคัญต่ำกับการเรียนรู้และทำความเข้าใจเครื่องมือที่ตัวเองใช้ รวมถึงความสำคัญของเอกสารที่ละเอียดรอบคอบซึ่งทำให้เรื่องนั้นเป็นไปได้ หรือความสัมพันธ์เชิงเหตุและผลอาจกลับกันก็ได้
ไม่ว่าอย่างไร ผมคิดว่าปัญหาอยู่ที่เราใช้เวลาน้อยเกินไปในการทำเอกสารเครื่องมือให้ครอบคลุมและทันสมัย ตั้งแต่แรกก็ยังออกแบบเครื่องมือไม่พอให้สามารถทำเอกสารแบบนั้นได้ และจัดสรรทรัพยากรน้อยเกินไปในการสอนและเรียนรู้เครื่องมือที่จำเป็น