แนวทางสำหรับอินเทอร์เฟซบรรทัดคำสั่ง (2021)
(clig.dev)- เอกสารอ้างอิงโอเพนซอร์สที่รวบรวม หลักการออกแบบและแนวทางปฏิบัติอย่างเป็นรูปธรรมสำหรับการสร้างโปรแกรม CLI โดยตีความปรัชญา UNIX แบบดั้งเดิมใหม่ให้เข้ากับยุคปัจจุบัน และมุ่งเน้นผู้อ่านหลักเป็นนักพัฒนาที่สร้างเครื่องมือบรรทัดคำสั่ง
- CLI ไม่ได้เป็นเพียงแพลตฟอร์มสำหรับสคริปต์เท่านั้น แต่ได้พัฒนาเป็น ข้อความ UI ที่ยึดมนุษย์เป็นศูนย์กลาง และหลักการออกแบบก็ควรถูกอัปเดตให้สอดคล้องกับการเปลี่ยนแปลงนี้
- ความสามารถในการนำมาประกอบกัน (composability) และความเป็นมิตรต่อมนุษย์ ไม่ได้ขัดแย้งกัน และหากยึดแนวปฏิบัติของ UNIX เช่น standard input/output, pipe, exit code ก็สามารถบรรลุทั้งสองอย่างพร้อมกันได้
- มีคำแนะนำเชิงปฏิบัติอย่างละเอียด ครอบคลุมหัวข้อที่มักถูกมองข้ามในการทำงานจริง เช่น help text, ข้อความ error, รูปแบบ output, interactivity และระบบ configuration
- ความเข้ากันได้ในอนาคตและความเชื่อมั่นของผู้ใช้ สำหรับเครื่องมือ CLI ถูกกำหนดโดยเสถียรภาพของอินเทอร์เฟซและความโปร่งใสของข้อมูล analytics และคู่มือนี้ได้เสนอเส้นฐานสำหรับสิ่งเหล่านั้น
ปรัชญา (Philosophy)
การออกแบบที่ยึดมนุษย์เป็นศูนย์กลาง
- คำสั่ง UNIX แบบดั้งเดิมถูกออกแบบโดยสมมติว่าใช้โดยโปรแกรมอื่นเป็นหลัก แต่ปัจจุบัน CLI ส่วนใหญ่ถูกใช้งานโดยมนุษย์โดยตรง จึงจำเป็นต้องมี การออกแบบที่ให้ความสำคัญกับมนุษย์ก่อน
- ในอดีต CLI เป็นแบบ "machine-first" แต่ปัจจุบันได้พัฒนาเป็น UI แบบข้อความที่ "human-first"
ชิ้นส่วนเล็ก ๆ ที่นำมาประกอบกันได้
- แก่นของปรัชญา UNIX คือ การนำโปรแกรมขนาดเล็กและเรียบง่ายมาประกอบกันเพื่อสร้างระบบที่ใหญ่กว่า ซึ่งยังคงใช้ได้ในปัจจุบัน
- standard
stdin/stdout/stderr, signal และ exit code ช่วยรับประกันการเชื่อมต่อระหว่างโปรแกรม ขณะที่ JSON รองรับการแลกเปลี่ยนข้อมูลที่มีโครงสร้างมากขึ้น - ซอฟต์แวร์ย่อมต้องเป็นส่วนประกอบของระบบที่ใหญ่กว่าเสมอ และการจะเป็นส่วนประกอบที่ทำงานได้ดีหรือไม่ ถูกกำหนดตั้งแต่ขั้นตอนการออกแบบ
ความสม่ำเสมอ
- ผู้ใช้ terminal คุ้นเคยกับ convention เดิมอยู่แล้ว จึงแนะนำให้ CLI ทำตามรูปแบบที่มีอยู่เดิม
- อย่างไรก็ตาม หากความสม่ำเสมอทำลายการใช้งานจริง ก็อาจเลือกแหกธรรมเนียมได้อย่างระมัดระวัง
ปริมาณข้อมูลที่เหมาะสม
- หากคำสั่งรอหลายนาทีโดยไม่มี output เลย นั่นคือข้อมูล "น้อยเกินไป" และหากพ่น debug log ปริมาณมหาศาลออกมา นั่นคือข้อมูล "มากเกินไป"
- ความสมดุลของปริมาณข้อมูล มีความสำคัญอย่างยิ่งต่อการที่ซอฟต์แวร์จะช่วยผู้ใช้ได้จริง
การค้นพบได้ง่าย (Ease of Discovery)
- GUI แสดงความสามารถทั้งหมดไว้บนหน้าจอ แต่ CLI มักถูกเข้าใจผิดว่าต้องพึ่งพาการจดจำ
- ด้วยการยืมเทคนิคจาก GUI เช่น help text ที่ครอบคลุม ตัวอย่างที่หลากหลาย และการแนะนำคำสั่งถัดไป ก็สามารถทำให้ CLI เรียนรู้ได้ง่ายเช่นกัน
CLI ในฐานะบทสนทนา
- การใช้ CLI มีโครงสร้างแบบ บทสนทนา ผ่านการลองผิดลองถูกซ้ำ ๆ และสามารถใช้คุณลักษณะนี้ในการออกแบบได้ เช่น การเสนอวิธีแก้ไขข้อผิดพลาด การแสดงสถานะระหว่างทาง และการขอการยืนยันก่อนทำงานที่เสี่ยง
- ปฏิสัมพันธ์ที่แย่ที่สุดคือบทสนทนาแบบเป็นปฏิปักษ์ที่ทำให้ผู้ใช้รู้สึกหมดแรง ขณะที่แบบที่ดีที่สุดคือการโต้ตอบที่รื่นรมย์และทำให้เกิดความรู้สึกสำเร็จ
ความทนทาน (Robustness)
- ซอฟต์แวร์ควรดู ทนทานทั้งในความเป็นจริงและในความรู้สึกของผู้ใช้
- หัวใจสำคัญคือการจัดการ input ที่ไม่คาดคิดอย่างสง่างาม การคงความเป็น idempotence การบอกความคืบหน้า และการหลีกเลี่ยงการแสดง stack trace โดยไม่จำเป็น
- หากลดกรณีพิเศษที่ซับซ้อนและคงความเรียบง่ายไว้ได้ ความทนทานก็จะสูงขึ้น
ความเห็นอกเห็นใจ (Empathy)
- เครื่องมือ CLI เป็นเครื่องมือสร้างสรรค์ของนักพัฒนา จึงควรใช้งานได้อย่างเพลิดเพลิน
- ควรคิดและออกแบบปัญหาให้รอบด้าน เพื่อให้ผู้ใช้ รู้สึกว่าเครื่องมือนี้ยืนอยู่ข้างเขา
ความโกลาหล (Chaos)
- โลกของ terminal เต็มไปด้วยความไม่สอดคล้องกัน แต่ความโกลาหลนั้นก็เป็นแหล่งกำเนิดของการสร้างสรรค์อย่างอิสระเช่นกัน
- "ถ้ามาตรฐานใดเป็นโทษต่อประสิทธิภาพการทำงานหรือความพึงพอใจของผู้ใช้อย่างชัดเจน ก็จงทิ้งมาตรฐานนั้นเสีย" — Jef Raskin
แนวทาง — พื้นฐาน (The Basics)
- ควรใช้ ไลบรารีสำหรับ parse argument: ไลบรารีที่แนะนำตามภาษา ได้แก่ Go(Cobra, cli), Python(Click, Typer, Argparse), Rust(clap), Node(oclif) และอื่น ๆ
- เมื่อสำเร็จให้คืนค่า exit code 0 และเมื่อผิดพลาดให้คืนค่า code ที่ไม่ใช่ 0 — นี่คือเกณฑ์ที่สคริปต์ใช้ตัดสินความสำเร็จหรือความล้มเหลว
- output ปกติให้ส่งไปที่
stdoutและข้อความอย่าง log หรือ error ให้ส่งไปที่stderr
แนวทาง — Help
- แสดง help text แบบละเอียด เมื่อใช้แฟล็ก
-hหรือ--helpและใช้หลักการเดียวกันกับ subcommand ด้วย - เมื่อรันโดยไม่มี argument ให้แสดง help แบบกระชับ (รวมคำอธิบาย ตัวอย่าง 1~2 รายการ คำอธิบายแฟล็ก และคำแนะนำ
--help)- มีการยก
jqเป็นตัวอย่างที่ทำสิ่งนี้ได้ดี
- มีการยก
- รองรับ รูปแบบการขอ help ที่หลากหลาย ทั้ง
--help/-h/help subcommandเป็นต้น - ที่ส่วนบนของ help text ควรมี ลิงก์เอกสารบนเว็บและช่องทางส่ง feedback
- ควร แสดงตัวอย่างก่อน — แนะนำให้เล่าเป็นลำดับจากกรณีใช้งานพื้นฐานไปสู่กรณีที่ซับซ้อนขึ้น
- วาง แฟล็กและคำสั่งที่ใช้บ่อย ไว้ด้านบนของ help text (อ้างอิงรูปแบบของ git)
- ใช้ รูปแบบอย่างหัวข้อหนา เพื่อให้อ่านแบบกวาดสายตาได้ง่าย แต่ควรใช้วิธีที่ไม่ขึ้นกับ terminal
- เมื่อผู้ใช้พิมพ์ผิด สามารถ คาดเดาเจตนาและเสนอการแก้ไข ได้ — แต่ควรตัดสินใจอย่างระมัดระวังว่าจะสั่งรันให้อัตโนมัติหรือไม่
- สิ่งที่พิมพ์ผิดอาจไม่ใช่แค่ typo แต่อาจเป็นความผิดพลาดเชิงตรรกะ และหากแก้อัตโนมัติ ก็อาจต้องแบกรับภาระในการรองรับไวยากรณ์นั้นถาวร
แนวทาง — เอกสาร (Documentation)
- ควรมี เอกสารบนเว็บ — จำเป็นต่อการค้นหาและการแชร์ลิงก์
- ควรมี เอกสารบน terminal — ซิงก์กับเวอร์ชันที่ติดตั้งอยู่และเข้าถึงได้แม้ออฟไลน์
- ควรพิจารณาจัดทำ man page — สามารถสร้างได้ด้วยเครื่องมืออย่าง
ronnและแนะนำให้รองรับการเข้าถึงผ่าน subcommand เช่นnpm help ls
แนวทาง — Output
- ให้ความสำคัญกับ ความอ่านง่ายสำหรับมนุษย์เป็นอันดับแรก — ใช้การเป็น TTY เพื่อตรวจว่าผู้อ่านเป็นมนุษย์หรือไม่
- text stream คืออินเทอร์เฟซสากลของ UNIX ดังนั้นควรรองรับ output ที่เครื่องอ่านได้ ด้วย
- หาก output ที่เป็นมิตรต่อมนุษย์ทำให้ความเข้ากันได้กับ pipe เสียไป ให้มีแฟล็ก
--plainสำหรับ output ข้อความธรรมดา - เมื่อส่งแฟล็ก
--jsonควรรองรับ output รูปแบบ JSON - เมื่อสำเร็จ output ควรกระชับ และถ้าไม่จำเป็นก็ไม่ต้องแสดงอะไร — สำหรับสคริปต์ควรรองรับตัวเลือก
-qเพื่อปิด output - แจ้งผู้ใช้เมื่อมีการเปลี่ยนสถานะ — ตัวอย่างที่ดีคือ
git pushที่แสดงสถานะของ remote branch - จัด output ให้ ตรวจสอบสถานะปัจจุบันของระบบได้ง่าย และแนะนำงานถัดไปด้วย เช่น
git status - ใช้สีอย่างตั้งใจ และต้อง ปิดสีได้ ในเงื่อนไขอย่างการใช้ pipe,
NO_COLOR,TERM=dumb,--no-colorเป็นต้น - ในสภาพแวดล้อมที่ไม่ใช่ TTY ห้ามแสดง animation หรือ spinner (เพื่อหลีกเลี่ยงการทำให้ CI log สกปรก)
- ใช้ emoji หรือสัญลักษณ์เฉพาะเมื่อช่วยเพิ่มความชัดเจนเท่านั้น (มีการยก
yubikey-agentเป็นตัวอย่าง) - ข้อมูลที่มีแต่ผู้พัฒนาเท่านั้นที่เข้าใจควรถูกตัดออกจาก output ปกติ และแสดงเฉพาะใน verbose mode
- อย่าใช้
stderrเหมือนเป็นไฟล์ log — โดยปกติควรหลีกเลี่ยงการพิมพ์ label ระดับ log (ERR,WARN) - เมื่อมี output จำนวนมาก ควรพิจารณาใช้ pager เช่น
less— เปิดใช้เฉพาะในสภาพแวดล้อม TTY และแนะนำตัวเลือกless -FIRX
แนวทาง — Error
- สำหรับ error ที่คาดการณ์ได้ ควร เขียนข้อความใหม่ให้อยู่ในรูปที่มนุษย์เข้าใจได้ (เช่น "ต้องรัน
chmod +w file.txt") - รักษา อัตราส่วนสัญญาณต่อสัญญาณรบกวน — error ประเภทเดียวกันควรถูกรวมไว้ใต้หัวข้อเดียว
- ข้อมูลสำคัญควร วางไว้ท้าย output — ข้อความสีแดงควรใช้โดยมีเจตนาและใช้น้อยครั้ง
- เมื่อเกิด error ที่ไม่คาดคิด ควรมีข้อมูล debug และ วิธีส่ง bug report รวมอยู่ด้วย
- ควรจัดให้ URL ของ bug report เติมข้อมูลให้อัตโนมัติ เพื่อให้ส่งรายงานได้ง่าย
แนวทาง — Argument และ Flag
- argument (args) อิงตามตำแหน่ง ส่วน flag อิงตามชื่อ — ควร เลือกใช้ flag มากกว่า argument
- ทุกแฟล็กควรมี เวอร์ชันชื่อเต็ม (เช่น รองรับทั้ง
-hและ--help) - แฟล็กตัวอักษรเดี่ยว ควรจำกัดไว้สำหรับแฟล็กที่ใช้บ่อยเท่านั้น
- หากมีมาตรฐานอยู่แล้ว ให้ใช้ ชื่อแฟล็กตามมาตรฐาน (
-f/--force,-q/--quiet,-v,--jsonเป็นต้น) - ค่าเริ่มต้นควรถูกตั้งเป็น ค่าที่เหมาะกับผู้ใช้ส่วนใหญ่
- หากไม่มี argument หรือ flag ที่จำเป็น ควร ขอ input ผ่าน prompt แต่ในสภาพแวดล้อมที่ไม่โต้ตอบ ห้ามบังคับให้มี prompt
- ก่อนทำงานที่เสี่ยง ควรมี การขอการยืนยัน — ตามระดับความเสี่ยงอาจใช้การยืนยัน
y/n, มี dry-run หรือให้พิมพ์ข้อความยืนยันเองโดยตรง- แบ่งระดับความเสี่ยงเป็น mild(ลบไฟล์), moderate(ลบไดเรกทอรี, เปลี่ยนทรัพยากรระยะไกล), severe(ลบเซิร์ฟเวอร์ทั้งหมด)
- เมื่อมี file input/output ควรรองรับการอ่าน/เขียน
stdin/stdoutด้วย-(เช่นcurl ... | tar xvf -) - ห้ามรับ secret ผ่านแฟล็กโดยตรง — แนะนำให้ใช้แฟล็กอย่าง
--password-fileหรือใช้ stdin (เพราะเสี่ยงถูกเปิดเผยผ่านผลลัพธ์psและ shell history)
แนวทาง — Interactivity
- prompt และองค์ประกอบแบบ interactive ควร แสดงเฉพาะเมื่อ stdin เป็น TTY
- เมื่อส่ง
--no-inputต้องปิด prompt ทั้งหมด - เมื่อต้องรับรหัสผ่าน ให้ ปิด echo (ไม่แสดงสิ่งที่พิมพ์บนหน้าจอ)
- ควรอธิบายให้ชัดเจนว่าผู้ใช้ สามารถออกจากกระบวนการได้ตลอดเวลา — และต้องทำให้ Ctrl-C ใช้งานได้เสมอ
แนวทาง — Subcommand
- รักษา ความสม่ำเสมอของชื่อแฟล็กและรูปแบบ output ระหว่าง subcommand ต่าง ๆ
- เครื่องมือที่ซับซ้อนควรใช้ โครงสร้าง subcommand สองระดับ แบบ
noun verbหรือverb noun(เช่นdocker container create) - หลีกเลี่ยง subcommand ที่ชื่อกำกวมหรือคล้ายกัน (เช่น ไม่ควรใช้ทั้ง update และ upgrade พร้อมกัน)
แนวทาง — ความทนทาน (Robustness Guidelines)
- ทำ การตรวจสอบ input ตั้งแต่เนิ่น ๆ และหากข้อมูลไม่ถูกต้องให้จบการทำงานอย่างรวดเร็วพร้อม error ที่เข้าใจง่าย
- ความตอบสนองสำคัญกว่าความเร็ว — ควร แสดงอะไรบางอย่างภายใน 100ms
- สำหรับงานที่ใช้เวลานาน ควรมี progress bar — สามารถใช้ไลบรารีอย่าง Python(
tqdm), Go(schollz/progressbar), Node(node-progress) - เมื่อต้องประมวลผลแบบขนาน ควรระวังไม่ให้ output ปะปนกัน
- ควร ตั้งค่า network timeout — รวมค่าเริ่มต้นไว้ด้วย เพื่อป้องกันการรอแบบไม่สิ้นสุด
- หลังเกิดข้อผิดพลาดชั่วคราว ควรออกแบบให้ ลองใหม่แล้วทำต่อจากสถานะเดิมได้
- การออกแบบแบบ crash-only — ทำให้สามารถจบการทำงานได้ทันทีโดยไม่ต้องมีขั้นตอน cleanup เพื่อคง idempotence
แนวทาง — การรองรับอนาคต (Future-proofing)
- การเปลี่ยนแปลงควรคงไว้ในรูปแบบ เพิ่มแบบไม่ทำลายความเข้ากันได้เดิม (additive)
- ก่อนมีการเปลี่ยนแปลงที่ทำลายความเข้ากันได้ ควร แสดงคำเตือนล่วงหน้าในโปรแกรม
- การเปลี่ยน output สำหรับมนุษย์โดยทั่วไปทำได้ — ส่วนกรณีสำหรับสคริปต์ควรชี้นำให้ใช้
--plainและ--json - ห้ามมี catch-all subcommand — เพราะจะทำให้ภายหลังเพิ่ม subcommand ชื่อนั้นไม่ได้
- ห้ามรองรับตัวย่อของ subcommand แบบอัตโนมัติ — อนุญาตเฉพาะ alias ที่ประกาศชัดเจนและดูแลให้เสถียร
- ห้ามสร้าง "ระเบิดเวลา" — ควรลดการพึ่งพาภายนอกให้มากที่สุดเพื่อให้ยังทำงานได้อีก 20 ปีข้างหน้า
แนวทาง — Signal และอักขระควบคุม (Signals)
- เมื่อได้รับ Ctrl-C (สัญญาณ INT) ให้ จบการทำงานทันที และกำหนด timeout สำหรับขั้นตอน cleanup
- ระหว่าง cleanup หากกด Ctrl-C ซ้ำ ควรมีคำแนะนำว่าผู้ใช้สามารถบังคับจบการทำงานได้ (ดูตัวอย่าง Docker Compose)
- โปรแกรมควรถูกออกแบบโดยสมมติว่า อาจเริ่มทำงานในสถานะที่ cleanup จากครั้งก่อนยังไม่เสร็จสมบูรณ์
แนวทาง — Configuration
ลำดับความสำคัญของการใช้ configuration (สูง → ต่ำ):
- flag → environment variable ของ shell ปัจจุบัน → configuration ระดับโปรเจกต์ (
.env) → configuration ระดับผู้ใช้ → configuration ทั้งระบบ
คำแนะนำตามประเภท configuration:
-
configuration ที่เปลี่ยนในแต่ละครั้งที่เรียกใช้ (ระดับ debug, dry-run): ใช้ flag
-
configuration ที่ต่างกันไปตามโปรเจกต์หรือเครื่อง (path, สี, HTTP proxy): ใช้ flag + environment variable ร่วมกัน
-
configuration ที่แชร์ร่วมกันทั้งโปรเจกต์ (ประเภท Makefile, package.json): ใช้ไฟล์ที่อยู่ภายใต้ version control
-
ปฏิบัติตาม XDG Base Directory spec — แนะนำเส้นทาง configuration บนพื้นฐาน
~/.config(รองรับโดย yarn, fish, neovim, tmux เป็นต้น) -
หากจะปรับแก้ไฟล์ configuration ของโปรแกรมอื่นโดยอัตโนมัติ ต้อง ขอความยินยอมจากผู้ใช้ก่อนเสมอ
แนวทาง — Environment Variables
- environment variable เหมาะกับ พฤติกรรมที่เปลี่ยนไปตาม execution context
- ชื่อควรใช้เฉพาะ ตัวพิมพ์ใหญ่ ตัวเลข และ underscore และห้ามขึ้นต้นด้วยตัวเลข
- แนะนำให้ใช้ ค่าแบบบรรทัดเดียว — ค่าแบบหลายบรรทัดอาจมีปัญหาความเข้ากันได้กับคำสั่ง
env - ควรตรวจสอบ environment variable ทั่วไป ก่อน เช่น
NO_COLOR,DEBUG,EDITOR,HTTP_PROXY,SHELL,TMPDIR,HOME,PAGER - แนะนำให้รองรับการอ่านไฟล์
.envของแต่ละโปรเจกต์ — แต่.envไม่ใช่ตัวแทนของไฟล์ configuration อย่างเป็นทางการ- ข้อจำกัดของ
.env: ไม่อยู่ภายใต้ version control, ไม่มีประวัติการเปลี่ยนแปลง, มี type เดียวคือ string, และเปราะบางต่อปัญหา encoding
- ข้อจำกัดของ
- ห้ามอ่าน secret จาก environment variable — เพราะถูกส่งต่อไปยังทุก process, อาจรั่วใน log และเสี่ยงถูกเปิดเผยผ่าน
Docker inspectหรือsystemctl show- secret ควรรับผ่าน credential file, pipe,
AF_UNIXsocket หรือบริการจัดการ secret เท่านั้น
- secret ควรรับผ่าน credential file, pipe,
แนวทาง — การตั้งชื่อ (Naming)
- ใช้ คำที่เรียบง่ายและจำง่าย — หากกว้างเกินไปอาจเสี่ยงชนกับคำสั่งอื่น
- ใช้เฉพาะ ตัวพิมพ์เล็กและขีดเมื่อจำเป็น (
curlเป็นตัวอย่างที่ดี ส่วนDownloadURLเป็นตัวอย่างที่ไม่ดี) - ควรรักษาให้สั้น แต่ชื่อที่สั้นมากระดับ
cd,ls,psควรถูกสงวนไว้สำหรับยูทิลิตีทั่วไป - กรณีเปลี่ยนชื่อจาก
plum→fig→docker composeซึ่งเป็นชื่อก่อนหน้าของ Docker Compose แสดงให้เห็นว่า ความสะดวกในการพิมพ์เป็นเกณฑ์สำคัญของการตั้งชื่อ
แนวทาง — การแจกจ่าย (Distribution)
- หากเป็นไปได้ ควร แจกจ่ายเป็นไบนารีเดียว — เช่น ใช้ PyInstaller
- หากไม่สามารถทำเป็นไบนารีเดียวได้ ให้ใช้ ตัวติดตั้งแพ็กเกจแบบ native ของแพลตฟอร์ม
- ควร ระบุวิธีถอนการติดตั้งไว้ด้านล่างของคำแนะนำการติดตั้ง
แนวทาง — Analytics
- ห้ามส่งข้อมูลการใช้งานหรือข้อมูล crash โดยไม่ได้รับความยินยอมจากผู้ใช้
- หากมีการเก็บข้อมูล ต้องเปิดเผยอย่างชัดเจนว่ารวบรวมอะไร ทำไมต้องเก็บ ทำให้ไม่ระบุตัวตนอย่างไร และเก็บไว้นานเท่าใด
- แนะนำให้ใช้แบบ opt-in เป็นค่าเริ่มต้น — หากเป็นแบบ opt-out ต้องแจ้งอย่างชัดเจนตั้งแต่การรันครั้งแรกหรือบนเว็บไซต์
- มีการแนะนำสามกรณีศึกษา: Angular.js (opt-in อย่างชัดเจน), Homebrew (Google Analytics พร้อม FAQ ที่เปิดเผย), Next.js (สถิติไม่ระบุตัวตนที่เปิดใช้โดยค่าเริ่มต้น)
- ทางเลือกแทน analytics ได้แก่ การวัดผลเอกสารบนเว็บ, การวัดจำนวนดาวน์โหลด และการสัมภาษณ์ผู้ใช้โดยตรง
1 ความคิดเห็น
ความเห็นจาก Hacker News
stderrมีไว้สำหรับการล็อก การให้ข้อมูล และงานลักษณะนั้น ส่วน stdout ควรให้ผลลัพธ์ที่เป็นประโยชน์ไม่ว่าจะเป็น tty หรือไม่ก็ตาม