วิธีทำให้เรื่องยากกลายเป็นเรื่องง่าย
(jvns.ca)- คีย์โน้ต Strange Loop ของ Julia Evans ว่าด้วยเหตุผลที่เทคโนโลยีซึ่งดูเหมือน “พื้นฐาน” อย่าง DNS, Bash, HTTP และ SQL กลับใช้เวลาเรียนรู้นาน และ วิธีลดกำแพงการเรียนรู้
- ความยากของ Bash อยู่ที่มี ข้อยกเว้นเล็ก ๆ และกับดัก มากมาย เช่น
set -eจะถูกปิดการทำงานเมื่อเรียกฟังก์ชันภายในเงื่อนไข||และหลายคนก็ใช้ Bash เพียงเป็นครั้งคราว จึงจำรายละเอียดให้แม่นได้ยาก - HTTP และ SQL มี ความซับซ้อนที่ซ่อนอยู่หลังพื้นผิวที่ดูเรียบง่าย เช่น implementation ของเบราว์เซอร์ที่มีโค้ดระดับ 20 ล้านบรรทัด, header และ flag จำนวนมาก, และความต่างระหว่างลำดับการเขียน SQL กับลำดับการทำงานจริง ทำให้ภาระในการเรียนรู้สูงขึ้น
- DNS ทำงานผ่านไลบรารี, แคช และการสื่อสารกับ authoritative nameserver ซึ่งผู้ใช้มองไม่ค่อยเห็น และ output ของ
digก็ซับซ้อน จึงทำให้ เครื่องมือและเดโมที่เผยให้เห็นการทำงานที่ซ่อนอยู่ มีความสำคัญ - การช่วยให้เรียนรู้ได้ดีควรมีทั้งการแชร์เครื่องมือและ reference, การย่อรายการใหญ่ให้เหลือรายการเล็กที่ใช้จริง, การอธิบายสิ่งที่คอมพิวเตอร์ทำตามลำดับเวลา, และการแบ่งปันทั้งเรื่องล้มเหลวและบันทึกบั๊ก
ทำไมเทคโนโลยีที่ดู “พื้นฐาน” ถึงใช้เวลาเรียนนาน
- คีย์โน้ต Strange Loop Making Hard Things Easy พูดถึงวิธีทำให้เทคโนโลยีที่เรียนยากเข้าใจได้ง่ายขึ้น
- จุดเริ่มต้นคือ DNS
- การหา IP address ของโดเมนดูเหมือนง่าย แต่ผู้บรรยายบอกว่าหลังจากเริ่มเรียน DNS ไปแล้ว 7 ปี ก็ยังเจอปัญหาระหว่างตั้งค่าเว็บไซต์ และโดยรวมแล้วใช้เวลาราว 10 ปี
- เพื่อนของเธอก็เจอปัญหาเดิมซ้ำ ๆ และหลายคนมักรับมันเป็นปัญหาส่วนตัวราวกับว่า “ควรจะเข้าใจได้แล้ว”
- ผู้บรรยายจึงเริ่มสำนักพิมพ์เล็ก ๆ ชื่อ Wizard Zines เพื่อเขียนอธิบายหัวข้อเหล่านี้ให้เข้าใจง่ายขึ้น โดยใช้ Bash, HTTP, SQL และ DNS เป็นกรณีศึกษา
Bash: ข้อยกเว้นที่จำยากควรให้เครื่องมือช่วยจำ
- Bash เป็นภาษาโปรแกรม แต่ก็เป็นหนึ่งในภาษาที่ผู้บรรยายใช้แล้วรู้สึกว่ามี พฤติกรรมประหลาด มากที่สุด
- ในสคริปต์ตัวอย่าง แม้
mv ./*.txt /tmmppจะล้มเหลว Bash ก็จะไม่หยุดโดยอัตโนมัติ และยังรันecho "success!"ต่อ- ถ้าใช้
set -eก็สามารถทำให้หยุดเมื่อเกิดความล้มเหลวได้ - แต่ถ้าเรียกฟังก์ชันในเงื่อนไขอย่าง
f || echo "failed!"ค่าset -eภายในฟังก์ชันจะถูกปิดการทำงานทั่วทั้งฟังก์ชัน ทำให้สุดท้ายยังพิมพ์successออกมาอีก - พฤติกรรมนี้ไม่ใช่บั๊กของ Bash แต่เป็นพฤติกรรมที่มีการระบุไว้ในเอกสาร
- ถ้าใช้
- เหตุผลหนึ่งที่ Bash ยากคือหลายคนเขียนสคริปต์ Bash แค่ ทุก 6 เดือนครั้ง แล้วก็ไม่ได้กลับมาดูอีก
- ถ้าระบบที่ใช้นาน ๆ ครั้งเต็มไปด้วยความรู้จุกจิกและกับดัก ก็จะใช้อย่างถูกต้องได้ยาก
- คำพูดแนว ๆ ว่า “ไม่มีใครใช้ Bash เป็น” ไม่ตรงกับความจริง
- มีคนจำนวนมากใช้ Bash อยู่ และถึงจะไม่สมบูรณ์แบบก็ยังใช้แก้งานได้บ่อย
- เป้าหมายคือพาคนที่ยืนงงอยู่หน้ากองกับดักมหาศาล ไปสู่จุดที่ “ใช้งานได้ค่อนข้างถูกต้องเป็นส่วนใหญ่”
- ShellCheck เป็นเครื่องมือที่ช่วยจำกับดักของ Bash ที่คนจำเองได้ยาก และเตือนแทนมนุษย์
shellcheck -o all bad-again.shจะแสดงคำเตือนSC2310ว่าset -eถูกปิดการทำงานเมื่อฟังก์ชันถูกเรียกในเงื่อนไข||- การตรวจนี้จะปรากฏก็ต่อเมื่อรันด้วย
-o all - เครื่องมือแบบนี้ช่วยย้ายภาระของความรู้จุกจิกไปให้คอมพิวเตอร์ ลดภาระทางความคิดของคน
เรื่องเล่าความล้มเหลวช่วยตัดสินใจได้มากกว่า “best practices”
- ถึงจะไม่ได้สร้างเครื่องมือเอง การบอกเพื่อนหรือเพื่อนร่วมงานถึงเครื่องมือที่มีประโยชน์และใช้อยู่แล้วก็สำคัญ
- ผู้บรรยายบอกว่าตัวเองก็เพิ่งมารู้จัก ShellCheck ช้าไปมาก และโกรธที่ก่อนหน้านั้นไม่รู้ว่าจริง ๆ แล้วไม่จำเป็นต้องจำทุกอย่างไว้ในหัว
- การแชร์กับดักและเรื่องเล่าความล้มเหลวใกล้เคียงกับ การทำประโยชน์ให้ชุมชน
- กรณี
set -eถูกปิดใน Bash เป็นสิ่งที่เธอเพิ่งเรียนรู้จากเพื่อนชื่อ Jesse ไม่กี่สัปดาห์ก่อน - ถ้าเรารู้เรื่องที่คนอื่นเคยพลาด ก็หลีกเลี่ยงปัญหาเดียวกันได้โดยไม่ต้องเจอเอง
- กรณี
- แทนที่จะมีความเห็นแรง ๆ อย่าง “ไม่มีใครควรใช้ Bash” เรื่องเล่าว่า Bash ทำให้เกิดปัญหาอะไรจริง ๆ จะมีประโยชน์มากกว่า
- บางคนอาจฟังแล้วตัดสินใจใช้ ShellCheck และคงสคริปต์ Bash ง่าย ๆ ต่อไป
- บางคนอาจตัดสินใจว่าไม่อยากใช้ Bash เลย
- ต่อให้แต่ละคนตอบสนองต่อกรณีเดียวกันต่างกัน ก็ไม่เป็นไร
HTTP: ต้องเข้าใจโดยมีเบราว์เซอร์ 20 ล้านบรรทัดเป็นฉากหลัง
- HTTP response อาจดูเหมือนมีโครงสร้างง่าย ๆ คือ status code, header และ body
- แต่คำถามว่า “ทำไมต้องตั้ง header นี้” ก็นำไปสู่ พฤติกรรมของเบราว์เซอร์ ทันที
- Firefox มีโค้ดประมาณ 20 ล้านบรรทัด
- เบราว์เซอร์พัฒนามาตั้งแต่ยุค 1990s และ security model ก็เปลี่ยนไปเรื่อย ๆ ตามการโจมตีและการเปลี่ยนแปลงของเว็บ
- ถ้าอยากเข้าใจว่าทำไมหัวข้อหนึ่งถึงยาก ต้องดูว่ามี codebase ขนาดมหาศาลอยู่เบื้องหลังหรือไม่
- ไม่ใช่แค่ HTTP อย่างเดียว แต่รวมถึง CSS, JS ฯลฯ ด้วย ทว่าความซับซ้อนของเบราว์เซอร์สมัยใหม่ก็ช่วยอธิบายกำแพงการเรียนรู้ของ HTTP ได้ดี
- รายการใหญ่ควรถูกย่อให้เป็น รายการเล็ก เพื่อให้เข้าใจง่ายขึ้น
- รายการ HTTP request header มีมากกว่า 43 รายการ และยังมี header ที่ไม่เป็นทางการอีก
- ผู้บรรยายพูดถึง 15 header ที่ตัวเองรู้จักและใช้จริงใน การ์ตูน HTTP request headers
- “header ที่สำคัญที่สุด” ไม่ใช่รายการแบบวัตถุวิสัย แต่เป็นรายการเชิงอัตวิสัยตามสิ่งที่ตัวเองรู้และใช้
- เช่น แค่รู้ว่าเมื่อตั้ง
Accept-Encodingเป็นgzipก็จะได้ response แบบบีบอัด สำหรับส่วนใหญ่ก็มักเพียงพอแล้ว
- เครื่องมือ command line ก็อธิบายด้วยแนวทางเดียวกันได้
- man page ของ
grepมี flag มากมาย แต่ผู้บรรยายบอกว่าแม้จะใช้grepมา 20 ปี ก็ยังไม่ได้รู้ทั้งหมด - ถ้าคนมีประสบการณ์บอกว่า “ฉันรู้แค่ 7 อย่างในระบบนี้ และมันคือสิ่งเหล่านี้” ก็ช่วยมือใหม่ได้มาก
- และคนมีประสบการณ์อีกคนก็อาจรู้ “7 อย่าง” อีกชุดหนึ่ง
- man page ของ
reference ควรถูกแชร์อย่างตรงไปตรงมาว่าใช้อะไรจริง
- สำหรับข้อมูลที่ใส่ไว้ในหัวคนไม่ได้ จำเป็นต้องมี reference ที่ดี
- ผู้บรรยายบอกว่าตัวเองเรียน CSS แบบเป็นช่วง ๆ มา 20 ปี แต่เพิ่งรู้จัก CSS-Tricks ในช่วงประมาณ 2 ปีล่าสุด และคิดว่าถ้ารู้จักเร็วกว่านี้ก็คงช่วยได้มาก
- แม้ CSS-Tricks หลังการเข้าซื้อกิจการจะดูเหมือนหยุดลงบทความใหม่ตั้งแต่เดือนเมษายน แต่บทความเก่าก็ยังมีประโยชน์อยู่มาก
- สำหรับ HTTP เธอใช้ Mozilla Developer Network บ่อยมาก
- ส่วน reference ทางการของ HTTP คือ RFC 9110, 9111, 9112, 9113, 9114) ที่เขียนในปี 2022
- ใช้ค้นหารายละเอียดอย่างพฤติกรรมที่แน่นอนของ header
Connectionได้ - ปกติ reference หลักของผู้บรรยายมักเป็น MDN แต่ก็ชื่นชมว่า RFC ทางการจัดระเบียบไว้ดีมาก
- ใช้ค้นหารายละเอียดอย่างพฤติกรรมที่แน่นอนของ header
- เวลาจะแชร์ reference ควรแยกให้ออกระหว่างสิ่งที่แชร์เพราะมันดูเท่ กับสิ่งที่ใช้งานจริงในงานประจำ
- ต่อให้ในชีวิตจริงใช้ reference ที่ “ดูไม่เท่” อย่าง w3schools ก็สำคัญที่จะพูดตรง ๆ ว่าใช้อยู่จริงหรือไม่
SQL: อธิบายสิ่งที่คอมพิวเตอร์ทำตามลำดับเวลา
- SQL อาจทำให้ผู้เริ่มต้นสับสน เพราะลำดับที่เขียน query ไม่ตรงกับลำดับการทำงานเชิงแนวคิดจริง
- mental model สำหรับ SQL ที่ผู้บรรยายใช้มีลำดับดังนี้
FROMWHEREGROUP BYHAVINGSELECTORDER BYLIMIT
- ฐานข้อมูลจริงมีการ optimize จึงซับซ้อนกว่านี้ แต่ โมเดลตามลำดับเวลา นี้ก็ยังมีประโยชน์ในสถานการณ์ส่วนใหญ่
- มันเกือบเหมือนกับลำดับที่เขียนใน query ต่างกันตรงที่
SELECTมาเป็นลำดับที่ห้า
- มันเกือบเหมือนกับลำดับที่เขียนใน query ต่างกันตรงที่
- วิธีตั้งคำถามว่า “คอมพิวเตอร์ทำอะไรก่อนจริง ๆ” ยังใช้กับหัวข้ออื่นได้ด้วย
- ใน CORS สามารถเขียนการสื่อสารทั้งหมดระหว่างเบราว์เซอร์กับเซิร์ฟเวอร์ตามลำดับเวลาเพื่อทำความเข้าใจได้
- ผู้บรรยายยก การ์ตูน CORS เป็นตัวอย่างของวิธีนี้
- การอธิบายตามลำดับเวลาดูเหมือนง่าย แต่จริง ๆ ทำได้ยาก จึงมีประโยชน์มากต่อการทำงานร่วมกัน
- Behind Hello World on Linux อธิบายว่าเกิดอะไรขึ้นบ้างเมื่อรัน “hello world” บน Linux
- ผู้บรรยายเคยเขียน บทความคล้ายกัน เมื่อ 10 ปีก่อน แต่บทความปี 2023 ยาวกว่าประมาณ 6 เท่า
- ไม่ใช่เพราะ Linux ซับซ้อนขึ้น แต่เพราะในปี 2013 เธอยังรู้ไม่มากพอว่ามีอะไรเกิดขึ้นตามลำดับเวลาบ้าง
- ในทีมเองก็ทำแบบนี้ได้ เช่น ถ้าคำขอเข้ามาที่ API endpoint หนึ่ง แล้วร่วมกันวาด timeline ตามลำดับเวลา ก็จะช่วยเชื่อมความรู้ที่แต่ละคนมีอยู่เข้าด้วยกัน
DNS: ต้องเผยให้เห็นระบบที่ซ่อนอยู่จึงจะเกิดสัญชาตญาณ
- DNS เป็นระบบที่เบราว์เซอร์, ฟังก์ชันไลบรารีที่ส่ง DNS request, แคช และ authoritative nameserver ทำงานร่วมกัน
- ปัญหาคือหลายส่วนในนี้ ถูกซ่อนจากผู้ใช้
- ไม่ง่ายที่จะรู้ว่าโค้ดไลบรารีส่วนไหนเป็นคนส่ง DNS request
- แคชเก็บข้อมูลภายในไว้และตรวจสอบได้ยาก อีกทั้งผู้ใช้ก็ไม่ได้ควบคุมมัน
- การสื่อสารระหว่างแคชกับ authoritative nameserver ก็ไม่ปรากฏให้เห็น
- ผู้บรรยายสร้าง DNS server ขนาดเล็กชื่อ Mess With DNS ร่วมกับเพื่อนชื่อ Marie
- ผู้ใช้สามารถสร้าง DNS record ให้โดเมนได้
- ทุกครั้งที่มี request เข้ามาจาก resolver ระบบจะแสดงให้เห็นว่ามีข้อความอะไรเข้ามา
- ในเดโมของ Strange Loop เธอสร้าง CNAME record ให้ชื่อ
strangeloopชี้ไปที่orange.jvns.caและเห็นว่า DNS resolver ในแคนาดาที่เบราว์เซอร์ใช้ส่งคำขอ A และ AAAA record มา
- อีกตัวอย่างของการเผยให้เห็นสิ่งที่ซ่อนอยู่คือ float.exposed
- สามารถปรับ significand และ exponent ของเลข floating point 32-bit แล้วดูเลข floating point ถัดไปและการเปลี่ยนแปลงของช่วงห่างได้
- อีกเหตุผลที่ DNS ยากคือมันเป็น distributed system ขนาดมหึมา
- ผู้บรรยายพูดในทำนองว่า “อาจมีคอมพิวเตอร์มากกว่า 5 ล้านเครื่องเกี่ยวข้อง” ซึ่งผู้ใช้ควบคุมแทบไม่ได้ และบางส่วนก็อาจทำงานไม่ตรงกับที่คาด
เมื่อเครื่องมือเองกลายเป็นกำแพงการเรียนรู้ เช่น output ของ dig
- output ของเครื่องมือ DNS เองก็เพิ่มความสับสนได้
digมี flag+norecurse- ใช้ขอให้ resolver ส่งกลับเฉพาะผลลัพธ์ที่มีอยู่ในแคชแล้ว
dig +norecurse jvns.caใช้ตรวจได้ประมาณว่า resolver นั้นได้แคชโดเมนนั้นไว้ในช่วง 5 นาทีที่ผ่านมาหรือไม่
- output ของ
digอาจทำให้ผู้เริ่มต้นรู้สึกว่า DNS ซับซ้อนกว่าที่เป็นจริง- ผู้บรรยายมองว่านี่ใกล้เคียงกับการที่รูปแบบ output ซึ่งค่อนข้างตามอำเภอใจและถูกกำหนดตั้งแต่ยุค 1990s ยังคงถูกใช้มาจนถึงทุกวันนี้
- “eraser eyes” คือวิธีมองข้าม output ที่ซับซ้อนโดยเหมือนลบทุกอย่างทิ้ง เหลือไว้เฉพาะส่วนที่ต้องดูจริง
- ในตัวอย่างจะสนใจเฉพาะ response code
SERVFAIL - ตามความเข้าใจของผู้บรรยาย
SERVFAILในบริบทนี้มีความหมายใกล้เคียงกับ “ไม่มีอยู่ในแคช”
- ในตัวอย่างจะสนใจเฉพาะ response code
- เวลาสาธิตเครื่องมือ ถ้าบอกด้วยว่าควรดู output หรือ UI ส่วนไหน และควรไม่สนใจส่วนไหน ก็จะช่วยให้เรียนรู้ได้มาก
digแม้ output จะหยาบ แต่ก็มีฟังก์ชันเยอะ รองรับ+norecurseมีอยู่แทบทุกที่ และมีข้อดีตรงความเสถียรเพราะแทบไม่เปลี่ยนมานาน
บทบาทต่าง ๆ ในการช่วยกันทำให้เรื่องยากง่ายขึ้น
- การทำให้เทคโนโลยีเข้าใจง่ายขึ้นเป็นสิ่งที่แบ่งปันกับคนรอบตัวได้ แม้จะไม่มีบล็อกส่วนตัว
- วิธีที่ผู้บรรยายสรุปไว้มีดังนี้
- แชร์ เครื่องมือ ที่มีประโยชน์
- แชร์ reference ที่ใช้งานจริง
- อธิบายสิ่งที่เกิดขึ้นในคอมพิวเตอร์ตามลำดับเวลา
- ย่อรายการใหญ่ให้เป็นรายการเล็กที่ตัวเองใช้จริง
- แสดงให้เห็นพฤติกรรมที่ซ่อนอยู่
- สาธิตเครื่องมือที่ชวนสับสน พร้อมบอกว่าควรดูตรงไหน
- คนที่ช่วยได้ก็มีหลายแบบ
- “ผู้ใช้เก๋า ๆ ที่ชอบบ่น” ช่วยเล่าว่าเมื่อก่อนอะไรเคยผิดพลาดบ้าง จึงช่วยลดความลำบากให้คนอื่น
- “มือใหม่เสียงดัง” ช่วยถามว่า “สิ่งนี้ทำงานอย่างไร?” ทำให้คนอื่นรู้สึกโล่งใจว่าไม่ได้งงอยู่คนเดียว
- ถ้า senior developer ถามสิ่งที่ตัวเองไม่รู้ต่อหน้าคนอื่น ก็ช่วยให้คนที่กังวลว่าจะถูกมองว่าไม่รู้ กล้าเรียนรู้ไปด้วยกัน
- “ผู้บันทึกบั๊ก” ช่วยจดว่าเกิดอะไรขึ้น เพื่อไม่ให้บั๊กเดิมกลับมาอีก
- “ผู้สร้างเครื่องมือ” ใช้โค้ดทำให้ปัญหาง่ายลงอย่างถาวร แทนการต้องอธิบายซ้ำ ๆ
- “ผู้แชร์สิ่งที่เพิ่งเรียนวันนี้” ช่วยแบ่งปันเครื่องมือใหม่ บั๊กที่เจอ หรือฟีเจอร์ใหม่ของไลบรารีที่เพิ่งรู้
- “คนที่เปิดแท็บไว้ 700 แท็บ” อาจรู้แล้วว่าควรไปหาข้อมูลจากที่ไหน
- ยังต้องมีทั้ง “คนที่ตอบคำถาม” และ “คนที่เขียนเก็บไว้ให้ค้นเจอทีหลัง”
- การรู้สึกว่าสิ่งที่ดูพื้นฐานนั้นยาก ไม่ใช่ปัญหาของใครคนเดียว
- คนจำนวนมากติดอยู่ที่จุดเดียวกันด้วยเหตุผลแบบเดียวกัน
- ถ้าเข้าใจเหตุผลที่ทำให้มันยาก เราก็จะแก้ปัญหาได้ดีขึ้น เหมือนกับการแก้บั๊กของโปรแกรมคอมพิวเตอร์
- ปัจจัยที่ทำให้มันยากมีทั้งความรู้จุกจิกมหาศาลและกับดัก, โค้ดระดับ 20 ล้านบรรทัด, ระบบที่ซ่อนอยู่, และ output ของเครื่องมือที่สับสนและไม่ได้รับการปรับปรุง
- ผู้บรรยายบอกว่ายังไม่เข้าใจดีนักว่าทำไม Git ถึงยาก แต่ก็ยังเป็นหัวข้อที่อยากคิดต่อและพยายามทำความเข้าใจต่อไป
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
ส่วนที่โดนใจที่สุดคือคำว่า จงแสดงสิ่งที่ปกติถูกซ่อนไว้
เครื่องมือแบบนี้แทบจะทำให้สถานการณ์ชัดเจนขึ้นได้ทันที ลองนึกถึง developer tools ของเว็บเบราว์เซอร์ดู ใน “ยุคมืด” ที่ยังไม่มีของแบบนั้น มันแย่มากเพราะเรามองไม่เห็นว่าเกิดอะไรขึ้นและต้องเดาเอาเอง
เครื่องมืออย่าง Wireshark ที่แสดง byte ของแพ็กเก็ตเครือข่ายที่เข้าถึงได้ และยัง parse โครงสร้างให้ด้วย ไม่ได้มีประโยชน์มากแค่กับการ debug เครือข่ายเท่านั้น แต่ยังมีประโยชน์มากในการสอนแนวคิดเครือข่ายด้วย เพราะมันไม่ซ่อนอะไรเลย
ผมชอบซอฟต์แวร์โอเพนซอร์สด้วยเหตุผลนี้เช่นกัน เพราะสามารถดูซอร์สเพื่อเข้าใจสาเหตุของบั๊ก เติมช่องว่างความรู้ที่เอกสารทิ้งไว้ หรือเรียนรู้แนวคิดการเขียนโปรแกรมเพิ่มเติมได้ จึงไม่มีอะไรถูกซ่อนไว้
ตัวอย่างเช่น มันไม่เคยแสดง Ethernet preamble เลย, แสดง Ethernet frame checksum แค่บางครั้ง และไม่เคยแสดง inter-frame gap ซึ่งเป็นองค์ประกอบจำเป็นของโปรโตคอล Ethernet
ถึงจะใกล้เคียงมากแล้ว แต่ก็แสดงให้เห็นว่าไม่ว่าที่ไหนก็ยังมีรายละเอียดซ่อนอยู่อีกเสมอ
ในหัวเราก็ visualize อยู่แล้ว และคำอธิบายใด ๆ เกี่ยวกับ computing สุดท้ายก็ถูกแสดงเป็น diagram ได้ แต่ตอนเขียนโค้ดกลับไม่มี diagram เลย
ก็แค่ instrument โค้ดทั้งหมดแบบ dynamic แล้วส่งข้อความไปที่ GUI ก็พอ
ผู้เชี่ยวชาญที่เคยมีความรู้นั้นและสอนได้ ตอนนี้ก็ไม่ได้อยู่ในองค์กรแล้ว แต่ไปรวมตัวกันอยู่ที่บริษัททำเครื่องมือเหล่านั้น
ดังนั้นเมื่อย้ายไปใช้ command line ตามธรรมชาติ ก็จะคุ้นเคยและใช้งานเองได้ทันที
Julia น่าจะเป็นหนึ่งในคนที่น่าชื่นชอบที่สุดในวงการเทคโนโลยี
ทุกครั้งที่อ่านงานเขียนของเธอ ความตื่นเต้นแบบตอนเด็ก ๆ ที่เพิ่งเริ่มไขความลับของโลกจริงผ่านการทดลองเล็ก ๆ ก็กลับมาอีก น่ารักจริง ๆ
โชคดีที่ช่วงหลังเริ่มพบคนที่เข้าข่ายแบบนี้มากขึ้น
แต่งานเขียนของ Julia ทุกชิ้นทำให้ผมรู้สึกถึงความตื่นเต้นแบบที่พูดถึงข้างต้นได้ทั้งหมด
ผมคิดว่าคำว่า “เมื่อมือใหม่บอกว่า ‘อันนี้ยาก’ คนมีประสบการณ์ก็บอกว่า ‘ใช่ bash ใช้ไม่ได้หรอก ไม่มีใครรู้จริง ๆ’” ไม่ควรตีความตามตัวอักษร
ความหมายใกล้เคียงกว่าคือ “เราไม่ได้เข้าใจโค้ด bash ที่เราเขียนไว้อย่างมั่นใจนัก และก็ไม่ได้มั่นใจว่ามันจะทำงานตามคาดในสถานการณ์ที่ไม่ได้ทดสอบ”
หมายความว่าเมื่อมีอะไรที่ไม่ธรรมดาแม้แต่นิดเดียวเกิดขึ้น ก็พอจะคาดได้ว่าบางอย่างจะล้มเหลว และข้อเท็จจริงใหม่ที่ได้เรียนรู้เกี่ยวกับ bash อาจทำให้ขนลุก หรือถึงขั้นฟาดของใกล้มือแรงจนเสียหาย
Bash เป็นภาษาที่ซับซ้อน และสำหรับโปรแกรมเมอร์ส่วนใหญ่ มันต่างจากภาษาอื่น ๆ ที่ใช้เป็นประจำโดยสิ้นเชิง บริษัทส่วนใหญ่มักมี bash อยู่บ้างใน production ที่ไหนสักแห่ง แต่บ่อยครั้งไม่มีใครใช้มันมากพอจนรู้ดีจริง ๆ
ผมไม่คิดว่าเป็นเรื่องบังเอิญที่ build tools, CI tools และเครื่องมือ cloud orchestration วิวัฒน์ไปในทิศทางที่ลดความจำเป็นของ shell scripting
ลองคิดเป็น thought experiment ว่าเราเพิ่ม assignment statement ที่ดีกว่าให้ bash ไม่ได้หรือ เช่นในโหมด
set --goodassถ้าเขียนแบบa = string1 + '.' + string2ได้ ก็จะตัดปัญหา shell quoting ออกไปได้มากเครื่องมืออย่าง
makeก็จะได้ประโยชน์ด้วย ถ้าใช้เวลา 6 เดือนทำให้makeมีตัวแปรที่ใช้งานได้ วิธีจัดการ path และ filename ที่ชัดเจน และ target ที่ใช้ได้ดีกว่าเดิม อาจดีกว่าการใช้เวลา 6 เดือนสร้าง Makefile ที่ซับซ้อนโดยเฉพาะความรู้สึกที่ว่า “สำหรับโปรแกรมเมอร์ส่วนใหญ่ bash ต่างจากทุกภาษาที่ใช้เป็นประจำ” ไม่ใช่สิ่งที่มือใหม่จะอนุมานได้เสมอไป เพราะต้องมีประสบการณ์พอจะแยกความต่างระหว่าง “ไม่ค่อยพบบ่อย” กับ “เข้าใจยากสุด ๆ”
เกี่ยวกับเรื่องนี้ ซอฟต์แวร์ส่วนใหญ่ถูก ออกแบบเกินความจำเป็น
ผมคิดว่าส่วนหนึ่งเป็นเพราะการรวมศูนย์ของอุตสาหกรรมด้วย คนกลุ่มเล็ก ๆ ที่ควบคุมเครื่องมือจำนวนน้อยได้ประโยชน์จากการผลักทุกคนไปใช้เครื่องมือเหล่านั้น และผลก็คือเครื่องมือจำนวนมากกลายเป็น “เครื่องมือสำหรับทุกอย่าง” ครอบคลุมมากกว่ากรณีใช้งานที่ต้องจัดการจริง ๆ มาก
บริษัทต่าง ๆ อยากให้นักพัฒนาทุกคนรู้จักเครื่องมือเดียวกัน เพราะทำให้ทดแทนกันได้ง่ายระหว่างโปรเจกต์และบริษัท และทำให้อำนาจต่อรองในอุตสาหกรรมอ่อนลง
ดังนั้นซอฟต์แวร์จึงเหลือเพียงสายหลักเดียว ส่วนแนวทางทางเลือกถูกกีดกันออกไปโดยไม่มีงานรองรับ ทั้งที่โดยธรรมชาติแล้วอุตสาหกรรมอยากกระจายตัว แต่กลับทำไม่ได้
มองในแง่บวก สักวันหนึ่งแนวทางนอกกระแสที่เหนือกว่ามาก ๆ จะปรากฏขึ้นและกัดเซาะแนวทางกระแสหลัก เทคโนโลยีไม่ใช่คณิตศาสตร์ และก็แตกต่างจากวิทยาศาสตร์ มันสามารถรองรับหลายแขนงที่แก้ปัญหาเดียวกันได้หลายวิธีอย่างเพียงพอ
ตอนนั้น browser war ทำให้เรายุ่งก็จริง แต่ตอนนี้แม้ browser ส่วนใหญ่จะเข้ากันได้แล้ว เรากลับสร้างความซับซ้อนฝั่ง frontend สำหรับ web app ที่ส่วนใหญ่ไม่จำเป็นขึ้นมาเต็มไปหมด
สิ่งอย่าง DNS, IP, HTTPS เป็นเทคโนโลยีพื้นฐานที่มีความเข้ากันได้ย้อนหลังและปัจจัยทางการเมืองพันกันอยู่ จึงช่วยไม่ได้
ถึงอย่างนั้น ผมรู้สึกว่าการเรียนรู้สิ่งเหล่านั้นให้ดีเป็นการลงทุนที่ดีกว่าการเรียน framework มาก ถ้าพูดต่อคงไปถึงเรื่อง innovation tokens แน่ ๆ
หากต้องการทำให้สิ่งที่ยากกลายเป็นเรื่องง่าย ต้องหา abstraction ที่เหมาะสมให้เจอ คือเก็บไว้ในหัวแค่บางส่วนของเนื้อหาที่ยากและรายละเอียดที่ใช้บ่อย ส่วนที่เหลือค่อยไปค้นเมื่อจำเป็น
ปัญหาคือผู้คนมักไม่ยอมสร้างการบีบอัดเชิงปริชานสำหรับหัวข้อใหญ่ ๆ จนกว่าจะจำเป็นจริง ๆ เพราะพวกเขาแบกรับภาระทางปริชานขนาดใหญ่อื่นอยู่แล้ว จึงต่อต้านการเพิ่มภาระใหม่
ถ้าสามารถพึ่งพาคนอื่นที่รู้เรื่องหัวข้อ X ดีได้ ก็อาจทำแบบนั้นไปเลย และไม่ต้องพยายามเรียนรู้ X ให้เพียงพอ วิธีที่ดีที่สุดสำหรับคนที่รู้ X ดีในการลดคำขอความช่วยเหลือ คือช่วยให้คนอื่นเข้าใจขั้นต่ำสุดของ X
set -eนั้นเสียอยู่ ความจำเป็นที่ต้องครอบทุกอย่างด้วยเครื่องหมายคำพูดก็เสียอยู่เช่นกัน และ globbing ควรเป็นฟีเจอร์ที่ต้องขอใช้อย่างชัดเจน ใน command line อาจน่ารำคาญ แต่ในสคริปต์เป็นคนละเรื่อง และตอนนี้ถ้าปิด globbing แบบ global ก็จะทำ globbing ในจุดที่ต้องการได้ยากค่าเริ่มต้นที่แย่แบบนี้ไม่ได้มีแค่ใน Bash แต่มีอยู่ทั่วทั้งเชลล์สาย Ksh และ Bourne shell
SQL ก็มีหลายคนที่อยากสลับลำดับ clause ไม่มีเหตุผลที่ทำไม่ได้ และดูเหมือนจะเป็นการเปลี่ยนแปลงค่อนข้างเล็กเพื่อให้ parser SQL ที่มีอยู่ยอมรับ clause ในลำดับอื่น
อย่างไรก็ตาม โดยส่วนตัวไม่มีปัญหาเชิงปริชานนี้ อาจเพราะรู้อยู่แล้วว่าต้องดูแหล่งที่มาของตารางก่อน
set -eเสียอยู่, ต้องครอบทุกอย่างด้วยเครื่องหมายคำพูด, และ globbing ควรเป็นแบบชัดเจน — OSH แก้ให้ได้โดยยังรันสคริปต์เชลล์เดิมได้ถ้าเพิ่ม
shopt --set ysh:upgradeไว้บนสุดของสคริปต์ ปัญหาทั้งสามอย่างนั้นจะหายไปถ้าอยากช่วยโปรเจกต์นี้ ก็ดาวน์โหลด tarball ไปตรวจสอบข้ออ้างเหล่านี้ แล้วเขียนบล็อกโพสต์ให้จะดีมาก
รายละเอียดอยู่ที่ https://www.oilshell.org/release/latest/doc/error-handling.h... และ https://www.oilshell.org/release/latest/doc/simple-word-eval...
เอกสารครอบคลุมมาก แต่คนส่วนใหญ่ไม่ได้ต้องการรายละเอียดระดับนั้น ดังนั้นถ้ามีใครช่วยทดสอบแล้วเขียนสรุปสั้น ๆ ก็จะเป็นประโยชน์
เหตุผลที่ช่วงหนึ่งไม่ได้ผลักดัน Oils อย่างจริงจังคือมันมี dependency กับ Python แต่ตอนนี้เป็น C++ ล้วนแล้ว และ ณ สัปดาห์นี้ชนะ bash ใน benchmark บางตัวที่เน้นการคำนวณ
สคริปต์ที่เน้น I/O นั้นเร็วเท่าเดิมมาตลอด เช่นเดียวกับสคริปต์เชลล์ส่วนใหญ่ ในเอกสารยังต้องเปลี่ยนชื่อ Oil เป็น YSH อยู่ จึงอาจมีความสับสนไปอีกพักหนึ่ง: https://www.oilshell.org/blog/2023/03/rename.html
set -eถึงอย่างนั้นตอนนี้ก็ยังมีเสิร์ชเอนจินprojectที่ทำงานเหมือนselectแต่สามารถวางไว้ในตำแหน่งที่ถูกต้องได้พลังพิเศษของผมคือความจำที่แย่มาก ดังนั้นถ้าจะจำอะไรได้ก็ต้องเข้าใจมันให้ได้ก่อน กล่าวคือต้องมีการบีบอัดเชิงปริชาน ผมเรียนแบบจำไปเฉย ๆ เหมือนคนทั่วไปไม่ได้
สิ่งที่ได้เรียนรู้วันนี้: ในรายการ
&&หรือ||แม้จะมีคำสั่งที่ล้มเหลว เชลล์ก็จะไม่จบการทำงาน ยกเว้นคำสั่งที่อยู่หลัง&&หรือ||ตัวสุดท้ายในบรรดาคำสั่งที่ถูกเรียกใช้อ้างอิง: https://www.gnu.org/software/bash/manual/bash.html#index-set
สิ่งที่
/bin/falseทำก็แค่คืนค่า 1 เท่านั้น แบบนั้นคือความล้มเหลวหรือ? ไม่ใช่ มันถูกออกแบบมาให้ทำงานแบบนั้น และเป็นเครื่องมือที่มีวัตถุประสงค์ตรงตามนั้นตามตัวอักษรผมเคยเขียนเชลล์สคริปต์มาหลายร้อยตัว และในนั้นมีหลายคำสั่งที่คืนค่าที่ไม่ใช่ 0 อย่างปกติมาก ๆ เพื่อทำงานของตัวเอง เช่น ตรวจสอบว่าสตริงมีแพตเทิร์นบางอย่างหรือไม่
โปรแกรมสามารถคืนค่า exit code ใดก็ได้ตามต้องการในทุกสถานการณ์ และตามธรรมเนียมแล้วความสำเร็จคือ 0 ความล้มเหลวคือค่าที่ไม่ใช่ 0 แต่สิ่งเดียวที่ภาษาเชลล์ใส่ใจคือ 0 ถูกประเมินเป็น “จริง” และค่าที่ไม่ใช่ 0 ถูกประเมินเป็น “เท็จ”
ถ้าเชลล์จบการทำงานทุกครั้งที่โปรแกรมใด ๆ คืนค่าที่ไม่ใช่ 0 คำสั่ง
ifและลูปก็จะเป็นไปไม่ได้ ทำให้ใช้งานลำบากมากถ้าเป็นสคริปต์ที่ให้ความสำคัญกับ return code ของโปรแกรมใดโปรแกรมหนึ่ง ก็ต้องตรวจสอบและจัดการอย่างชัดเจน อย่างที่ลิงก์ไว้ มีตัวเลือกที่ทำให้เชลล์จบการทำงานเมื่อคำสั่งภายในคืนค่าที่ไม่ใช่ 0 และผู้เขียนเชลล์สคริปต์ระดับต้นถึงกลางจำนวนมากก็ยืนยันแบบยึดหลักการว่าควรใช้ตัวเลือกเหล่านั้นในทุกสคริปต์
แต่ในสคริปต์ที่ซับซ้อน ผมรู้สึกว่ามี edge case จำนวนมากที่ค่อนข้างแฮ็ก ๆ และรับมือยาก ถ้าต้องใช้ตัวเลือกพวกนั้นทุกครั้ง บางทีใช้ Makefile ไปเลยอาจดีกว่า
&&และ||ถูกใช้เหมือนเงื่อนไขบ่อยมาก[ -e README ] && cat READMEช่วยหลีกเลี่ยงข้อผิดพลาดเมื่อไม่มีไฟล์ README และ[ -e README ] || echo "You should write a README!"ทำงานในทางกลับกันปัญหาที่แยบยลกว่าคือ แม้จะสมมติว่าใช้
set -eใน pipeline เชลล์ก็จะไม่จบการทำงานถ้าคำสั่งสุดท้ายไม่ล้มเหลวgrep foo README | sortจะไม่ล้มเหลวแม้ไม่มี README ตราบใดที่ไม่ได้ใช้set -o pipefailด้วยแม้จะตั้ง
set -eอย่างชัดเจนภายในฟังก์ชัน ก็ยังถูกเขียนทับเคยยกตัวอย่างไว้ก่อนหน้านี้: https://news.ycombinator.com/item?id=22213830
เป็นบทความที่อธิบายสิ่งต่าง ๆ ที่ดูเหมือนไม่ควรยาก แต่จริง ๆ แล้วมีความซับซ้อนมากได้ดี
แต่ส่วน SQL ดูเหมือนจะยิ่งผลักดันความล้มเหลวเชิงแนวคิด มากกว่าจะช่วยไขความลึกลับ
ตรรกะของคิวรีเป็นแบบ declarative และกำหนดผลลัพธ์ สิ่งที่มีลำดับการทำงานหรือลักษณะเชิงขั้นตอนคือ query plan ต่างหาก ควรเรียนสิ่งนี้ก่อน
จากนั้นจึงค่อยเรียนพื้นที่ก้ำกึ่งอย่าง dependent subquery ได้ ถ้ามองเห็นว่า
not existsกับ anti join เทียบเท่ากัน ก็จะเข้าใจและให้เหตุผลได้การเปรียบเปรยในทำนองให้เข้าใจคิวรีที่เขียนขึ้นแบบเชิงขั้นตอนเป็นเพียงการเลื่อนปัญหาออกไป และเมื่อไปติดกับสิ่งที่ซับซ้อนกว่า ก็จะไม่มีวิธีแกะ “คำโกหกด้วยเจตนาดี” นั้นออก
เป็นการนำเสนอที่ยอดเยี่ยม จริงอยู่ที่ Bash เต็มไปด้วย “กับดัก” และเกร็ดความรู้จิปาถะ และยากที่จะจำทั้งหมด แต่ผมคิดว่าเกร็ดบางอย่างก็ควรจำไว้เหมือนกัน
เช่น ผมมักลืมลำดับอาร์กิวเมนต์ของคำสั่ง
findอยู่บ่อย ๆ จนเสียเวลาในการนึก syntax ตอนอยู่หน้าเครื่องที่ต่ออินเทอร์เน็ตไม่ได้ทันทีดังนั้นผมจึงตัดสินใจเรียนและจำเครื่องมือบรรทัดคำสั่งที่ใช้บ่อยที่สุดกับ กับดัก บางส่วนของมัน โดยใช้ Anki และเทคนิคช่วยจำบางอย่าง ผมคิดว่าผลตอบแทนต่อการลงทุนคุ้มค่ามาก
จริง ๆ แล้วผมอ่าน Networking for System Administrators ของ Michael W. Lucas หลังจากเห็นคำแนะนำหนังสือจาก jvns.ca และดึงความรู้ทางเทคนิคกับภูมิปัญญาของผู้ดูแลระบบไม่น้อยออกมาเป็นการ์ด Anki
ตอนนี้เมื่อดีบักปัญหาใน transport layer ผมจำได้ทันทีว่าควรใช้เครื่องมืออย่าง netcat และ tcpdump อย่างไร ดังนั้นมันอาจเป็นหนึ่งในหนังสือที่ให้ผลตอบแทนต่อการลงทุนสูงที่สุดในบรรดาหนังสือที่ผมอ่านมา
ยังทำคีย์ลัดสำหรับเพิ่มคำสั่งล่าสุดที่รันลงในไฟล์นี้ และคีย์ลัดสำหรับค้นหาในไฟล์นี้ไว้ด้วย
man page ของ bash ใหญ่และซับซ้อน แต่ครอบคลุมมาก ถ้าคุ้นเคยกับส่วนหลัก ๆ และรูปร่างทางสายตาของข้อความ ก็สามารถเลื่อนผ่านอย่างรวดเร็วเพื่อหาข้อมูลที่ถูกต้องที่ต้องการได้ ซึ่งค่อนข้างมีประโยชน์
วิธีนี้หลายครั้งยังเร็วกว่าการใช้เสิร์ชเอนจินบนอินเทอร์เน็ตด้วย
เช่น แปลง man page เก่า ๆ ให้อยู่ในรูปแบบที่เป็นมิตรกับ text editor หรือใช้เครื่องมือที่ดีกว่าอย่าง tldr, Dash เพราะไม่ได้มีแค่
findเท่านั้นที่เป็นแบบนี้ไม่รู้ทำไมเหมือนกัน แต่ตอนแรกอยากจะไม่ชอบบทความนี้ อาจเป็นเพราะเห็น jvns บน HN บ่อยเกินไป หรืออารมณ์ไม่ดีอยู่ก็ได้
แต่จริง ๆ แล้วเป็นบทความที่ดีมาก และในฐานะคนที่มีประสบการณ์พัฒนา 20 ปี ผมคิดว่านี่ค่อนข้างใกล้ความจริงในบรรดาการถกเถียงระดับเมตาเกี่ยวกับการเขียนโปรแกรม
เรื่อง การมองเห็นแบบเลือกได้ นั้นตรงกับทั้ง
digและหน้าmanมาก ๆ ผมจำไม่ได้แล้วว่ากี่ครั้งที่เปิดmanแล้วถูกตัวเลือกการตั้งค่าและแฟล็กบรรทัดคำสั่งที่ไม่มีที่สิ้นสุดถาโถมใส่จนงงทิปที่ใช้กับ
manคือใช้ฟังก์ชันค้นหาแบบ Vim ด้วย/เช่น ถ้าอยากหาวิธีให้ grep แสดงเลขบรรทัดของแต่ละ match แต่จำไม่ได้ ก็เปิดman grepแล้วพิมพ์/lineจากนั้นกด Enter เพื่อค้นหาคำว่า “line” ในหน้า man ส่วน match ถัดไปก็แค่กด/ข่าวที่ว่า Strange Loop จบลงแล้วก็ทำให้เศร้านิดหน่อย เพิ่งรู้จักเมื่อราวปีก่อน แต่หลาย ๆ ทอล์กดูมีคุณภาพสูงเป็นพิเศษ
ถึงจะเศร้าที่มันจบลง แต่ทอล์กนี้แสดงให้เห็นอย่างค่อนข้างน่าเชื่อว่า บางครั้งการที่บางสิ่งจบลงก็เป็นเรื่องดีได้ ถ้าดูทั้งทอล์กจะเข้าใจ
และยังทำ https://github.com/kristopolous/mansnip ด้วย
nก็ได้ผมไม่เห็นด้วยอย่างแรงกับมุมมองเรื่อง bash ทางออกที่ดีที่สุดไม่ใช่การวางเครื่องมือทับบน bash หรือท่องจำความแปลกเฉพาะของมัน แต่คือ ไม่ใช้ bash
นั่นเป็นวิธีเดียวที่จะเลี่ยงกับดักได้
ทางเลือกที่พบบ่อยที่สุดคือ 1) ใช้เชลล์ใหม่อย่าง Oil shell [0] หรือ 2) ใช้ภาษาโปรแกรมอย่าง Python, JavaScript, PHP
ปัญหาของเชลล์ใหม่คือ ต้องติดตั้งเชลล์นั้นในทุกที่ที่อยากรันสคริปต์ ในขณะที่ bash มีอยู่แทบทุกที่ ถ้าไม่ใช่สคริปต์ที่คุณดูแลคนเดียว ก็เท่ากับกำลังขอให้คนอื่นเรียนเชลล์นั้นเพื่อมาดูแลต่อด้วย
ปัญหาของภาษาโปรแกรมอื่นคือ สิ่งที่ bash ทำได้ดีมากคือการต่อคำสั่งเข้าด้วยกัน และจัดการ input/output ของคำสั่งกับไฟล์ ซึ่งมี usability ที่ดีอย่างหาได้ยาก
พอลองทำด้วยภาษาอื่น จู่ ๆ มันก็ซับซ้อนขึ้นมาก หรืออย่างน้อยก็ยืดยาวขึ้น
ดังนั้นผมยังใช้ bash อยู่ แต่ยอมรับว่าจุดแข็งของมันอยู่ที่การรันคำสั่งอื่นและจัดการ I/O ถ้าเป็นลอจิกซับซ้อนที่ไม่เกี่ยวกับสิ่งเหล่านั้น ก็โยนไปให้ภาษาอื่น บางครั้งก็แค่เรียกสคริปต์ Python จาก bash ไม่ใช่หลีกเลี่ยง bash ทั้งหมด
ถ้ามีแนวทางอื่นที่เหมาะกว่า อยากให้ช่วยแชร์ด้วย
[0] https://www.oilshell.org
ทั้งที่เครื่องมือใหม่นั้นก็คงไม่ได้ผ่านการดีบักมาหลายสิบปีแบบ bash เอง ปัญหาอยู่ที่ตัว bash นั่นแหละ
เรามีแนวโน้มประเมิน usability ต่ำเกินไป และประเมิน “ความฉลาดล้ำ” สูงเกินไป
ตัวอย่างเด่นคือ Git เป็นเครื่องมือที่ฉลาดมาก แต่ usability แย่มาก ถึงอย่างนั้น Linus ก็เป็นคนสร้าง และ Linus ก็ฉลาด เราเลยรู้สึกเหมือนปัญหาอยู่ที่ตัวเราเอง
สุดท้ายเราจะได้สิ่งที่เราให้คุณค่า เราควรให้คุณค่ากับ usability มากกว่านี้
ทางออกที่ดีที่สุดคืออยู่ให้ห่าง มันต้องหยุดจริง ๆ อย่าพยายามทำตัว macho
โมเดลทั้งหมดของภาษานี้พังโดยพื้นฐาน ทั้งไทป์ที่เน้นสตริงเป็นศูนย์กลาง, สวิตช์โหมดแบบ global, แฟล็กตัวอักษรเดียวใน operator เปรียบเทียบพื้นฐาน, พฤติกรรมที่โดยปริยายมักเมิน error ไปทั่ว และโดยเฉพาะ function ด้วย
แค่ความแปลกอย่างใดอย่างหนึ่งก็พอจะตัดภาษานี้ทิ้งแล้ว แต่ bash มีทั้งหมดนั้นและมากกว่านั้นอีก
set -xอาจทำลายพฤติกรรมที่คาดหวังของ||และ&&มีภาษาอะไรบ้างที่ชนกันเพราะฟังก์ชันคืนค่า false ตั้งแต่แรก? มีภาษาที่ throw exception ก็จริง แต่ “false” เป็นค่าที่ถูกต้องที่คืนได้ไม่ใช่หรือ
ใน Makefile ก็เห็นเรื่องเดียวกัน ผู้คนไม่เข้าใจว่าตัวเองกำลังทำอะไร และไม่เคยคิดลึก ๆ เกี่ยวกับ build system จึงคาดหวังว่ามันจะทำงานแบบใดแบบหนึ่ง
ตัวอย่างเช่น recursive assignment ของ Make ทำให้แทบทุกคนสะดุด
FLAGS=-b,COMPILE=compile $(FLAGS),$(info compile command=$(COMPILE)),FLAGS=-a,myfile:,echo $(COMPILE) $? -o $@จะทำให้การพิมพ์ข้อมูลครั้งแรกออกมาเป็นcompile -bแต่ตอนรันจริงจะเป็นcompile -a -o myfileแต่ว่าถ้าทำให้การกำหนดค่าทั้งหมดประเมินผลทันทีเพื่อให้เข้ากับภาษาโปรแกรมอื่น ก็เท่ากับพรากเครื่องมือที่มีประโยชน์มากไป ยิ่งเข้าใจเครื่องมือแบบนี้มากเท่าไร ก็ยิ่งรู้ดีขึ้นว่าจะใช้ตรงไหนและควรทุ่มแรงแค่ไหน
แต่โดยรวมเห็นด้วย ผมพยายามโยนทุกอย่างที่ซับซ้อนแม้แต่นิดเดียวไปให้สคริปต์ที่เขียนด้วยภาษาที่แปลกน้อยกว่า
ในตอนนั้น เครื่องมือที่ช่วยหลีกเลี่ยงความผิดพลาดใน 5% สุดท้ายที่ยังเหลือเป็น bash มีประโยชน์มาก